Skip to content

Commit 2003dbd

Browse files
Ignasi Cambra DiazIgnasi Cambra Diaz
authored andcommitted
fix(whatsapp): restore config-driven block streaming for WhatsApp delivery
1 parent 26e0a3e commit 2003dbd

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

extensions/whatsapp/src/auto-reply/monitor/process-message.inbound-contract.test.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ describe("web processMessage inbound contract", () => {
280280
expect(groupHistories.get("whatsapp:default:group:[email protected]") ?? []).toHaveLength(0);
281281
});
282282

283-
it("suppresses non-final WhatsApp payload delivery", async () => {
283+
it("suppresses tool payload delivery but allows block and final", async () => {
284284
const rememberSentText = vi.fn();
285285
await processMessage(createWhatsAppDirectStreamingArgs({ rememberSentText }));
286286

@@ -291,21 +291,46 @@ describe("web processMessage inbound contract", () => {
291291
expect(deliver).toBeTypeOf("function");
292292

293293
await deliver?.({ text: "tool payload" }, { kind: "tool" });
294-
await deliver?.({ text: "block payload" }, { kind: "block" });
295294
expect(deliverWebReplyMock).not.toHaveBeenCalled();
296-
expect(rememberSentText).not.toHaveBeenCalled();
297295

298-
await deliver?.({ text: "final payload" }, { kind: "final" });
296+
await deliver?.({ text: "block payload" }, { kind: "block" });
299297
expect(deliverWebReplyMock).toHaveBeenCalledTimes(1);
300-
expect(rememberSentText).toHaveBeenCalledTimes(1);
298+
299+
await deliver?.({ text: "final payload" }, { kind: "final" });
300+
expect(deliverWebReplyMock).toHaveBeenCalledTimes(2);
301+
expect(rememberSentText).toHaveBeenCalledTimes(2);
301302
});
302303

303-
it("forces disableBlockStreaming for WhatsApp dispatch", async () => {
304+
it("respects per-account blockStreaming config for disableBlockStreaming", async () => {
304305
await processMessage(createWhatsAppDirectStreamingArgs());
305306

306307
// oxlint-disable-next-line typescript/no-explicit-any
307308
const replyOptions = (capturedDispatchParams as any)?.replyOptions;
308-
expect(replyOptions?.disableBlockStreaming).toBe(true);
309+
// blockStreaming: true in config → disableBlockStreaming: false
310+
expect(replyOptions?.disableBlockStreaming).toBe(false);
311+
});
312+
313+
it("leaves disableBlockStreaming undefined when blockStreaming not configured", async () => {
314+
const args = makeProcessMessageArgs({
315+
routeSessionKey: "agent:main:whatsapp:direct:+1555",
316+
groupHistoryKey: "+1555",
317+
cfg: {
318+
messages: {},
319+
session: { store: sessionStorePath },
320+
} as unknown as ReturnType<typeof import("../../../../../src/config/config.js").loadConfig>,
321+
msg: {
322+
id: "msg1",
323+
from: "+1555",
324+
to: "+2000",
325+
chatType: "direct",
326+
body: "hi",
327+
},
328+
});
329+
await processMessage(args);
330+
331+
// oxlint-disable-next-line typescript/no-explicit-any
332+
const replyOptions = (capturedDispatchParams as any)?.replyOptions;
333+
expect(replyOptions?.disableBlockStreaming).toBeUndefined();
309334
});
310335

311336
it("passes sendComposing through as the reply typing callback", async () => {

extensions/whatsapp/src/auto-reply/monitor/process-message.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ export async function processMessage(params: {
255255
})()
256256
: undefined;
257257

258+
const account = resolveWhatsAppAccount({ cfg: params.cfg, accountId: params.route.accountId });
258259
const textLimit = params.maxMediaTextChunkLimit ?? resolveTextChunkLimit(params.cfg, "whatsapp");
259260
const chunkMode = resolveChunkMode(params.cfg, "whatsapp", params.route.accountId);
260261
const tableMode = resolveMarkdownTableMode({
@@ -402,12 +403,12 @@ export async function processMessage(params: {
402403
}
403404
},
404405
deliver: async (payload: ReplyPayload, info) => {
405-
if (info.kind !== "final") {
406-
// Only deliver final replies to external messaging channels (WhatsApp).
407-
// Block (reasoning/thinking) and tool updates are meant for the internal
408-
// web UI only; sending them here leaks chain-of-thought to end users.
406+
if (info.kind === "tool") {
407+
// Tool updates are internal-only; don't deliver to WhatsApp.
409408
return;
410409
}
410+
// Reasoning blocks are already filtered upstream by shouldSuppressReasoningPayload
411+
// in dispatch-from-config.ts. Only user-facing text/media blocks reach here.
411412
await deliverWebReply({
412413
replyResult: payload,
413414
msg: params.msg,
@@ -450,9 +451,8 @@ export async function processMessage(params: {
450451
onReplyStart: params.msg.sendComposing,
451452
},
452453
replyOptions: {
453-
// WhatsApp delivery intentionally suppresses non-final payloads.
454-
// Keep block streaming disabled so final replies are still produced.
455-
disableBlockStreaming: true,
454+
disableBlockStreaming:
455+
typeof account.blockStreaming === "boolean" ? !account.blockStreaming : undefined,
456456
onModelSelected,
457457
},
458458
});

0 commit comments

Comments
 (0)