fix(acp): stream subagent text chunks (with thoughts)

Propagate `thought` metadata through SubAgent STREAM_TEXT events and render them as agent message/thought chunks in ACP sessions.
This commit is contained in:
tanzhenxin 2026-01-27 16:59:02 +08:00
parent 7177b41120
commit 8ce176389c
4 changed files with 209 additions and 1 deletions

View file

@ -10,6 +10,7 @@ import type {
SubAgentToolResultEvent,
SubAgentApprovalRequestEvent,
SubAgentUsageEvent,
SubAgentStreamTextEvent,
ToolCallConfirmationDetails,
AnyDeclarativeTool,
AnyToolInvocation,
@ -97,11 +98,13 @@ export class SubAgentTracker {
const onToolResult = this.createToolResultHandler(abortSignal);
const onApproval = this.createApprovalHandler(abortSignal);
const onUsageMetadata = this.createUsageMetadataHandler(abortSignal);
const onStreamText = this.createStreamTextHandler(abortSignal);
eventEmitter.on(SubAgentEventType.TOOL_CALL, onToolCall);
eventEmitter.on(SubAgentEventType.TOOL_RESULT, onToolResult);
eventEmitter.on(SubAgentEventType.TOOL_WAITING_APPROVAL, onApproval);
eventEmitter.on(SubAgentEventType.USAGE_METADATA, onUsageMetadata);
eventEmitter.on(SubAgentEventType.STREAM_TEXT, onStreamText);
return [
() => {
@ -109,6 +112,7 @@ export class SubAgentTracker {
eventEmitter.off(SubAgentEventType.TOOL_RESULT, onToolResult);
eventEmitter.off(SubAgentEventType.TOOL_WAITING_APPROVAL, onApproval);
eventEmitter.off(SubAgentEventType.USAGE_METADATA, onUsageMetadata);
eventEmitter.off(SubAgentEventType.STREAM_TEXT, onStreamText);
// Clean up any remaining states
this.toolStates.clear();
},
@ -273,6 +277,26 @@ export class SubAgentTracker {
};
}
/**
* Creates a handler for stream text events.
* Emits agent message or thought chunks for text content from subagent model responses.
*/
private createStreamTextHandler(
abortSignal: AbortSignal,
): (...args: unknown[]) => void {
return (...args: unknown[]) => {
const event = args[0] as SubAgentStreamTextEvent;
if (abortSignal.aborted) return;
// Emit streamed text as agent message or thought based on the flag
void this.messageEmitter.emitMessage(
event.text,
'assistant',
event.thought ?? false,
);
};
}
/**
* Converts confirmation details to permission options for the client.
*/