From 4d6017e782a99981d4f2ee050f09e6d52cccfd24 Mon Sep 17 00:00:00 2001 From: yushen Date: Fri, 6 Feb 2026 19:04:40 +0800 Subject: [PATCH] fix(subagent): forward assistant stream events for internal announce --- src/agent/async-agent.test.ts | 28 +++++++++++++++++++++++++--- src/agent/async-agent.ts | 4 +++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/agent/async-agent.test.ts b/src/agent/async-agent.test.ts index b4dd4276..3d359d3d 100644 --- a/src/agent/async-agent.test.ts +++ b/src/agent/async-agent.test.ts @@ -144,7 +144,7 @@ describe("AsyncAgent internal flow", () => { agent.close(); }); - it("forwards only assistant message_end events when writeInternal opts in", async () => { + it("forwards assistant message stream (start/update/end) when writeInternal opts in", async () => { let resolveRunInternal: ((value: { text: string; thinking: undefined; error: undefined }) => void) | undefined; runInternalMock.mockImplementationOnce( () => new Promise((resolve) => { @@ -161,6 +161,14 @@ describe("AsyncAgent internal flow", () => { await Promise.resolve(); internalRunState.value = true; + streamCallback!({ + type: "message_start", + message: { role: "assistant", content: [] }, + }); + streamCallback!({ + type: "message_update", + message: { role: "assistant", content: [{ type: "text", text: "partial" }] }, + }); streamCallback!({ type: "message_end", message: { role: "user", content: [{ type: "text", text: "hidden internal prompt" }] }, @@ -173,12 +181,26 @@ describe("AsyncAgent internal flow", () => { const first = await nextWithTimeout(iter); expect(first).not.toBe("timeout"); if (first !== "timeout") { - expect((first as { type: string }).type).toBe("message_end"); + expect((first as { type: string }).type).toBe("message_start"); expect((first as { message: { role: string } }).message.role).toBe("assistant"); } const second = await nextWithTimeout(iter); - expect(second).toBe("timeout"); + expect(second).not.toBe("timeout"); + if (second !== "timeout") { + expect((second as { type: string }).type).toBe("message_update"); + expect((second as { message: { role: string } }).message.role).toBe("assistant"); + } + + const third = await nextWithTimeout(iter); + expect(third).not.toBe("timeout"); + if (third !== "timeout") { + expect((third as { type: string }).type).toBe("message_end"); + expect((third as { message: { role: string } }).message.role).toBe("assistant"); + } + + const fourth = await nextWithTimeout(iter); + expect(fourth).toBe("timeout"); resolveRunInternal!({ text: "", thinking: undefined, error: undefined }); await agent.waitForIdle(); diff --git a/src/agent/async-agent.ts b/src/agent/async-agent.ts index fc7fb15b..a284871b 100644 --- a/src/agent/async-agent.ts +++ b/src/agent/async-agent.ts @@ -129,7 +129,9 @@ export class AsyncAgent { private shouldForwardEvent(event: AgentEvent | MulticaEvent): boolean { if (!this.agent.isInternalRun) return true; if (!this.forwardInternalAssistant) return false; - if (event.type !== "message_end") return false; + if (event.type !== "message_start" && event.type !== "message_update" && event.type !== "message_end") { + return false; + } const maybeMessage = (event as { message?: unknown }).message; if (!maybeMessage || typeof maybeMessage !== "object") return false;