diff --git a/CHANGELOG.md b/CHANGELOG.md index a6899ebfa..920d789c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- WhatsApp: route queued replies to the original sender instead of the bot's own number. (#534) — thanks @mcinteerj - Models: add OAuth expiry checks in doctor, expanded `models status` auth output (missing auth + `--check` exit codes). (#538) — thanks @latitudeki5223 - Security: per-agent mention patterns and group elevated directives now require explicit mention to avoid cross-agent toggles. - Config: support inline env vars in config (`env.*` / `env.vars`) and document env precedence. diff --git a/src/web/auto-reply.test.ts b/src/web/auto-reply.test.ts index cf85f0414..55945741f 100644 --- a/src/web/auto-reply.test.ts +++ b/src/web/auto-reply.test.ts @@ -1132,6 +1132,44 @@ describe("web auto-reply", () => { expect(payload.Body).toContain("[from: Bob (+222)]"); }); + it("sets OriginatingTo to the sender for queued routing", async () => { + const sendMedia = vi.fn(); + const reply = vi.fn().mockResolvedValue(undefined); + const sendComposing = vi.fn(); + const resolver = vi.fn().mockResolvedValue({ text: "ok" }); + + let capturedOnMessage: + | ((msg: import("./inbound.js").WebInboundMessage) => Promise) + | undefined; + const listenerFactory = async (opts: { + onMessage: ( + msg: import("./inbound.js").WebInboundMessage, + ) => Promise; + }) => { + capturedOnMessage = opts.onMessage; + return { close: vi.fn() }; + }; + + await monitorWebProvider(false, listenerFactory, false, resolver); + expect(capturedOnMessage).toBeDefined(); + + await capturedOnMessage?.({ + body: "hello", + from: "+15551234567", + to: "+19998887777", + id: "m-originating", + sendComposing, + reply, + sendMedia, + }); + + expect(resolver).toHaveBeenCalledTimes(1); + const payload = resolver.mock.calls[0][0]; + expect(payload.OriginatingChannel).toBe("whatsapp"); + expect(payload.OriginatingTo).toBe("+15551234567"); + expect(payload.To).toBe("+19998887777"); + }); + it("uses per-agent mention patterns for group gating", async () => { const sendMedia = vi.fn(); const reply = vi.fn().mockResolvedValue(undefined);