feat(ui): add dedicated Agent subagent rendering with nested tool call display

Render Agent/Explore/Plan tool calls in a visually distinct collapsible
container with colored left border, replacing the generic tool card. Parse
subagent JSONL transcripts from {sessionId}/subagents/ to extract and
display the actual tool calls (Bash, Read, Grep, etc.) the subagent
executed, reusing the existing ToolCallPart for consistent appearance.

- Add AgentToolCallPart component with collapsible body, prompt section,
  execution stats, and nested tool call list via render prop injection
- Add AgentExecutionStats and AgentToolCall types (Rust + TypeScript)
- Parse toolUseResult.agentId to locate and read subagent JSONL files
- Validate agentId against path traversal before filesystem access
- Pass agentStats through adapter for both ID-matched and positional
  tool result pairing
- Strip agentStats in nested render to prevent recursive Agent expansion
- Add i18n keys for agent UI labels across all 10 languages
This commit is contained in:
xintaofei
2026-04-16 21:32:25 +08:00
parent ffdc0019fc
commit 9f82fdf350
22 changed files with 634 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ import type {
ContentBlock,
MessageRole,
TurnUsage,
AgentExecutionStats,
} from "@/lib/types"
/**
@@ -25,6 +26,7 @@ export type AdaptedContentPart =
state: ToolCallState
output?: string | null
errorText?: string
agentStats?: AgentExecutionStats | null
}
| {
type: "tool-result"
@@ -662,6 +664,7 @@ export function adaptMessageTurn(
errorText: matchedResult.is_error
? matchedResult.output_preview || undefined
: undefined,
agentStats: matchedResult.agent_stats ?? undefined,
})
} else {
// Position-based matching: if this tool_use has no ID, check next block
@@ -687,6 +690,7 @@ export function adaptMessageTurn(
errorText: positionalResult.is_error
? positionalResult.output_preview || undefined
: undefined,
agentStats: positionalResult.agent_stats ?? undefined,
})
} else {
// For live streaming, unmatched tools are still running.