fix: preserve session send database path

This commit is contained in:
Peter Steinberger
2026-05-16 08:58:01 +01:00
parent 9a5a505246
commit 13d4e53a54
3 changed files with 68 additions and 6 deletions

View File

@@ -477,7 +477,7 @@ async function createAgentMainSessionForSend(params: {
canonicalKey: string;
context: GatewayRequestContext;
}): Promise<
| { ok: true; canonicalKey: string; entry: SessionEntry }
| { ok: true; agentId: string; databasePath: string; canonicalKey: string; entry: SessionEntry }
| { ok: false; error: ReturnType<typeof errorShape> }
> {
const target = resolveGatewaySessionDatabaseTarget({
@@ -515,7 +515,13 @@ async function createAgentMainSessionForSend(params: {
),
};
}
return { ok: true, canonicalKey: target.canonicalKey, entry: patched.entry };
return {
ok: true,
agentId: target.agentId,
databasePath: target.databasePath,
canonicalKey: target.canonicalKey,
entry: patched.entry,
};
}
function resolveAbortSessionKey(params: {
@@ -721,6 +727,8 @@ async function handleSessionSend(params: {
const cfg = loadedSession.cfg;
let entry = loadedSession.entry;
let canonicalKey = loadedSession.canonicalKey;
let agentId = loadedSession.agentId;
let databasePath = resolveGatewaySessionDatabaseTarget({ cfg, key: canonicalKey }).databasePath;
// Reject sends/steers targeting sessions whose owning agent was deleted (#65524).
const deletedAgentId = resolveDeletedAgentIdFromSessionKey(cfg, canonicalKey);
if (deletedAgentId !== null) {
@@ -746,6 +754,8 @@ async function handleSessionSend(params: {
}
entry = created.entry;
canonicalKey = created.canonicalKey;
agentId = created.agentId;
databasePath = created.databasePath;
}
if (!entry?.sessionId) {
params.respond(
@@ -776,7 +786,8 @@ async function handleSessionSend(params: {
const messageSeq =
(await readSessionMessageCountAsync({
agentId: resolveAgentIdFromSessionKey(canonicalKey),
agentId,
path: databasePath,
sessionId: entry.sessionId,
})) + 1;
let sendAcked = false;

View File

@@ -14,6 +14,7 @@ import {
loadGatewaySessionRow,
loadSessionEntry,
readSessionMessageCountAsync,
resolveGatewaySessionDatabaseTarget,
type GatewaySessionRow,
} from "./session-utils.js";
@@ -126,12 +127,17 @@ async function handleTranscriptUpdateBroadcast(
if (connIds.size === 0) {
return;
}
const { entry } = loadSessionEntry(sessionKey);
const { cfg, entry } = loadSessionEntry(sessionKey);
const agentId = resolveAgentIdFromSessionKey(sessionKey);
const databasePath = resolveGatewaySessionDatabaseTarget({ cfg, key: sessionKey }).databasePath;
const messageSeq =
asPositiveSafeInteger(update.messageSeq) ??
(entry?.sessionId && agentId
? await readSessionMessageCountAsync({ agentId, sessionId: entry.sessionId })
? await readSessionMessageCountAsync({
agentId,
path: databasePath,
sessionId: entry.sessionId,
})
: undefined);
const sessionSnapshot = buildGatewaySessionSnapshot({
sessionRow: loadGatewaySessionRow(sessionKey, { transcriptUsageMaxBytes: 64 * 1024 }),

View File

@@ -3,10 +3,11 @@ import os from "node:os";
import path from "node:path";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { WebSocket } from "ws";
import { getSessionEntry } from "../config/sessions.js";
import { getSessionEntry, upsertSessionEntry } from "../config/sessions.js";
import { replaceSqliteSessionTranscriptEvents } from "../config/sessions/transcript-store.sqlite.js";
import { emitAgentEvent, registerAgentRunContext } from "../infra/agent-events.js";
import { extractFirstTextBlock } from "../shared/chat-message-content.js";
import { openOpenClawAgentDatabase } from "../state/openclaw-agent-db.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { testing as agentJobTesting } from "./server-methods/agent-job.js";
import {
@@ -261,6 +262,50 @@ describe("gateway server chat", () => {
}
});
test("sessions.send counts messages from selected registered database path", async () => {
const stateDir = process.env.OPENCLAW_STATE_DIR;
if (!stateDir) {
throw new Error("OPENCLAW_STATE_DIR missing in gateway test environment");
}
const databasePath = path.join(stateDir, "relocated", "send.sqlite");
openOpenClawAgentDatabase({ agentId: "main", path: databasePath });
upsertSessionEntry({
agentId: "main",
path: databasePath,
sessionKey: "agent:main:dashboard:registered-send",
entry: {
sessionId: "sess-registered-send",
updatedAt: Date.now(),
},
});
replaceSqliteSessionTranscriptEvents({
agentId: "main",
path: databasePath,
sessionId: "sess-registered-send",
events: [
{ type: "session", version: 1, id: "sess-registered-send" },
{
type: "message",
id: "msg-1",
message: { role: "user", content: "first" },
},
{
type: "message",
id: "msg-2",
message: { role: "assistant", content: "second" },
},
],
});
const res = await rpcReq(ws, "sessions.send", {
key: "agent:main:dashboard:registered-send",
message: "hello relocated",
idempotencyKey: "idem-sessions-send-registered-db",
});
expect(res.ok).toBe(true);
expect(res.payload?.messageSeq).toBe(3);
});
test("sessions.send creates a configured agent main session before sending", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-sessions-send-agent-"));
testState.sessionStorePath = path.join(dir, "sessions.json");