diff --git a/CHANGELOG.md b/CHANGELOG.md index c6ee9ff1d93..e0421383603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- WhatsApp/DM routing: only update main-session last-route state when DM traffic is bound to the main session, preserving isolated `dmScope` routing. (#24949) Thanks @kevinWangSheng. - Providers/OpenRouter: when thinking is explicitly off, avoid injecting `reasoning.effort` so reasoning-required models can use provider defaults instead of failing request validation. (#24863) Thanks @DevSecTim. - Status/Pairing recovery: show explicit pairing-approval command hints (including requestId when safe) when gateway probe failures report pairing-required closures. (#24771) Thanks @markmusson. - Discord/Threading: recover missing thread parent IDs by refetching thread metadata before resolving parent channel context. (#24897) Thanks @z-x-yang. diff --git a/src/web/auto-reply/monitor/process-message.inbound-contract.test.ts b/src/web/auto-reply/monitor/process-message.inbound-contract.test.ts index 0acd4056fc9..8458487d8e9 100644 --- a/src/web/auto-reply/monitor/process-message.inbound-contract.test.ts +++ b/src/web/auto-reply/monitor/process-message.inbound-contract.test.ts @@ -84,6 +84,7 @@ vi.mock("../deliver-reply.js", () => ({ deliverWebReply: deliverWebReplyMock, })); +import { updateLastRouteInBackground } from "./last-route.js"; import { processMessage } from "./process-message.js"; describe("web processMessage inbound contract", () => { @@ -302,4 +303,58 @@ describe("web processMessage inbound contract", () => { const replyOptions = (capturedDispatchParams as any)?.replyOptions; expect(replyOptions?.disableBlockStreaming).toBe(true); }); + + it("updates main last route for DM when session key matches main session key", async () => { + const updateLastRouteMock = vi.mocked(updateLastRouteInBackground); + updateLastRouteMock.mockClear(); + + const args = makeProcessMessageArgs({ + routeSessionKey: "agent:main:whatsapp:direct:+1000", + groupHistoryKey: "+1000", + msg: { + id: "msg-last-route-1", + from: "+1000", + to: "+2000", + chatType: "direct", + body: "hello", + senderE164: "+1000", + }, + }); + args.route = { + ...args.route, + sessionKey: "agent:main:whatsapp:direct:+1000", + mainSessionKey: "agent:main:whatsapp:direct:+1000", + }; + + await processMessage(args); + + expect(updateLastRouteMock).toHaveBeenCalledTimes(1); + }); + + it("does not update main last route for isolated DM scope sessions", async () => { + const updateLastRouteMock = vi.mocked(updateLastRouteInBackground); + updateLastRouteMock.mockClear(); + + const args = makeProcessMessageArgs({ + routeSessionKey: "agent:main:whatsapp:dm:+1000:peer:+3000", + groupHistoryKey: "+3000", + msg: { + id: "msg-last-route-2", + from: "+3000", + to: "+2000", + chatType: "direct", + body: "hello", + senderE164: "+3000", + }, + }); + args.route = { + ...args.route, + sessionKey: "agent:main:whatsapp:dm:+1000:peer:+3000", + mainSessionKey: "agent:main:whatsapp:direct:+1000", + }; + + await processMessage(args); + + expect(updateLastRouteMock).not.toHaveBeenCalled(); + }); });