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:
@@ -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.
|
||||
|
||||
@@ -44,6 +44,29 @@ export interface ConversationSummary {
|
||||
|
||||
export type MessageRole = "user" | "assistant" | "system" | "tool"
|
||||
|
||||
export interface AgentToolCall {
|
||||
tool_name: string
|
||||
input_preview?: string | null
|
||||
output_preview?: string | null
|
||||
is_error: boolean
|
||||
}
|
||||
|
||||
export interface AgentExecutionStats {
|
||||
agent_type?: string | null
|
||||
status?: string | null
|
||||
total_duration_ms?: number | null
|
||||
total_tokens?: number | null
|
||||
total_tool_use_count?: number | null
|
||||
read_count?: number | null
|
||||
search_count?: number | null
|
||||
bash_count?: number | null
|
||||
edit_file_count?: number | null
|
||||
lines_added?: number | null
|
||||
lines_removed?: number | null
|
||||
other_tool_count?: number | null
|
||||
tool_calls?: AgentToolCall[]
|
||||
}
|
||||
|
||||
export type ContentBlock =
|
||||
| { type: "text"; text: string }
|
||||
| {
|
||||
@@ -63,6 +86,7 @@ export type ContentBlock =
|
||||
tool_use_id: string | null
|
||||
output_preview: string | null
|
||||
is_error: boolean
|
||||
agent_stats?: AgentExecutionStats | null
|
||||
}
|
||||
| { type: "thinking"; text: string }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user