import React, { useCallback, useMemo, useState } from 'react'; import { CARD_BG, CARD_BORDER_STYLE, CARD_HEADER_BG, CARD_HEADER_HOVER, CARD_ICON_MUTED, CARD_SEPARATOR, CARD_TEXT_LIGHT, CARD_TEXT_LIGHTER, COLOR_TEXT_MUTED, COLOR_TEXT_SECONDARY, TAG_BG, TAG_BORDER, TAG_TEXT,} from '@renderer/constants/cssVariables';import { getTeamColorSet } from '@renderer/constants/teamColors';import { useTabUI } from '@renderer/hooks/useTabUI';import { useStore } from '@renderer/store';import { buildDisplayItemsFromMessages, buildSummary } from '@renderer/utils/aiGroupEnhancer';import { computeSubagentPhaseBreakdown } from '@renderer/utils/aiGroupHelpers';import { formatDuration, formatTokensCompact } from '@renderer/utils/formatters';import { getHighlightProps, type TriggerColor } from '@shared/constants/triggerColors';import { getModelColorClass, parseModelString } from '@shared/utils/modelParser';import { ArrowUpRight, Bot, CheckCircle2, ChevronRight, CircleDot, Loader2, Sigma, Terminal,} from 'lucide-react'; import { ExecutionTrace } from './ExecutionTrace';import { MetricsPill } from './MetricsPill'; import type { Process, SemanticStep } from '@renderer/types/data'; // =============================================================================// Types// ============================================================================= interface SubagentItemProps { step: SemanticStep; subagent: Process; onClick: () => void; isExpanded: boolean; aiGroupId: string; /** Tool use ID to highlight for error deep linking */ highlightToolUseId?: string; /** Custom highlight color from trigger */ highlightColor?: TriggerColor; /** Map of tool use ID to trigger color for notification dots */ notificationColorMap?: Map<string, TriggerColor>; /** Optional callback to register tool element refs for scroll targeting */ registerToolRef?: (toolId: string, el: HTMLDivElement | null) => void;} // =============================================================================// Main Component - Linear-style DevTools Card// ============================================================================= export const SubagentItem: React.FC<SubagentItemProps> = ({ step, subagent, onClick, isExpanded, aiGroupId, highlightToolUseId, highlightColor, notificationColorMap, registerToolRef,}) => { const description = subagent.description ?? step.content.subagentDescription ?? 'Subagent'; const subagentType = subagent.subagentType ?? 'Task'; const truncatedDesc = description.length > 60 ? description.slice(0, 60) + '...' : description; // Team member colors (when this subagent is a team member) const teamColors = subagent.team ? getTeamColorSet(subagent.team.memberColor) : null; // Detect shutdown-only team activations (trivial: just a shutdown_response) const isShutdownOnly = useMemo(() => { if (!subagent.team || !subagent.messages?.length) return false; const assistantMsgs = subagent.messages.filter((m) => m.type === 'assistant'); if (assistantMsgs.length !== 1) return false; const calls = assistantMsgs[0].toolCalls ?? []; return ( calls.length === 1 && calls[0].name === 'SendMessage' && calls[0].input?.type === 'shutdown_response' ); }, [subagent.team, subagent.messages]); // Per-tab trace expansion state (replaces local useState for true per-tab isolation) const { isSubagentTraceExpanded, toggleSubagentTraceExpansion } = useTabUI(); const isTraceManuallyExpanded = isSubagentTraceExpanded(subagent.id); // Check if contains highlighted error // Also matches when the highlight targets the parent Task tool_use that spawned this subagent const containsHighlightedError = useMemo(() => { if (!highlightToolUseId) return false; // Match parent Task tool_use ID (trigger matched the Task call itself) if (subagent.parentTaskId === highlightToolUseId) return true; // Match inner tool calls/results within the subagent if (!subagent.messages) return false; for (const msg of subagent.messages) { if (msg.toolCalls?.some((tc) => tc.id === highlightToolUseId)) return true; if (msg.toolResults?.some((tr) => tr.toolUseId === highlightToolUseId)) return true; } return false; }, [highlightToolUseId, subagent.parentTaskId, subagent.messages]); // Build display items const displayItems = useMemo(() => { if ((!isExpanded && !containsHighlightedError) || !subagent.messages?.length) { return []; } return buildDisplayItemsFromMessages(subagent.messages, []); }, [isExpanded, containsHighlightedError, subagent.messages]); // Build summary const itemsSummary = useMemo(() => { if (!isExpanded && !containsHighlightedError) { const toolCount = subagent.messages?.filter( (m) => m.type === 'assistant' && Array.isArray(m.content) && m.content.some((b) => b.type === 'tool_use') ).length ?? 0; return toolCount > 0 ? `${toolCount} tools` : ''; } return buildSummary(displayItems); }, [isExpanded, containsHighlightedError, displayItems, subagent.messages]); // Model info const modelInfo = useMemo(() => { const msg = subagent.messages?.find( (m) => m.type === 'assistant' && m.model && m.model !== '<synthetic>' ); return msg?.model ? parseModelString(msg.model) : null; }, [subagent.messages]); // Last usage const lastUsage = useMemo(() => { const messages = subagent.messages ?? []; for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].type === 'assistant' && messages[i].usage) { return messages[i].usage; } } return null; }, [subagent.messages]); // Multi-phase context breakdown (for subagents with compaction) const phaseData = useMemo(() => { if (!subagent.messages?.length) return null; return computeSubagentPhaseBreakdown(subagent.messages); }, [subagent.messages]); // Search expansion const searchExpandedSubagentIds = useStore((s) => s.searchExpandedSubagentIds); const searchCurrentSubagentItemId = useStore((s) => s.searchCurrentSubagentItemId); const shouldExpandForSearch = searchExpandedSubagentIds.has(subagent.id); // Combine manual expansion with auto-expansion for errors/search const isTraceExpanded = isTraceManuallyExpanded || containsHighlightedError || shouldExpandForSearch; const [isTraceHeaderHovered, setIsTraceHeaderHovered] = useState(false); // Outer card highlight when this subagent contains the highlighted tool const outerHighlight = useMemo(() => { if (!containsHighlightedError) return { className: '', style: undefined as React.CSSProperties | undefined }; return getHighlightProps(highlightColor); }, [containsHighlightedError, highlightColor]); // Register outer card as a tool ref target for the parent Task tool_use ID // so the navigation controller can scroll directly to this SubagentItem const outerCardRef = useCallback( (el: HTMLDivElement | null) => { if (subagent.parentTaskId && registerToolRef) { registerToolRef(subagent.parentTaskId, el); } }, [subagent.parentTaskId, registerToolRef] ); // Cumulative metrics for team members — show total output generated const cumulativeMetrics = useMemo(() => { if (!subagent.team || !subagent.metrics) return undefined; const turnCount = subagent.messages?.filter((m) => m.type === 'assistant' && m.usage).length ?? 0; return { outputTokens: subagent.metrics.outputTokens, turnCount, }; }, [subagent.team, subagent.metrics, subagent.messages]); // Computed values for metrics const hasMainImpact = subagent.mainSessionImpact && subagent.mainSessionImpact.totalTokens > 0; const hasIsolated = lastUsage && lastUsage.input_tokens + lastUsage.output_tokens > 0; const isMultiPhase = phaseData != null && phaseData.compactionCount > 0; const isolatedTotal = isMultiPhase ? phaseData.totalConsumption : lastUsage ? lastUsage.input_tokens + lastUsage.output_tokens + (lastUsage.cache_read_input_tokens ?? 0) + (lastUsage.cache_creation_input_tokens ?? 0) : 0; // Shutdown-only team activations: minimal inline row (no metrics, no expand) if (isShutdownOnly && teamColors && subagent.team) { return ( <div className="flex items-center gap-2 rounded-md px-3 py-1.5" style={{ backgroundColor: CARD_BG, border: CARD_BORDER_STYLE, opacity: 0.6, }} > <span className="size-2.5 shrink-0 rounded-full" style={{ backgroundColor: teamColors.border }} /> <span className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide" style={{ backgroundColor: teamColors.badge, color: teamColors.text, border: `1px solid ${teamColors.border}40`, }} > {subagent.team.memberName} </span> <span className="text-xs" style={{ color: CARD_ICON_MUTED }}> Shutdown confirmed </span> <span className="flex-1" /> <span className="shrink-0 font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatDuration(subagent.durationMs)} </span> </div> ); } return ( <div ref={outerCardRef} className={`overflow-hidden rounded-md transition-all duration-300 ${outerHighlight.className}`} style={{ backgroundColor: CARD_BG, border: CARD_BORDER_STYLE, ...outerHighlight.style, }} > {/* ========== Level 1: Clickable Header ========== */} <div role="button" tabIndex={0} onClick={onClick} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } }} className="flex cursor-pointer items-center gap-2 px-3 py-2 transition-colors" style={{ backgroundColor: isExpanded ? CARD_HEADER_BG : 'transparent', borderBottom: isExpanded ? CARD_BORDER_STYLE : 'none', }} > {/* Expand chevron */} <ChevronRight className={`size-3.5 shrink-0 transition-transform ${isExpanded ? 'rotate-90' : ''}`} style={{ color: CARD_ICON_MUTED }} /> {/* Icon - colored dot for team members, Bot icon for regular subagents */} {teamColors ? ( <span className="size-3.5 shrink-0 rounded-full" style={{ backgroundColor: teamColors.border }} /> ) : ( <Bot className="size-4 shrink-0" style={{ color: subagent.isOngoing ? '#3b82f6' : COLOR_TEXT_MUTED }} /> )} {/* Type badge - team member name or generic type */} {teamColors && subagent.team ? ( <span className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide" style={{ backgroundColor: teamColors.badge, color: teamColors.text, border: `1px solid ${teamColors.border}40`, }} > {subagent.team.memberName} </span> ) : ( <span className="rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide" style={{ backgroundColor: TAG_BG, color: TAG_TEXT, border: `1px solid ${TAG_BORDER}`, }} > {subagentType} </span> )} {/* Model */} {modelInfo && ( <span className={`text-[11px] ${getModelColorClass(modelInfo.family)}`}> {modelInfo.name} </span> )} {/* Description */} <span className="flex-1 truncate text-xs" style={{ color: CARD_TEXT_LIGHT }}> {truncatedDesc} </span> {/* Status indicator */} {subagent.isOngoing ? ( <Loader2 className="size-3.5 shrink-0 animate-spin" style={{ color: '#3b82f6' }} /> ) : ( <CheckCircle2 className="size-3.5 shrink-0" style={{ color: '#22c55e' }} /> )} {/* Unified Metrics Pill — team members don't show mainSessionImpact (spawn cost only; real main impact comes from teammate messages) */} <MetricsPill mainSessionImpact={subagent.team ? undefined : subagent.mainSessionImpact} lastUsage={lastUsage ?? undefined} isolatedLabel={subagent.team ? 'Context Window' : undefined} isolatedOverride={ phaseData && phaseData.compactionCount > 0 ? phaseData.totalConsumption : undefined } phaseBreakdown={phaseData?.phases} /> {/* Duration */} <span className="shrink-0 font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatDuration(subagent.durationMs)} </span> </div> {/* ========== Level 1 Expanded: Dashboard Content ========== */} {isExpanded && ( <div className="space-y-3 p-3"> {/* ========== Row 1: Meta Info (Horizontal Flow) ========== */} <div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px]" style={{ color: COLOR_TEXT_MUTED }} > <span> <span style={{ color: CARD_ICON_MUTED }}>Type</span>{' '} <span className="font-mono" style={{ color: CARD_TEXT_LIGHT }}> {subagentType} </span> </span> <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>Duration</span>{' '} <span className="font-mono tabular-nums" style={{ color: CARD_TEXT_LIGHT }}> {formatDuration(subagent.durationMs)} </span> </span> {modelInfo && ( <> <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>Model</span>{' '} <span className={`font-mono ${getModelColorClass(modelInfo.family)}`}> {modelInfo.name} </span> </span> </> )} <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>ID</span>{' '} <span className="inline-block max-w-[120px] truncate align-bottom font-mono" style={{ color: CARD_ICON_MUTED }} title={subagent.id} > {subagent.id.slice(0, 8)} </span> </span> </div> {/* ========== Row 2: Context Usage (Clean List) ========== */} {(hasMainImpact ?? hasIsolated) && ( <div className="pt-2"> {/* Overline title */} <div className="mb-2 text-[10px] font-semibold uppercase tracking-wider" style={{ color: CARD_ICON_MUTED }} > Context Usage </div> {/* Token rows - floating alignment */} <div className="space-y-1.5"> {hasMainImpact && !subagent.team && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <ArrowUpRight className="size-3" style={{ color: 'rgba(251, 191, 36, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> Main Context </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {subagent.mainSessionImpact!.totalTokens.toLocaleString()} </span> </div> )} {cumulativeMetrics && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <Sigma className="size-3" style={{ color: 'rgba(168, 85, 247, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> Total Output </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {cumulativeMetrics.outputTokens.toLocaleString()} <span style={{ color: CARD_ICON_MUTED }}> {' '} ({cumulativeMetrics.turnCount} turns) </span> </span> </div> )} {hasIsolated && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <CircleDot className="size-3" style={{ color: 'rgba(56, 189, 248, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> {subagent.team ? 'Context Window' : 'Subagent Context'} </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {isolatedTotal.toLocaleString()} </span> </div> )} {/* Per-phase breakdown when multi-phase */} {isMultiPhase && phaseData.phases.map((phase) => ( <div key={phase.phaseNumber} className="flex items-center justify-between pl-5"> <span className="text-[11px]" style={{ color: CARD_ICON_MUTED }}> Phase {phase.phaseNumber} </span> <span className="font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatTokensCompact(phase.peakTokens)} {phase.postCompaction != null && ( <span style={{ color: '#4ade80' }}> {' '} → {formatTokensCompact(phase.postCompaction)} </span> )} </span>Lines 1–500 of 574. Load more
import React, { useCallback, useMemo, useState } from 'react'; import { CARD_BG, CARD_BORDER_STYLE, CARD_HEADER_BG, CARD_HEADER_HOVER, CARD_ICON_MUTED, CARD_SEPARATOR, CARD_TEXT_LIGHT, CARD_TEXT_LIGHTER, COLOR_TEXT_MUTED, COLOR_TEXT_SECONDARY, TAG_BG, TAG_BORDER, TAG_TEXT, } from '@renderer/constants/cssVariables'; import { getTeamColorSet } from '@renderer/constants/teamColors'; import { useTabUI } from '@renderer/hooks/useTabUI'; import { useStore } from '@renderer/store'; import { buildDisplayItemsFromMessages, buildSummary } from '@renderer/utils/aiGroupEnhancer'; import { computeSubagentPhaseBreakdown } from '@renderer/utils/aiGroupHelpers'; import { formatDuration, formatTokensCompact } from '@renderer/utils/formatters'; import { getHighlightProps, type TriggerColor } from '@shared/constants/triggerColors'; import { getModelColorClass, parseModelString } from '@shared/utils/modelParser'; import { ArrowUpRight, Bot, CheckCircle2, ChevronRight, CircleDot, Loader2, Sigma, Terminal, } from 'lucide-react'; import { ExecutionTrace } from './ExecutionTrace'; import { MetricsPill } from './MetricsPill'; import type { Process, SemanticStep } from '@renderer/types/data'; // ============================================================================= // Types // ============================================================================= interface SubagentItemProps { step: SemanticStep; subagent: Process; onClick: () => void; isExpanded: boolean; aiGroupId: string; /** Tool use ID to highlight for error deep linking */ highlightToolUseId?: string; /** Custom highlight color from trigger */ highlightColor?: TriggerColor; /** Map of tool use ID to trigger color for notification dots */ notificationColorMap?: Map<string, TriggerColor>; /** Optional callback to register tool element refs for scroll targeting */ registerToolRef?: (toolId: string, el: HTMLDivElement | null) => void; } // ============================================================================= // Main Component - Linear-style DevTools Card // ============================================================================= export const SubagentItem: React.FC<SubagentItemProps> = ({ step, subagent, onClick, isExpanded, aiGroupId, highlightToolUseId, highlightColor, notificationColorMap, registerToolRef, }) => { const description = subagent.description ?? step.content.subagentDescription ?? 'Subagent'; const subagentType = subagent.subagentType ?? 'Task'; const truncatedDesc = description.length > 60 ? description.slice(0, 60) + '...' : description; // Team member colors (when this subagent is a team member) const teamColors = subagent.team ? getTeamColorSet(subagent.team.memberColor) : null; // Detect shutdown-only team activations (trivial: just a shutdown_response) const isShutdownOnly = useMemo(() => { if (!subagent.team || !subagent.messages?.length) return false; const assistantMsgs = subagent.messages.filter((m) => m.type === 'assistant'); if (assistantMsgs.length !== 1) return false; const calls = assistantMsgs[0].toolCalls ?? []; return ( calls.length === 1 && calls[0].name === 'SendMessage' && calls[0].input?.type === 'shutdown_response' ); }, [subagent.team, subagent.messages]); // Per-tab trace expansion state (replaces local useState for true per-tab isolation) const { isSubagentTraceExpanded, toggleSubagentTraceExpansion } = useTabUI(); const isTraceManuallyExpanded = isSubagentTraceExpanded(subagent.id); // Check if contains highlighted error // Also matches when the highlight targets the parent Task tool_use that spawned this subagent const containsHighlightedError = useMemo(() => { if (!highlightToolUseId) return false; // Match parent Task tool_use ID (trigger matched the Task call itself) if (subagent.parentTaskId === highlightToolUseId) return true; // Match inner tool calls/results within the subagent if (!subagent.messages) return false; for (const msg of subagent.messages) { if (msg.toolCalls?.some((tc) => tc.id === highlightToolUseId)) return true; if (msg.toolResults?.some((tr) => tr.toolUseId === highlightToolUseId)) return true; } return false; }, [highlightToolUseId, subagent.parentTaskId, subagent.messages]); // Build display items const displayItems = useMemo(() => { if ((!isExpanded && !containsHighlightedError) || !subagent.messages?.length) { return []; } return buildDisplayItemsFromMessages(subagent.messages, []); }, [isExpanded, containsHighlightedError, subagent.messages]); // Build summary const itemsSummary = useMemo(() => { if (!isExpanded && !containsHighlightedError) { const toolCount = subagent.messages?.filter( (m) => m.type === 'assistant' && Array.isArray(m.content) && m.content.some((b) => b.type === 'tool_use') ).length ?? 0; return toolCount > 0 ? `${toolCount} tools` : ''; } return buildSummary(displayItems); }, [isExpanded, containsHighlightedError, displayItems, subagent.messages]); // Model info const modelInfo = useMemo(() => { const msg = subagent.messages?.find( (m) => m.type === 'assistant' && m.model && m.model !== '<synthetic>' ); return msg?.model ? parseModelString(msg.model) : null; }, [subagent.messages]); // Last usage const lastUsage = useMemo(() => { const messages = subagent.messages ?? []; for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].type === 'assistant' && messages[i].usage) { return messages[i].usage; } } return null; }, [subagent.messages]); // Multi-phase context breakdown (for subagents with compaction) const phaseData = useMemo(() => { if (!subagent.messages?.length) return null; return computeSubagentPhaseBreakdown(subagent.messages); }, [subagent.messages]); // Search expansion const searchExpandedSubagentIds = useStore((s) => s.searchExpandedSubagentIds); const searchCurrentSubagentItemId = useStore((s) => s.searchCurrentSubagentItemId); const shouldExpandForSearch = searchExpandedSubagentIds.has(subagent.id); // Combine manual expansion with auto-expansion for errors/search const isTraceExpanded = isTraceManuallyExpanded || containsHighlightedError || shouldExpandForSearch; const [isTraceHeaderHovered, setIsTraceHeaderHovered] = useState(false); // Outer card highlight when this subagent contains the highlighted tool const outerHighlight = useMemo(() => { if (!containsHighlightedError) return { className: '', style: undefined as React.CSSProperties | undefined }; return getHighlightProps(highlightColor); }, [containsHighlightedError, highlightColor]); // Register outer card as a tool ref target for the parent Task tool_use ID // so the navigation controller can scroll directly to this SubagentItem const outerCardRef = useCallback( (el: HTMLDivElement | null) => { if (subagent.parentTaskId && registerToolRef) { registerToolRef(subagent.parentTaskId, el); } }, [subagent.parentTaskId, registerToolRef] ); // Cumulative metrics for team members — show total output generated const cumulativeMetrics = useMemo(() => { if (!subagent.team || !subagent.metrics) return undefined; const turnCount = subagent.messages?.filter((m) => m.type === 'assistant' && m.usage).length ?? 0; return { outputTokens: subagent.metrics.outputTokens, turnCount, }; }, [subagent.team, subagent.metrics, subagent.messages]); // Computed values for metrics const hasMainImpact = subagent.mainSessionImpact && subagent.mainSessionImpact.totalTokens > 0; const hasIsolated = lastUsage && lastUsage.input_tokens + lastUsage.output_tokens > 0; const isMultiPhase = phaseData != null && phaseData.compactionCount > 0; const isolatedTotal = isMultiPhase ? phaseData.totalConsumption : lastUsage ? lastUsage.input_tokens + lastUsage.output_tokens + (lastUsage.cache_read_input_tokens ?? 0) + (lastUsage.cache_creation_input_tokens ?? 0) : 0; // Shutdown-only team activations: minimal inline row (no metrics, no expand) if (isShutdownOnly && teamColors && subagent.team) { return ( <div className="flex items-center gap-2 rounded-md px-3 py-1.5" style={{ backgroundColor: CARD_BG, border: CARD_BORDER_STYLE, opacity: 0.6, }} > <span className="size-2.5 shrink-0 rounded-full" style={{ backgroundColor: teamColors.border }} /> <span className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide" style={{ backgroundColor: teamColors.badge, color: teamColors.text, border: `1px solid ${teamColors.border}40`, }} > {subagent.team.memberName} </span> <span className="text-xs" style={{ color: CARD_ICON_MUTED }}> Shutdown confirmed </span> <span className="flex-1" /> <span className="shrink-0 font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatDuration(subagent.durationMs)} </span> </div> ); } return ( <div ref={outerCardRef} className={`overflow-hidden rounded-md transition-all duration-300 ${outerHighlight.className}`} style={{ backgroundColor: CARD_BG, border: CARD_BORDER_STYLE, ...outerHighlight.style, }} > {/* ========== Level 1: Clickable Header ========== */} <div role="button" tabIndex={0} onClick={onClick} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick(); } }} className="flex cursor-pointer items-center gap-2 px-3 py-2 transition-colors" style={{ backgroundColor: isExpanded ? CARD_HEADER_BG : 'transparent', borderBottom: isExpanded ? CARD_BORDER_STYLE : 'none', }} > {/* Expand chevron */} <ChevronRight className={`size-3.5 shrink-0 transition-transform ${isExpanded ? 'rotate-90' : ''}`} style={{ color: CARD_ICON_MUTED }} /> {/* Icon - colored dot for team members, Bot icon for regular subagents */} {teamColors ? ( <span className="size-3.5 shrink-0 rounded-full" style={{ backgroundColor: teamColors.border }} /> ) : ( <Bot className="size-4 shrink-0" style={{ color: subagent.isOngoing ? '#3b82f6' : COLOR_TEXT_MUTED }} /> )} {/* Type badge - team member name or generic type */} {teamColors && subagent.team ? ( <span className="rounded px-1.5 py-0.5 text-[10px] font-medium tracking-wide" style={{ backgroundColor: teamColors.badge, color: teamColors.text, border: `1px solid ${teamColors.border}40`, }} > {subagent.team.memberName} </span> ) : ( <span className="rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide" style={{ backgroundColor: TAG_BG, color: TAG_TEXT, border: `1px solid ${TAG_BORDER}`, }} > {subagentType} </span> )} {/* Model */} {modelInfo && ( <span className={`text-[11px] ${getModelColorClass(modelInfo.family)}`}> {modelInfo.name} </span> )} {/* Description */} <span className="flex-1 truncate text-xs" style={{ color: CARD_TEXT_LIGHT }}> {truncatedDesc} </span> {/* Status indicator */} {subagent.isOngoing ? ( <Loader2 className="size-3.5 shrink-0 animate-spin" style={{ color: '#3b82f6' }} /> ) : ( <CheckCircle2 className="size-3.5 shrink-0" style={{ color: '#22c55e' }} /> )} {/* Unified Metrics Pill — team members don't show mainSessionImpact (spawn cost only; real main impact comes from teammate messages) */} <MetricsPill mainSessionImpact={subagent.team ? undefined : subagent.mainSessionImpact} lastUsage={lastUsage ?? undefined} isolatedLabel={subagent.team ? 'Context Window' : undefined} isolatedOverride={ phaseData && phaseData.compactionCount > 0 ? phaseData.totalConsumption : undefined } phaseBreakdown={phaseData?.phases} /> {/* Duration */} <span className="shrink-0 font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatDuration(subagent.durationMs)} </span> </div> {/* ========== Level 1 Expanded: Dashboard Content ========== */} {isExpanded && ( <div className="space-y-3 p-3"> {/* ========== Row 1: Meta Info (Horizontal Flow) ========== */} <div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px]" style={{ color: COLOR_TEXT_MUTED }} > <span> <span style={{ color: CARD_ICON_MUTED }}>Type</span>{' '} <span className="font-mono" style={{ color: CARD_TEXT_LIGHT }}> {subagentType} </span> </span> <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>Duration</span>{' '} <span className="font-mono tabular-nums" style={{ color: CARD_TEXT_LIGHT }}> {formatDuration(subagent.durationMs)} </span> </span> {modelInfo && ( <> <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>Model</span>{' '} <span className={`font-mono ${getModelColorClass(modelInfo.family)}`}> {modelInfo.name} </span> </span> </> )} <span style={{ color: CARD_SEPARATOR }}>•</span> <span> <span style={{ color: CARD_ICON_MUTED }}>ID</span>{' '} <span className="inline-block max-w-[120px] truncate align-bottom font-mono" style={{ color: CARD_ICON_MUTED }} title={subagent.id} > {subagent.id.slice(0, 8)} </span> </span> </div> {/* ========== Row 2: Context Usage (Clean List) ========== */} {(hasMainImpact ?? hasIsolated) && ( <div className="pt-2"> {/* Overline title */} <div className="mb-2 text-[10px] font-semibold uppercase tracking-wider" style={{ color: CARD_ICON_MUTED }} > Context Usage </div> {/* Token rows - floating alignment */} <div className="space-y-1.5"> {hasMainImpact && !subagent.team && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <ArrowUpRight className="size-3" style={{ color: 'rgba(251, 191, 36, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> Main Context </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {subagent.mainSessionImpact!.totalTokens.toLocaleString()} </span> </div> )} {cumulativeMetrics && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <Sigma className="size-3" style={{ color: 'rgba(168, 85, 247, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> Total Output </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {cumulativeMetrics.outputTokens.toLocaleString()} <span style={{ color: CARD_ICON_MUTED }}> {' '} ({cumulativeMetrics.turnCount} turns) </span> </span> </div> )} {hasIsolated && ( <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> <CircleDot className="size-3" style={{ color: 'rgba(56, 189, 248, 0.7)' }} /> <span className="text-xs" style={{ color: COLOR_TEXT_SECONDARY }}> {subagent.team ? 'Context Window' : 'Subagent Context'} </span> </div> <span className="font-mono text-xs font-medium tabular-nums" style={{ color: CARD_TEXT_LIGHTER }} > {isolatedTotal.toLocaleString()} </span> </div> )} {/* Per-phase breakdown when multi-phase */} {isMultiPhase && phaseData.phases.map((phase) => ( <div key={phase.phaseNumber} className="flex items-center justify-between pl-5"> <span className="text-[11px]" style={{ color: CARD_ICON_MUTED }}> Phase {phase.phaseNumber} </span> <span className="font-mono text-[11px] tabular-nums" style={{ color: CARD_ICON_MUTED }} > {formatTokensCompact(phase.peakTokens)} {phase.postCompaction != null && ( <span style={{ color: '#4ade80' }}> {' '} → {formatTokensCompact(phase.postCompaction)} </span> )} </span>