diff --git a/CHANGELOG.md b/CHANGELOG.md index 59f3ab556..5975c6ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Docs: https://docs.clawd.bot ### Fixes - BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell. +- Message tool: keep path/filePath as-is for send; hydrate buffers only for sendAttachment. (#1444) Thanks @hopyky. - Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla. - Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer. - Agents: surface concrete API error details instead of generic AI service errors. diff --git a/src/agents/tools/message-tool.test.ts b/src/agents/tools/message-tool.test.ts index b1f887813..375b6ccb0 100644 --- a/src/agents/tools/message-tool.test.ts +++ b/src/agents/tools/message-tool.test.ts @@ -86,6 +86,64 @@ describe("message tool mirroring", () => { }); }); +describe("message tool path passthrough", () => { + it("does not convert path to media for send", async () => { + mocks.runMessageAction.mockClear(); + mocks.runMessageAction.mockResolvedValue({ + kind: "send", + action: "send", + channel: "telegram", + to: "telegram:123", + handledBy: "plugin", + payload: {}, + dryRun: true, + } satisfies MessageActionRunResult); + + const tool = createMessageTool({ + config: {} as never, + }); + + await tool.execute("1", { + action: "send", + target: "telegram:123", + path: "~/Downloads/voice.ogg", + message: "", + }); + + const call = mocks.runMessageAction.mock.calls[0]?.[0]; + expect(call?.params?.path).toBe("~/Downloads/voice.ogg"); + expect(call?.params?.media).toBeUndefined(); + }); + + it("does not convert filePath to media for send", async () => { + mocks.runMessageAction.mockClear(); + mocks.runMessageAction.mockResolvedValue({ + kind: "send", + action: "send", + channel: "telegram", + to: "telegram:123", + handledBy: "plugin", + payload: {}, + dryRun: true, + } satisfies MessageActionRunResult); + + const tool = createMessageTool({ + config: {} as never, + }); + + await tool.execute("1", { + action: "send", + target: "telegram:123", + filePath: "./tmp/note.m4a", + message: "", + }); + + const call = mocks.runMessageAction.mock.calls[0]?.[0]; + expect(call?.params?.filePath).toBe("./tmp/note.m4a"); + expect(call?.params?.media).toBeUndefined(); + }); +}); + describe("message tool description", () => { const bluebubblesPlugin: ChannelPlugin = { id: "bluebubbles", diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index 19cdb122d..4ab3c7e18 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -341,15 +341,6 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { required: true, }) as ChannelMessageActionName; - // Handle path and filePath parameters: convert to media with file:// URL - if (action === "send" && !params.media) { - const filePath = - (params.path as string | undefined) || (params.filePath as string | undefined); - if (filePath) { - params.media = filePath.startsWith("file://") ? filePath : `file://${filePath}`; - } - } - const accountId = readStringParam(params, "accountId") ?? agentAccountId; const gateway = { diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index aef11c09c..dc8aeddf3 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -343,7 +343,7 @@ async function hydrateSendAttachmentParams(params: { action: ChannelMessageActionName; dryRun?: boolean; }): Promise { - if (params.action !== "sendAttachment" && params.action !== "send") return; + if (params.action !== "sendAttachment") return; const mediaHint = readStringParam(params.args, "media", { trim: false }); const fileHint =