/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { Box, Text } from 'ink'; import { theme } from '../../semantic-colors.js'; import Spinner from 'ink-spinner'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import type { SubagentProgress, SubagentActivityItem, } from '@google/gemini-cli-core'; import { TOOL_STATUS } from '../../constants.js'; import { STATUS_INDICATOR_WIDTH } from './ToolShared.js'; import { safeJsonToMarkdown } from '@google/gemini-cli-core'; export interface SubagentProgressDisplayProps { progress: SubagentProgress; terminalWidth: number; } export const formatToolArgs = (args?: string): string => { if (!args) return ''; try { const parsed: unknown = JSON.parse(args); if (typeof parsed !== 'object' || parsed === null) { return args; } if ( 'description' in parsed && typeof parsed.description === 'string' && parsed.description ) { return parsed.description; } if ('command' in parsed && typeof parsed.command === 'string') return parsed.command; if ('file_path' in parsed && typeof parsed.file_path === 'string') return parsed.file_path; if ('dir_path' in parsed && typeof parsed.dir_path === 'string') return parsed.dir_path; if ('query' in parsed && typeof parsed.query === 'string') return parsed.query; if ('url' in parsed && typeof parsed.url === 'string') return parsed.url; if ('target' in parsed && typeof parsed.target === 'string') return parsed.target; return args; } catch { return args; } }; export const SubagentProgressDisplay: React.FC< SubagentProgressDisplayProps > = ({ progress, terminalWidth }) => { let headerText: string | undefined; let headerColor = theme.text.secondary; if (progress.state === 'cancelled') { headerText = `Subagent ${progress.agentName} was cancelled.`; headerColor = theme.status.warning; } else if (progress.state === 'error') { headerText = `Subagent ${progress.agentName} failed.`; headerColor = theme.status.error; } else if (progress.state === 'completed') { headerText = `Subagent ${progress.agentName} completed.`; headerColor = theme.status.success; } else { headerText = `Running subagent ${progress.agentName}...`; headerColor = theme.text.primary; } return ( {headerText && ( {headerText} )} {progress.recentActivity.map((item: SubagentActivityItem) => { if (item.type === 'thought') { const isCancellation = item.content === 'Request cancelled.'; const icon = isCancellation ? 'ℹ ' : '💭'; const color = isCancellation ? theme.status.warning : theme.text.secondary; return ( {icon} {item.content} ); } else if (item.type === 'tool_call') { const statusSymbol = item.status === 'running' ? ( ) : item.status === 'completed' ? ( {TOOL_STATUS.SUCCESS} ) : item.status === 'cancelled' ? ( {TOOL_STATUS.CANCELED} ) : ( {TOOL_STATUS.ERROR} ); const formattedArgs = item.description || formatToolArgs(item.args); const displayArgs = formattedArgs.length > 60 ? formattedArgs.slice(0, 60) + '...' : formattedArgs; return ( {statusSymbol} {item.displayName || item.content} {displayArgs && ( {displayArgs} )} ); } return null; })} {progress.state === 'completed' && progress.result && ( {progress.terminateReason && progress.terminateReason !== 'GOAL' && ( Agent Finished Early ({progress.terminateReason}) )} )} ); };