diff --git a/extensions/whatsapp/src/channel-react-action.test.ts b/extensions/whatsapp/src/channel-react-action.test.ts index 17297f0c4af1..c49c259b2ec3 100644 --- a/extensions/whatsapp/src/channel-react-action.test.ts +++ b/extensions/whatsapp/src/channel-react-action.test.ts @@ -150,6 +150,43 @@ describe("whatsapp react action messageId resolution", () => { }); }); + it("uses toolContext current chat for same-chat upload-file", async () => { + const mediaReadFile = vi.fn(async () => Buffer.from("media")); + + await handleWhatsAppMessageAction({ + action: "upload-file", + params: { + filePath: "/tmp/pic.png", + caption: "picture caption", + }, + cfg: baseCfg, + accountId: "default", + mediaLocalRoots: ["/tmp"], + mediaReadFile, + toolContext: { + currentChannelId: "whatsapp:+1555", + currentChannelProvider: "whatsapp", + currentMessageId: "ctx-msg-42", + }, + }); + + expect(hoisted.resolveAuthorizedWhatsAppOutboundTarget).toHaveBeenCalledWith({ + cfg: baseCfg, + chatJid: "+1555", + accountId: "default", + actionLabel: "upload-file", + }); + expect(hoisted.sendMessageWhatsApp).toHaveBeenCalledWith( + "+1555", + "picture caption", + expect.objectContaining({ + accountId: "default", + mediaReadFile, + mediaUrl: "/tmp/pic.png", + }), + ); + }); + it("does not send upload-file when target authorization fails", async () => { hoisted.resolveAuthorizedWhatsAppOutboundTarget.mockImplementationOnce(() => { throw new Error("WhatsApp upload-file blocked"); @@ -286,6 +323,33 @@ describe("whatsapp react action messageId resolution", () => { ); }); + it("falls back to toolContext current chat for same-chat reactions", async () => { + await handleWhatsAppMessageAction({ + action: "react", + params: { emoji: "❤️" }, + cfg: baseCfg, + accountId: "default", + toolContext: { + currentChannelId: "whatsapp:+1555", + currentChannelProvider: "whatsapp", + currentMessageId: "ctx-msg-42", + }, + }); + expect(hoisted.handleWhatsAppAction).toHaveBeenCalledWith( + { + action: "react", + chatJid: "+1555", + messageId: "ctx-msg-42", + emoji: "❤️", + remove: undefined, + participant: undefined, + accountId: "default", + fromMe: undefined, + }, + baseCfg, + ); + }); + it("converts numeric toolContext messageId to string", async () => { await handleWhatsAppMessageAction({ action: "react", diff --git a/extensions/whatsapp/src/channel-react-action.ts b/extensions/whatsapp/src/channel-react-action.ts index 36e3f154db77..161938e74046 100644 --- a/extensions/whatsapp/src/channel-react-action.ts +++ b/extensions/whatsapp/src/channel-react-action.ts @@ -76,6 +76,21 @@ function hasUploadFileBufferPayload(args: Record): boolean { return readStringParam(args, "buffer", { trim: false }) !== undefined; } +function readWhatsAppActionChatJid(params: WhatsAppMessageActionParams): string | undefined { + const explicit = + readStringParam(params.params, "chatJid") ?? readStringParam(params.params, "to"); + if (explicit) { + return explicit; + } + if ( + params.toolContext?.currentChannelProvider !== WHATSAPP_CHANNEL || + !params.toolContext.currentChannelId + ) { + return undefined; + } + return normalizeWhatsAppTarget(params.toolContext.currentChannelId) ?? undefined; +} + function extractBase64Payload(encoded: string): string { const match = /^data:[^;]+;base64,(.*)$/i.exec(encoded.trim()); return match ? match[1] : encoded; @@ -134,7 +149,8 @@ async function handleWhatsAppUploadFileAction(params: WhatsAppMessageActionParam "WhatsApp upload-file requires media, mediaUrl, filePath, path, fileUrl, or buffer.", ); } - const to = readStringParam(params.params, "to", { required: true }); + const to = + readWhatsAppActionChatJid(params) ?? readStringParam(params.params, "to", { required: true }); const resolved = resolveAuthorizedWhatsAppOutboundTarget({ cfg: params.cfg, chatJid: to, @@ -188,8 +204,7 @@ export async function handleWhatsAppMessageAction(params: WhatsAppMessageActionP throw new Error(`Action ${params.action} is not supported for provider ${WHATSAPP_CHANNEL}.`); } const isWhatsAppSource = params.toolContext?.currentChannelProvider === WHATSAPP_CHANNEL; - const explicitTarget = - readStringParam(params.params, "chatJid") ?? readStringParam(params.params, "to"); + const explicitTarget = readWhatsAppActionChatJid(params); const normalizedTarget = explicitTarget ? normalizeWhatsAppTarget(explicitTarget) : null; const normalizedCurrent = isWhatsAppSource && params.toolContext?.currentChannelId @@ -232,7 +247,7 @@ export async function handleWhatsAppMessageAction(params: WhatsAppMessageActionP { action: "react", chatJid: - readStringParam(params.params, "chatJid") ?? + readWhatsAppActionChatJid(params) ?? readStringParam(params.params, "to", { required: true }), messageId, emoji,