From 55e7f5f27ce2fddcdd21cffc331c170b59d22fd5 Mon Sep 17 00:00:00 2001 From: hcl Date: Sun, 10 May 2026 00:46:54 +0800 Subject: [PATCH] fix(whatsapp): pass routing ctx to transcribeFirstAudio so echoTranscript can deliver (#79778) (#79788) Merged via squash. Prepared head SHA: b5d2936d52bacd08f6058c357ebe98debc2d12eb Co-authored-by: hclsys <7755017+hclsys@users.noreply.github.com> Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com> Reviewed-by: @velvet-shark --- CHANGELOG.md | 1 + .../on-message.audio-preflight.test.ts | 42 +++++++++++++++++++ .../src/auto-reply/monitor/on-message.ts | 7 ++++ .../process-message.audio-preflight.test.ts | 7 ++++ .../src/auto-reply/monitor/process-message.ts | 7 ++++ 5 files changed, 64 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fec490737ed..b5fa3c47e0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Feishu: auto-thread `message(action="send")` replies inside the topic when the active session is group_topic or group_topic_sender, and propagate `replyInThread` through text, card, and media outbound adapters so topic-scoped sessions no longer post at the group root. Fixes #74903. (#77151) Thanks @ai-hpc. +- WhatsApp: pass routing context into voice-note transcript echo preflight so echoed transcripts can deliver to the originating chat. Fixes #79778. (#79788) Thanks @hclsys. ## 2026.5.9 diff --git a/extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts b/extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts index 2fbadc7b034..114acd5b54f 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/on-message.audio-preflight.test.ts @@ -316,6 +316,48 @@ describe("createWebOnMessageHandler audio preflight", () => { ); }); + it("passes routing ctx fields to transcribeFirstAudio so echoTranscript can deliver (#79778)", async () => { + let capturedCtx: unknown; + transcribeFirstAudioMock.mockImplementation(async ({ ctx }: { ctx: unknown }) => { + capturedCtx = ctx; + return "transcribed voice note"; + }); + const handler = createWebOnMessageHandler({ + cfg: { + channels: { + whatsapp: { + ackReaction: { enabled: true }, + }, + }, + } as never, + verbose: false, + connectionId: "conn-1", + maxMediaBytes: 1024 * 1024, + groupHistoryLimit: 20, + groupHistories: new Map(), + groupMemberNames: new Map(), + echoTracker: makeEchoTracker() as never, + backgroundTasks: new Set(), + replyResolver: vi.fn() as never, + replyLogger: { + info: () => {}, + warn: () => {}, + debug: () => {}, + error: () => {}, + } as never, + baseMentionConfig: {} as never, + account: { authDir: "/tmp/auth", accountId: "default" }, + }); + + await handler(makeAudioMsg()); + + expect(capturedCtx).toMatchObject({ + Provider: "whatsapp", + OriginatingTo: "+15550000002", + From: "+15550000002", + }); + }); + it("does not transcribe group voice when policy gating rejects before mention", async () => { applyGroupGatingMock.mockResolvedValueOnce({ shouldProcess: false }); const handler = createWebOnMessageHandler({ diff --git a/extensions/whatsapp/src/auto-reply/monitor/on-message.ts b/extensions/whatsapp/src/auto-reply/monitor/on-message.ts index 64eea5bf425..3ca364201c8 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/on-message.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/on-message.ts @@ -162,6 +162,13 @@ export function createWebOnMessageHandler(params: { ctx: { MediaPaths: [msg.mediaPath], MediaTypes: msg.mediaType ? [msg.mediaType] : undefined, + From: msg.from, + To: msg.to, + Provider: "whatsapp", + Surface: "whatsapp", + OriginatingChannel: "whatsapp", + OriginatingTo: conversationId, + AccountId: route.accountId, }, cfg: params.cfg, })) ?? null; diff --git a/extensions/whatsapp/src/auto-reply/monitor/process-message.audio-preflight.test.ts b/extensions/whatsapp/src/auto-reply/monitor/process-message.audio-preflight.test.ts index de4766b4403..13d548bd53e 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/process-message.audio-preflight.test.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/process-message.audio-preflight.test.ts @@ -219,8 +219,15 @@ describe("processMessage audio preflight transcription", () => { expect(transcribeFirstAudioMock).toHaveBeenCalledWith( expect.objectContaining({ ctx: expect.objectContaining({ + AccountId: "default", + From: "+15550000002", MediaPaths: ["/tmp/voice.ogg"], MediaTypes: ["audio/ogg; codecs=opus"], + OriginatingChannel: "whatsapp", + OriginatingTo: "+15550000002", + Provider: "whatsapp", + Surface: "whatsapp", + To: "+15550000001", }), }), ); diff --git a/extensions/whatsapp/src/auto-reply/monitor/process-message.ts b/extensions/whatsapp/src/auto-reply/monitor/process-message.ts index 61e6c4920df..7c321f3f379 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/process-message.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/process-message.ts @@ -243,6 +243,13 @@ export async function processMessage(params: { ctx: { MediaPaths: [params.msg.mediaPath], MediaTypes: params.msg.mediaType ? [params.msg.mediaType] : undefined, + From: params.msg.from, + To: params.msg.to, + Provider: "whatsapp", + Surface: "whatsapp", + OriginatingChannel: "whatsapp", + OriginatingTo: conversationId, + AccountId: params.route.accountId, }, cfg: params.cfg, });