refactor: canonicalize gateway session store keys

This commit is contained in:
Peter Steinberger
2026-01-17 07:41:06 +00:00
parent d5fdda8e28
commit c92265a51b
13 changed files with 449 additions and 650 deletions

View File

@@ -1,7 +1,7 @@
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import { loadSessionStore, resolveStorePath } from "../config/sessions.js"; import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
import { getAgentRunContext, registerAgentRunContext } from "../infra/agent-events.js"; import { getAgentRunContext, registerAgentRunContext } from "../infra/agent-events.js";
import { parseAgentSessionKey } from "../routing/session-key.js"; import { toAgentRequestSessionKey } from "../routing/session-key.js";
export function resolveSessionKeyForRun(runId: string) { export function resolveSessionKeyForRun(runId: string) {
const cached = getAgentRunContext(runId)?.sessionKey; const cached = getAgentRunContext(runId)?.sessionKey;
@@ -12,8 +12,7 @@ export function resolveSessionKeyForRun(runId: string) {
const found = Object.entries(store).find(([, entry]) => entry?.sessionId === runId); const found = Object.entries(store).find(([, entry]) => entry?.sessionId === runId);
const storeKey = found?.[0]; const storeKey = found?.[0];
if (storeKey) { if (storeKey) {
const parsed = parseAgentSessionKey(storeKey); const sessionKey = toAgentRequestSessionKey(storeKey) ?? storeKey;
const sessionKey = parsed?.rest ?? storeKey;
registerAgentRunContext(runId, { sessionKey }); registerAgentRunContext(runId, { sessionKey });
return sessionKey; return sessionKey;
} }

View File

@@ -9,6 +9,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -26,22 +27,16 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+436769770569"]; testState.allowFrom = ["+436769770569"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-stale",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-stale", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp",
lastTo: "+1555",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -70,20 +65,14 @@ describe("gateway server agent", () => {
test("agent forwards sessionKey to agentCommand", async () => { test("agent forwards sessionKey to agentCommand", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( "agent:main:subagent:abc": {
{ sessionId: "sess-sub",
"agent:main:subagent:abc": { updatedAt: Date.now(),
sessionId: "sess-sub",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -111,23 +100,17 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-account",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-account", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp", lastAccountId: "default",
lastTo: "+1555",
lastAccountId: "default",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -156,23 +139,17 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-explicit",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-explicit", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp", lastAccountId: "legacy",
lastTo: "+1555",
lastAccountId: "legacy",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -201,23 +178,17 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-explicit-account",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-explicit-account", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp", lastAccountId: "legacy",
lastTo: "+1555",
lastAccountId: "legacy",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -247,23 +218,17 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-implicit",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-implicit", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp", lastAccountId: "kev",
lastTo: "+1555",
lastAccountId: "kev",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -290,20 +255,14 @@ describe("gateway server agent", () => {
test("agent forwards image attachments as images[]", async () => { test("agent forwards image attachments as images[]", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-images",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-images",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -343,20 +302,14 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-missing-provider",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-missing-provider",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -384,22 +337,16 @@ describe("gateway server agent", () => {
test("agent routes main last-channel whatsapp", async () => { test("agent routes main last-channel whatsapp", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-whatsapp",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-whatsapp", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp",
lastTo: "+1555",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -429,22 +376,16 @@ describe("gateway server agent", () => {
test("agent routes main last-channel telegram", async () => { test("agent routes main last-channel telegram", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main", lastChannel: "telegram",
updatedAt: Date.now(), lastTo: "123",
lastChannel: "telegram",
lastTo: "123",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -473,22 +414,16 @@ describe("gateway server agent", () => {
test("agent routes main last-channel discord", async () => { test("agent routes main last-channel discord", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-discord",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-discord", lastChannel: "discord",
updatedAt: Date.now(), lastTo: "channel:discord-123",
lastChannel: "discord",
lastTo: "channel:discord-123",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -517,22 +452,16 @@ describe("gateway server agent", () => {
test("agent routes main last-channel slack", async () => { test("agent routes main last-channel slack", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-slack",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-slack", lastChannel: "slack",
updatedAt: Date.now(), lastTo: "channel:slack-123",
lastChannel: "slack",
lastTo: "channel:slack-123",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -561,22 +490,16 @@ describe("gateway server agent", () => {
test("agent routes main last-channel signal", async () => { test("agent routes main last-channel signal", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-signal",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-signal", lastChannel: "signal",
updatedAt: Date.now(), lastTo: "+15551234567",
lastChannel: "signal",
lastTo: "+15551234567",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);

View File

@@ -18,6 +18,7 @@ import {
startGatewayServer, startGatewayServer,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -80,22 +81,16 @@ describe("gateway server agent", () => {
setActivePluginRegistry(registry); setActivePluginRegistry(registry);
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-teams",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-teams", lastChannel: "msteams",
updatedAt: Date.now(), lastTo: "conversation:teams-123",
lastChannel: "msteams",
lastTo: "conversation:teams-123",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -133,22 +128,16 @@ describe("gateway server agent", () => {
setActivePluginRegistry(registry); setActivePluginRegistry(registry);
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-alias",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-alias", lastChannel: "imessage",
updatedAt: Date.now(), lastTo: "chat_id:123",
lastChannel: "imessage",
lastTo: "chat_id:123",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -206,22 +195,16 @@ describe("gateway server agent", () => {
testState.allowFrom = ["+1555"]; testState.allowFrom = ["+1555"];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main-webchat",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main-webchat", lastChannel: "webchat",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "webchat",
lastTo: "+1555",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -247,25 +230,19 @@ describe("gateway server agent", () => {
await server.close(); await server.close();
}); });
test("agent uses webchat for internal runs when last provider is webchat", async () => { test("agent uses webchat for internal runs when last provider is webchat", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{
"agent:main:main": {
sessionId: "sess-main-webchat-internal", sessionId: "sess-main-webchat-internal",
updatedAt: Date.now(), updatedAt: Date.now(),
lastChannel: "webchat", lastChannel: "webchat",
lastTo: "+1555", lastTo: "+1555",
}, },
}, },
null, });
2,
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -408,20 +385,14 @@ describe("gateway server agent", () => {
test("agent events stream to webchat clients when run context is registered", async () => { test("agent events stream to webchat clients when run context is registered", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws, { await connectOk(ws, {

View File

@@ -11,6 +11,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -66,21 +67,15 @@ describe("gateway server agent", () => {
test("suppresses tool stream events when verbose is off", async () => { test("suppresses tool stream events when verbose is off", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main", verboseLevel: "off",
updatedAt: Date.now(),
verboseLevel: "off",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws, { await connectOk(ws, {

View File

@@ -11,6 +11,7 @@ import {
sessionStoreSaveDelayMs, sessionStoreSaveDelayMs,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -28,20 +29,14 @@ describe("gateway server chat", () => {
test("chat.history caps payload bytes", { timeout: 15_000 }, async () => { test("chat.history caps payload bytes", { timeout: 15_000 }, async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -78,22 +73,16 @@ describe("gateway server chat", () => {
test("chat.send does not overwrite last delivery route", async () => { test("chat.send does not overwrite last delivery route", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp",
lastTo: "+1555",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -105,11 +94,12 @@ describe("gateway server chat", () => {
}); });
expect(res.ok).toBe(true); expect(res.ok).toBe(true);
const stored = JSON.parse(await fs.readFile(testState.sessionStorePath, "utf-8")) as { const stored = JSON.parse(await fs.readFile(testState.sessionStorePath, "utf-8")) as Record<
main?: { lastChannel?: string; lastTo?: string }; string,
}; { lastChannel?: string; lastTo?: string } | undefined
expect(stored.main?.lastChannel).toBe("whatsapp"); >;
expect(stored.main?.lastTo).toBe("+1555"); expect(stored["agent:main:main"]?.lastChannel).toBe("whatsapp");
expect(stored["agent:main:main"]?.lastTo).toBe("+1555");
ws.close(); ws.close();
await server.close(); await server.close();
@@ -118,20 +108,14 @@ describe("gateway server chat", () => {
test("chat.abort cancels an in-flight chat.send", { timeout: 15000 }, async () => { test("chat.abort cancels an in-flight chat.send", { timeout: 15000 }, async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
let inFlight: Promise<unknown> | undefined; let inFlight: Promise<unknown> | undefined;
@@ -210,20 +194,14 @@ describe("gateway server chat", () => {
test("chat.abort cancels while saving the session store", async () => { test("chat.abort cancels while saving the session store", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
sessionStoreSaveDelayMs.value = 120; sessionStoreSaveDelayMs.value = 120;
@@ -288,11 +266,11 @@ describe("gateway server chat", () => {
test("chat.send treats /stop as an out-of-band abort", { timeout: 15000 }, async () => { test("chat.send treats /stop as an out-of-band abort", { timeout: 15000 }, async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify({ main: { sessionId: "sess-main", updatedAt: Date.now() } }, null, 2), main: { sessionId: "sess-main", updatedAt: Date.now() },
"utf-8", },
); });
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);

View File

@@ -11,6 +11,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -96,7 +97,7 @@ describe("gateway server chat", () => {
test("chat.abort returns aborted=false for unknown runId", async () => { test("chat.abort returns aborted=false for unknown runId", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile(testState.sessionStorePath, JSON.stringify({}, null, 2), "utf-8"); await writeSessionStore({ entries: {} });
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -116,20 +117,14 @@ describe("gateway server chat", () => {
test("chat.abort rejects mismatched sessionKey", async () => { test("chat.abort rejects mismatched sessionKey", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -189,20 +184,14 @@ describe("gateway server chat", () => {
test("chat.abort is a no-op after chat.send completes", async () => { test("chat.abort is a no-op after chat.send completes", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -259,20 +248,14 @@ describe("gateway server chat", () => {
test("chat.send preserves run ordering for queued runs", async () => { test("chat.send preserves run ordering for queued runs", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
main: { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);

View File

@@ -12,6 +12,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -106,22 +107,16 @@ describe("gateway server chat", () => {
}, },
}; };
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( "discord:group:dev": {
{ sessionId: "sess-discord",
"discord:group:dev": { updatedAt: Date.now(),
sessionId: "sess-discord", chatType: "group",
updatedAt: Date.now(), channel: "discord",
chatType: "group",
channel: "discord",
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -148,20 +143,14 @@ describe("gateway server chat", () => {
}, },
}; };
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( "cron:job-1": {
{ sessionId: "sess-cron",
"cron:job-1": { updatedAt: Date.now(),
sessionId: "sess-cron",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -236,20 +225,14 @@ describe("gateway server chat", () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const lines: string[] = []; const lines: string[] = [];
for (let i = 0; i < 300; i += 1) { for (let i = 0; i < 300; i += 1) {
@@ -349,21 +332,15 @@ describe("gateway server chat", () => {
"utf-8", "utf-8",
); );
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { sessionFile: forkedPath,
sessionId: "sess-main", updatedAt: Date.now(),
sessionFile: forkedPath,
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -397,20 +374,14 @@ describe("gateway server chat", () => {
"utf-8", "utf-8",
); );
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -447,20 +418,14 @@ describe("gateway server chat", () => {
]; ];
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
await fs.writeFile( await fs.writeFile(
path.join(dir, "sess-main.jsonl"), path.join(dir, "sess-main.jsonl"),
JSON.stringify({ JSON.stringify({

View File

@@ -16,6 +16,7 @@ import {
startGatewayServer, startGatewayServer,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
const _decodeWsData = (data: unknown): string => { const _decodeWsData = (data: unknown): string => {
@@ -217,20 +218,14 @@ describe("gateway server node/bridge", () => {
test("bridge RPC chat.history returns session messages", async () => { test("bridge RPC chat.history returns session messages", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
await fs.writeFile( await fs.writeFile(
path.join(dir, "sess-main.jsonl"), path.join(dir, "sess-main.jsonl"),
@@ -274,20 +269,14 @@ describe("gateway server node/bridge", () => {
test("bridge RPC sessions.list returns session rows", async () => { test("bridge RPC sessions.list returns session rows", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const port = await getFreePort(); const port = await getFreePort();
const server = await startGatewayServer(port); const server = await startGatewayServer(port);
@@ -331,20 +320,14 @@ describe("gateway server node/bridge", () => {
test("bridge chat events are pushed to subscribed nodes", async () => { test("bridge chat events are pushed to subscribed nodes", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const port = await getFreePort(); const port = await getFreePort();
const server = await startGatewayServer(port); const server = await startGatewayServer(port);
@@ -408,20 +391,14 @@ describe("gateway server node/bridge", () => {
test("bridge chat.send forwards image attachments to agentCommand", async () => { test("bridge chat.send forwards image attachments to agentCommand", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const port = await getFreePort(); const port = await getFreePort();
const server = await startGatewayServer(port); const server = await startGatewayServer(port);

View File

@@ -14,6 +14,7 @@ import {
startGatewayServer, startGatewayServer,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
const decodeWsData = (data: unknown): string => { const decodeWsData = (data: unknown): string => {
@@ -42,22 +43,16 @@ describe("gateway server node/bridge", () => {
test("bridge voice transcript defaults to main session", async () => { test("bridge voice transcript defaults to main session", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main", lastChannel: "whatsapp",
updatedAt: Date.now(), lastTo: "+1555",
lastChannel: "whatsapp",
lastTo: "+1555",
},
}, },
null, },
2, });
),
"utf-8",
);
const port = await getFreePort(); const port = await getFreePort();
const server = await startGatewayServer(port); const server = await startGatewayServer(port);
@@ -92,20 +87,14 @@ describe("gateway server node/bridge", () => {
test("bridge voice transcript triggers chat events for webchat clients", async () => { test("bridge voice transcript triggers chat events for webchat clients", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
await connectOk(ws, { await connectOk(ws, {
@@ -183,20 +172,14 @@ describe("gateway server node/bridge", () => {
test("bridge chat.abort cancels while saving the session store", async () => { test("bridge chat.abort cancels while saving the session store", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-")); const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json"); testState.sessionStorePath = path.join(dir, "sessions.json");
await fs.writeFile( await writeSessionStore({
testState.sessionStorePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: Date.now(),
sessionId: "sess-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
sessionStoreSaveDelayMs.value = 120; sessionStoreSaveDelayMs.value = 120;

View File

@@ -10,6 +10,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
import { DEFAULT_PROVIDER } from "../agents/defaults.js"; import { DEFAULT_PROVIDER } from "../agents/defaults.js";
@@ -65,41 +66,35 @@ describe("gateway server sessions", () => {
"utf-8", "utf-8",
); );
await fs.writeFile( await writeSessionStore({
storePath, entries: {
JSON.stringify( main: {
{ sessionId: "sess-main",
"agent:main:main": { updatedAt: now - 30_000,
sessionId: "sess-main", inputTokens: 10,
updatedAt: now - 30_000, outputTokens: 20,
inputTokens: 10, thinkingLevel: "low",
outputTokens: 20, verboseLevel: "on",
thinkingLevel: "low", lastChannel: "whatsapp",
verboseLevel: "on", lastTo: "+1555",
lastProvider: "whatsapp", lastAccountId: "work",
lastTo: "+1555",
lastAccountId: "work",
},
"agent:main:discord:group:dev": {
sessionId: "sess-group",
updatedAt: now - 120_000,
totalTokens: 50,
},
"agent:main:subagent:one": {
sessionId: "sess-subagent",
updatedAt: now - 120_000,
spawnedBy: "agent:main:main",
},
global: {
sessionId: "sess-global",
updatedAt: now - 10_000,
},
}, },
null, "discord:group:dev": {
2, sessionId: "sess-group",
), updatedAt: now - 120_000,
"utf-8", totalTokens: 50,
); },
"agent:main:subagent:one": {
sessionId: "sess-subagent",
updatedAt: now - 120_000,
spawnedBy: "agent:main:main",
},
global: {
sessionId: "sess-global",
updatedAt: now - 10_000,
},
},
});
const { server, ws } = await startServerWithClient(); const { server, ws } = await startServerWithClient();
const hello = await connectOk(ws); const hello = await connectOk(ws);
@@ -355,21 +350,15 @@ describe("gateway server sessions", () => {
"utf-8", "utf-8",
); );
await fs.writeFile( await writeSessionStore({
storePath, entries: {
JSON.stringify( main: { sessionId: "sess-main", updatedAt: Date.now() },
{ "discord:group:dev": {
"agent:main:main": { sessionId: "sess-main", updatedAt: Date.now() }, sessionId: "sess-active",
"agent:main:discord:group:dev": { updatedAt: Date.now(),
sessionId: "sess-active",
updatedAt: Date.now(),
},
}, },
null, },
2, });
),
"utf-8",
);
embeddedRunMock.activeIds.add("sess-active"); embeddedRunMock.activeIds.add("sess-active");
embeddedRunMock.waitResults.set("sess-active", true); embeddedRunMock.waitResults.set("sess-active", true);

View File

@@ -8,6 +8,7 @@ import {
rpcReq, rpcReq,
startServerWithClient, startServerWithClient,
testState, testState,
writeSessionStore,
} from "./test-helpers.js"; } from "./test-helpers.js";
installGatewayTestHooks(); installGatewayTestHooks();
@@ -25,38 +26,30 @@ describe("gateway server sessions", () => {
const workDir = path.join(dir, "work"); const workDir = path.join(dir, "work");
await fs.mkdir(homeDir, { recursive: true }); await fs.mkdir(homeDir, { recursive: true });
await fs.mkdir(workDir, { recursive: true }); await fs.mkdir(workDir, { recursive: true });
await fs.writeFile( await writeSessionStore({
path.join(homeDir, "sessions.json"), storePath: path.join(homeDir, "sessions.json"),
JSON.stringify( agentId: "home",
{ entries: {
"agent:home:main": { main: {
sessionId: "sess-home-main", sessionId: "sess-home-main",
updatedAt: Date.now(), updatedAt: Date.now(),
},
"agent:home:discord:group:dev": {
sessionId: "sess-home-group",
updatedAt: Date.now() - 1000,
},
}, },
null, "discord:group:dev": {
2, sessionId: "sess-home-group",
), updatedAt: Date.now() - 1000,
"utf-8",
);
await fs.writeFile(
path.join(workDir, "sessions.json"),
JSON.stringify(
{
"agent:work:main": {
sessionId: "sess-work-main",
updatedAt: Date.now(),
},
}, },
null, },
2, });
), await writeSessionStore({
"utf-8", storePath: path.join(workDir, "sessions.json"),
); agentId: "work",
entries: {
main: {
sessionId: "sess-work-main",
updatedAt: Date.now(),
},
},
});
const { ws } = await startServerWithClient(); const { ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);
@@ -92,20 +85,17 @@ describe("gateway server sessions", () => {
testState.agentsConfig = { list: [{ id: "ops", default: true }] }; testState.agentsConfig = { list: [{ id: "ops", default: true }] };
testState.sessionConfig = { mainKey: "work" }; testState.sessionConfig = { mainKey: "work" };
await fs.writeFile( await writeSessionStore({
storePath, storePath,
JSON.stringify( agentId: "ops",
{ mainKey: "work",
"agent:ops:work": { entries: {
sessionId: "sess-ops-main", main: {
updatedAt: Date.now(), sessionId: "sess-ops-main",
}, updatedAt: Date.now(),
}, },
null, },
2, });
),
"utf-8",
);
const { ws } = await startServerWithClient(); const { ws } = await startServerWithClient();
await connectOk(ws); await connectOk(ws);

View File

@@ -6,11 +6,12 @@ import path from "node:path";
import { afterEach, beforeEach, expect } from "vitest"; import { afterEach, beforeEach, expect } from "vitest";
import { WebSocket } from "ws"; import { WebSocket } from "ws";
import { resolveMainSessionKeyFromConfig } from "../config/sessions.js"; import { resolveMainSessionKeyFromConfig, type SessionEntry } from "../config/sessions.js";
import { resetAgentRunContextForTest } from "../infra/agent-events.js"; import { resetAgentRunContextForTest } from "../infra/agent-events.js";
import { drainSystemEvents, peekSystemEvents } from "../infra/system-events.js"; import { drainSystemEvents, peekSystemEvents } from "../infra/system-events.js";
import { rawDataToString } from "../infra/ws.js"; import { rawDataToString } from "../infra/ws.js";
import { resetLogger, setLoggerOverride } from "../logging.js"; import { resetLogger, setLoggerOverride } from "../logging.js";
import { DEFAULT_AGENT_ID, toAgentStoreSessionKey } from "../routing/session-key.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { PROTOCOL_VERSION } from "./protocol/index.js"; import { PROTOCOL_VERSION } from "./protocol/index.js";
@@ -31,6 +32,32 @@ let previousHome: string | undefined;
let tempHome: string | undefined; let tempHome: string | undefined;
let tempConfigRoot: string | undefined; let tempConfigRoot: string | undefined;
export async function writeSessionStore(params: {
entries: Record<string, Partial<SessionEntry>>;
storePath?: string;
agentId?: string;
mainKey?: string;
}): Promise<void> {
const storePath = params.storePath ?? testState.sessionStorePath;
if (!storePath) throw new Error("writeSessionStore requires testState.sessionStorePath");
const agentId = params.agentId ?? DEFAULT_AGENT_ID;
const store: Record<string, Partial<SessionEntry>> = {};
for (const [requestKey, entry] of Object.entries(params.entries)) {
const rawKey = requestKey.trim();
const storeKey =
rawKey === "global" || rawKey === "unknown"
? rawKey
: toAgentStoreSessionKey({
agentId,
requestKey,
mainKey: params.mainKey,
});
store[storeKey] = entry;
}
await fs.mkdir(path.dirname(storePath), { recursive: true });
await fs.writeFile(storePath, JSON.stringify(store, null, 2), "utf-8");
}
export function installGatewayTestHooks() { export function installGatewayTestHooks() {
beforeEach(async () => { beforeEach(async () => {
setLoggerOverride({ level: "silent", consoleLevel: "silent" }); setLoggerOverride({ level: "silent", consoleLevel: "silent" });

View File

@@ -16,6 +16,25 @@ export type ParsedAgentSessionKey = {
rest: string; rest: string;
}; };
export function toAgentRequestSessionKey(storeKey: string | undefined | null): string | undefined {
const raw = (storeKey ?? "").trim();
if (!raw) return undefined;
return parseAgentSessionKey(raw)?.rest ?? raw;
}
export function toAgentStoreSessionKey(params: {
agentId: string;
requestKey: string | undefined | null;
mainKey?: string | undefined;
}): string {
const raw = (params.requestKey ?? "").trim();
if (!raw || raw === DEFAULT_MAIN_KEY) {
return buildAgentMainSessionKey({ agentId: params.agentId, mainKey: params.mainKey });
}
if (raw.startsWith("agent:")) return raw;
return `agent:${normalizeAgentId(params.agentId)}:${raw}`;
}
export function resolveAgentIdFromSessionKey(sessionKey: string | undefined | null): string { export function resolveAgentIdFromSessionKey(sessionKey: string | undefined | null): string {
const parsed = parseAgentSessionKey(sessionKey); const parsed = parseAgentSessionKey(sessionKey);
return normalizeAgentId(parsed?.agentId ?? DEFAULT_AGENT_ID); return normalizeAgentId(parsed?.agentId ?? DEFAULT_AGENT_ID);