/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { useEffect, useId } from 'react'; import { Box, Text } from 'ink'; import { theme } from '../../semantic-colors.js'; import type { IndividualToolCallDisplay } from '../../types.js'; import { isSubagentProgress, checkExhaustive, type SubagentActivityItem, } from '@google/gemini-cli-core'; import { SubagentProgressDisplay, formatToolArgs, } from './SubagentProgressDisplay.js'; import { useOverflowActions } from '../../contexts/OverflowContext.js'; export interface SubagentGroupDisplayProps { toolCalls: IndividualToolCallDisplay[]; availableTerminalHeight?: number; terminalWidth: number; borderColor?: string; borderDimColor?: boolean; isFirst?: boolean; isExpandable?: boolean; } export const SubagentGroupDisplay: React.FC = ({ toolCalls, availableTerminalHeight, terminalWidth, borderColor, borderDimColor, isFirst, isExpandable = true, }) => { const isExpanded = availableTerminalHeight === undefined; const overflowActions = useOverflowActions(); const uniqueId = useId(); const overflowId = `subagent-${uniqueId}`; useEffect(() => { if (isExpandable && overflowActions) { // Register with the global overflow system so "ctrl+o to expand" shows in the sticky footer // and AppContainer passes the shortcut through. overflowActions.addOverflowingId(overflowId); } return () => { if (overflowActions) { overflowActions.removeOverflowingId(overflowId); } }; }, [isExpandable, overflowActions, overflowId]); if (toolCalls.length === 0) { return null; } let headerText = ''; if (toolCalls.length === 1) { const singleAgent = toolCalls[0].resultDisplay; if (isSubagentProgress(singleAgent)) { switch (singleAgent.state) { case 'completed': headerText = 'Agent Completed'; break; case 'cancelled': headerText = 'Agent Cancelled'; break; case 'error': headerText = 'Agent Error'; break; default: headerText = 'Running Agent...'; break; } } else { headerText = 'Running Agent...'; } } else { let completedCount = 0; let runningCount = 0; for (const tc of toolCalls) { const progress = tc.resultDisplay; if (isSubagentProgress(progress)) { if (progress.state === 'completed') completedCount++; else if (progress.state === 'running') runningCount++; } else { // It hasn't emitted progress yet, but it is "running" runningCount++; } } if (completedCount === toolCalls.length) { headerText = `${toolCalls.length} Agents Completed`; } else if (completedCount > 0) { headerText = `${toolCalls.length} Agents (${runningCount} running, ${completedCount} completed)...`; } else { headerText = `Running ${toolCalls.length} Agents...`; } } const toggleText = `(ctrl+o to ${isExpanded ? 'collapse' : 'expand'})`; const renderCollapsedRow = ( key: string, agentName: string, icon: React.ReactNode, content: string, displayArgs?: string, ) => ( {icon} {agentName} · {content} {displayArgs && ` ${displayArgs}`} ); return ( {headerText} {isExpandable && {toggleText}} {toolCalls.map((toolCall) => { const progress = toolCall.resultDisplay; if (!isSubagentProgress(progress)) { const agentName = toolCall.name || 'agent'; if (!isExpanded) { return renderCollapsedRow( toolCall.callId, agentName, !, 'Starting...', ); } else { return ( ! {agentName} Starting... ); } } const lastActivity: SubagentActivityItem | undefined = progress.recentActivity[progress.recentActivity.length - 1]; // Collapsed View: Show single compact line per agent if (!isExpanded) { let content = 'Starting...'; let formattedArgs: string | undefined; if (progress.state === 'completed') { if ( progress.terminateReason && progress.terminateReason !== 'GOAL' ) { content = `Finished Early (${progress.terminateReason})`; } else { content = 'Completed successfully'; } } else if (lastActivity) { // Match expanded view logic exactly: // Primary text: displayName || content content = lastActivity.displayName || lastActivity.content; // Secondary text: description || formatToolArgs(args) if (lastActivity.description) { formattedArgs = lastActivity.description; } else if (lastActivity.type === 'tool_call' && lastActivity.args) { formattedArgs = formatToolArgs(lastActivity.args); } } const displayArgs = progress.state === 'completed' ? '' : formattedArgs; const renderStatusIcon = () => { const state = progress.state ?? 'running'; switch (state) { case 'running': return !; case 'completed': return ; case 'cancelled': return ; case 'error': return ; default: return checkExhaustive(state); } }; return renderCollapsedRow( toolCall.callId, progress.agentName, renderStatusIcon(), lastActivity?.type === 'thought' ? `💭 ${content}` : content, displayArgs, ); } // Expanded View: Render full history return ( ); })} ); };