From ec08a8d6a19c7efe55c3c22ca5279000a4df5f70 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 6 Mar 2026 00:40:04 -0500 Subject: [PATCH] Auto-reply tests: cover explicit deliver routes from internal webchat turns --- .../reply/dispatch-from-config.test.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/auto-reply/reply/dispatch-from-config.test.ts b/src/auto-reply/reply/dispatch-from-config.test.ts index 85a8dd6f786..cb71c9b09ba 100644 --- a/src/auto-reply/reply/dispatch-from-config.test.ts +++ b/src/auto-reply/reply/dispatch-from-config.test.ts @@ -399,7 +399,7 @@ describe("dispatchReplyFromConfig", () => { expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); }); - it("does not route external origin replies when current surface is internal webchat", async () => { + it("does not route external origin replies when current surface is internal webchat without explicit delivery", async () => { setNoAbort(); mocks.routeReply.mockClear(); const cfg = emptyConfig; @@ -422,6 +422,35 @@ describe("dispatchReplyFromConfig", () => { expect(dispatcher.sendFinalReply).toHaveBeenCalledTimes(1); }); + it("routes external origin replies for internal webchat turns when explicit delivery is set", async () => { + setNoAbort(); + mocks.routeReply.mockClear(); + const cfg = emptyConfig; + const dispatcher = createDispatcher(); + const ctx = buildTestCtx({ + Provider: "webchat", + Surface: "webchat", + OriginatingChannel: "imessage", + OriginatingTo: "imessage:+15550001111", + ExplicitDeliverRoute: true, + }); + + const replyResolver = async ( + _ctx: MsgContext, + _opts?: GetReplyOptions, + _cfg?: OpenClawConfig, + ) => ({ text: "hi" }) satisfies ReplyPayload; + await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver }); + + expect(dispatcher.sendFinalReply).not.toHaveBeenCalled(); + expect(mocks.routeReply).toHaveBeenCalledWith( + expect.objectContaining({ + channel: "imessage", + to: "imessage:+15550001111", + }), + ); + }); + it("routes media-only tool results when summaries are suppressed", async () => { setNoAbort(); mocks.routeReply.mockClear();