import { memo, useState, type JSX } from "react"; import { ChevronDown, ChevronRight } from "lucide-react"; import { ToolActivityKindIcon } from "../../toolActivityKindIcons"; import type { AgentToolCallVM } from "../contracts/agentToolCallVM"; import type { AgentToolGroupRowVM } from "../contracts/agentToolGroupRowVM"; import { AgentAskUserQuestionCard } from "./AgentAskUserQuestionCard"; import { CollapsibleReveal } from "./CollapsibleReveal"; import { AgentEnterPlanModeCard } from "./AgentEnterPlanModeCard"; import { AgentExitPlanModeCard } from "./AgentExitPlanModeCard"; import { AgentThinkingDisclosure } from "./AgentThinkingDisclosure"; import { AgentTaskCallCard } from "./AgentTaskCallCard"; import { AgentSubAgentCards } from "./AgentSubAgentCards"; import { AgentToolCallCard } from "./AgentToolCallCard"; import { RawTimelineJsonDisclosure } from "./RawTimelineJsonDisclosure"; interface AgentToolGroupRowProps { row: AgentToolGroupRowVM; label: (count: number) => string; thinkingLabel: string; onLinkClick?: (href: string) => void; expanded?: boolean; expansionKey?: string; onExpandedChange?: (key: string, expanded: boolean) => void; showRawTimelineJson?: boolean; rawTimelineJsonLabel?: string; } export const AgentToolGroupRow = memo(function AgentToolGroupRow({ row, label, thinkingLabel, onLinkClick, expanded, expansionKey, onExpandedChange, showRawTimelineJson = false, rawTimelineJsonLabel = "" }: AgentToolGroupRowProps): JSX.Element { "use memo"; const [localExpanded, setLocalExpanded] = useState(false); const hasDetail = row.entries.length > 0; const isExpanded = hasDetail && (expanded ?? localExpanded); const setNextExpanded = (nextExpanded: boolean) => { if (expanded === undefined) { setLocalExpanded(nextExpanded); } if (expansionKey) { onExpandedChange?.(expansionKey, nextExpanded); } }; const groupLabel = label(row.calls.length); const singleCall = row.calls[0]; if (!row.grouped && singleCall) { return renderToolCard( singleCall, onLinkClick, showRawTimelineJson, rawTimelineJsonLabel ); } return (
{hasDetail ? ( ) : (
)} {hasDetail ? (
{row.entries.map((entry) => entry.kind === "thinking" ? (
) : (
{renderToolCard( entry.call, onLinkClick, showRawTimelineJson, rawTimelineJsonLabel )}
) )}
) : null}
); }); function ToolGroupLabel({ label }: { label: string }): JSX.Element { "use memo"; return ( <> {renderToolCountLabel(label)} ); } function renderToolCountLabel(label: string): JSX.Element { return ( {label} ); } function renderToolCard( call: AgentToolCallVM, onLinkClick?: (href: string) => void, showRawTimelineJson = false, rawTimelineJsonLabel = "" ): JSX.Element { const props = { call, onLinkClick }; // A delegated sub-agent renders as its own first-class card - no tool-row // chrome, no prompt echo; identity/status belong to the sub-agent itself. if (call.task?.subAgents?.length) { return ; } let card: JSX.Element; switch (call.rendererKind) { case "plan-enter": card = ; break; case "plan-exit": card = ; break; case "ask-user": card = ; break; case "task": card = ; break; default: card = ; } return ( <> {card} {showRawTimelineJson && rawTimelineJsonLabel ? ( ) : null} ); }