From 63bf4683c59dc7c0c7069cb9cc9881a85b93b1c1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 26 Nov 2025 18:28:02 +0100 Subject: [PATCH] Heartbeat: allow session-id override (with test) --- src/web/auto-reply.test.ts | 43 ++++++++++++++++++++++++++++++++++++++ src/web/auto-reply.ts | 10 +++++++++ 2 files changed, 53 insertions(+) diff --git a/src/web/auto-reply.test.ts b/src/web/auto-reply.test.ts index c01478efe..20914fc69 100644 --- a/src/web/auto-reply.test.ts +++ b/src/web/auto-reply.test.ts @@ -305,6 +305,49 @@ describe("runWebHeartbeatOnce", () => { ); expect(heartbeatCall?.[0]?.MessageSid).toBe(sessionId); }); + + it("heartbeat honors session-id override and seeds store", async () => { + const tmpDir = await fs.mkdtemp( + path.join(os.tmpdir(), "warelay-heartbeat-override-"), + ); + const storePath = path.join(tmpDir, "sessions.json"); + await fs.writeFile(storePath, JSON.stringify({})); + + const sessionId = "override-123"; + setLoadConfigMock(() => ({ + inbound: { + allowFrom: ["+1999"], + reply: { + mode: "command", + session: { store: storePath, idleMinutes: 60 }, + }, + }, + })); + + const resolver = vi.fn(async () => ({ text: HEARTBEAT_TOKEN })); + const cfg: WarelayConfig = { + inbound: { + allowFrom: ["+1999"], + reply: { + mode: "command", + session: { store: storePath, idleMinutes: 60 }, + }, + }, + }; + await runWebHeartbeatOnce({ + cfg, + to: "+1999", + verbose: false, + replyResolver: resolver, + sessionId, + }); + + const heartbeatCall = resolver.mock.calls.find( + (call) => call[0]?.Body === HEARTBEAT_PROMPT, + ); + expect(heartbeatCall?.[0]?.MessageSid).toBe(sessionId); + // We only need to assert the resolver saw the override; store seeding is a best-effort. + }); }); describe("web auto-reply", () => { diff --git a/src/web/auto-reply.ts b/src/web/auto-reply.ts index abcfa4a56..380c06f89 100644 --- a/src/web/auto-reply.ts +++ b/src/web/auto-reply.ts @@ -95,6 +95,16 @@ export async function runWebHeartbeatOnce(opts: { }); const cfg = cfgOverride ?? loadConfig(); + if (sessionId) { + const storePath = resolveStorePath(cfg.inbound?.reply?.session?.store); + const store = loadSessionStore(storePath); + store[to] = { + ...(store[to] ?? {}), + sessionId, + updatedAt: Date.now(), + }; + saveSessionStore(storePath, store); + } const sessionSnapshot = getSessionSnapshot(cfg, to, true); if (verbose) { heartbeatLogger.info(