diff --git a/src/acp-agent.ts b/src/acp-agent.ts index 7a4979b5..caedb478 100644 --- a/src/acp-agent.ts +++ b/src/acp-agent.ts @@ -4857,6 +4857,9 @@ export function toAcpNotifications( const registerHooks = options?.registerHooks !== false; const supportsTerminalOutput = options?.clientCapabilities?._meta?.["terminal_output"] === true; if (typeof content === "string") { + if (content.length === 0) { + return []; + } const update: SessionNotification["update"] = { sessionUpdate: role === "assistant" ? "agent_message_chunk" : "user_message_chunk", content: { @@ -4886,13 +4889,15 @@ export function toAcpNotifications( switch (chunk.type) { case "text": case "text_delta": - update = { - sessionUpdate: role === "assistant" ? "agent_message_chunk" : "user_message_chunk", - content: { - type: "text", - text: chunk.text, - }, - }; + if (chunk.text.length > 0) { + update = { + sessionUpdate: role === "assistant" ? "agent_message_chunk" : "user_message_chunk", + content: { + type: "text", + text: chunk.text, + }, + }; + } break; case "image": update = { diff --git a/src/tests/tools.test.ts b/src/tests/tools.test.ts index f0660e1e..76300923 100644 --- a/src/tests/tools.test.ts +++ b/src/tests/tools.test.ts @@ -2153,3 +2153,55 @@ describe("createTaskHook", () => { expect(changes).toBe(0); }); }); + +describe("empty message content is not emitted", () => { + const mockClient = {} as AcpClient; + const mockLogger: Logger = { log: () => {}, error: () => {} }; + + it("drops an empty string content instead of emitting an empty agent_message_chunk", () => { + const notifications = toAcpNotifications( + "", + "assistant", + "test-session", + {}, + mockClient, + mockLogger, + ); + expect(notifications).toHaveLength(0); + }); + + it("still emits non-empty string content", () => { + const notifications = toAcpNotifications( + "hello", + "assistant", + "test-session", + {}, + mockClient, + mockLogger, + ); + expect(notifications).toHaveLength(1); + expect(notifications[0].update).toMatchObject({ + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: "hello" }, + }); + }); + + it("drops an empty streamed text block but keeps a non-empty one", () => { + const notifications = toAcpNotifications( + [ + { type: "text", text: "" }, + { type: "text", text: "real" }, + ] as any, + "assistant", + "test-session", + {}, + mockClient, + mockLogger, + ); + expect(notifications).toHaveLength(1); + expect(notifications[0].update).toMatchObject({ + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: "real" }, + }); + }); +});