From cf96ad8ef91c7e0de789a78b504d18f9b5cd1db7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 20 Dec 2025 15:30:45 +0100 Subject: [PATCH] fix: route voice wake to main --- apps/ios/Sources/Model/NodeAppModel.swift | 3 +- .../Sources/Clawdis/Bridge/BridgeServer.swift | 2 +- src/gateway/server.test.ts | 48 +++++++++++++++++++ src/gateway/server.ts | 4 +- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/apps/ios/Sources/Model/NodeAppModel.swift b/apps/ios/Sources/Model/NodeAppModel.swift index f18c42623..c01e1eca7 100644 --- a/apps/ios/Sources/Model/NodeAppModel.swift +++ b/apps/ios/Sources/Model/NodeAppModel.swift @@ -38,8 +38,7 @@ final class NodeAppModel { init() { self.voiceWake.configure { [weak self] cmd in guard let self else { return } - let nodeId = UserDefaults.standard.string(forKey: "node.instanceId") ?? "ios-node" - let sessionKey = "node-\(nodeId)" + let sessionKey = "main" do { try await self.sendVoiceTranscript(text: cmd, sessionKey: sessionKey) } catch { diff --git a/apps/macos/Sources/Clawdis/Bridge/BridgeServer.swift b/apps/macos/Sources/Clawdis/Bridge/BridgeServer.swift index 89a7dbc3c..c7f24870f 100644 --- a/apps/macos/Sources/Clawdis/Bridge/BridgeServer.swift +++ b/apps/macos/Sources/Clawdis/Bridge/BridgeServer.swift @@ -179,7 +179,7 @@ actor BridgeServer { guard !text.isEmpty else { return } let sessionKey = payload.sessionKey?.trimmingCharacters(in: .whitespacesAndNewlines).nonEmpty - ?? "node-\(nodeId)" + ?? "main" _ = await GatewayConnection.shared.sendAgent(GatewayAgentInvocation( message: text, diff --git a/src/gateway/server.test.ts b/src/gateway/server.test.ts index 628ff031a..6085de4fe 100644 --- a/src/gateway/server.test.ts +++ b/src/gateway/server.test.ts @@ -2793,6 +2793,54 @@ describe("gateway server", () => { await server.close(); }); + test("bridge voice transcript defaults to main session", async () => { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-gw-")); + testSessionStorePath = path.join(dir, "sessions.json"); + await fs.writeFile( + testSessionStorePath, + JSON.stringify( + { + main: { + sessionId: "sess-main", + updatedAt: Date.now(), + lastChannel: "whatsapp", + lastTo: "+1555", + }, + }, + null, + 2, + ), + "utf-8", + ); + + const port = await getFreePort(); + const server = await startGatewayServer(port); + const bridgeCall = bridgeStartCalls.at(-1); + expect(bridgeCall?.onEvent).toBeDefined(); + + const spy = vi.mocked(agentCommand); + const beforeCalls = spy.mock.calls.length; + + await bridgeCall?.onEvent?.("ios-node", { + event: "voice.transcript", + payloadJSON: JSON.stringify({ text: "hello" }), + }); + + expect(spy.mock.calls.length).toBe(beforeCalls + 1); + const call = spy.mock.calls.at(-1)?.[0] as Record; + expect(call.sessionId).toBe("sess-main"); + expect(call.deliver).toBe(false); + expect(call.surface).toBe("Node"); + + const stored = JSON.parse( + await fs.readFile(testSessionStorePath, "utf-8"), + ) as Record; + expect(stored.main?.sessionId).toBe("sess-main"); + expect(stored["node-ios-node"]).toBeUndefined(); + + await server.close(); + }); + test("bridge chat.abort cancels while saving the session store", async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-gw-")); testSessionStorePath = path.join(dir, "sessions.json"); diff --git a/src/gateway/server.ts b/src/gateway/server.ts index cdef46360..44aefe229 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -1800,8 +1800,10 @@ export async function startGatewayServer( if (text.length > 20_000) return; const sessionKeyRaw = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : ""; + const mainKey = + (loadConfig().inbound?.session?.mainKey ?? "main").trim() || "main"; const sessionKey = - sessionKeyRaw.length > 0 ? sessionKeyRaw : `node-${nodeId}`; + sessionKeyRaw.length > 0 ? sessionKeyRaw : mainKey; const { storePath, store, entry } = loadSessionEntry(sessionKey); const now = Date.now(); const sessionId = entry?.sessionId ?? randomUUID();