/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../../colors.js';
import { TaskResultDisplay } from '@qwen-code/qwen-code-core';
export interface SubagentExecutionDisplayProps {
data: TaskResultDisplay;
}
/**
* Component to display subagent execution progress and results.
* This is now a pure component that renders the provided SubagentExecutionResultDisplay data.
* Real-time updates are handled by the parent component updating the data prop.
*/
export const SubagentExecutionDisplay: React.FC<
SubagentExecutionDisplayProps
> = ({ data }) => (
{/* Header with subagent name and status */}
{data.subagentName}
•
{/* Task description */}
Task:
{data.taskDescription}
{/* Progress section for running tasks */}
{data.status === 'running' && (
)}
{/* Results section for completed/failed tasks */}
{(data.status === 'completed' || data.status === 'failed') && (
)}
);
/**
* Status dot component with similar height as text
*/
const StatusDot: React.FC<{
status: TaskResultDisplay['status'];
}> = ({ status }) => {
const color = React.useMemo(() => {
switch (status) {
case 'running':
return Colors.AccentYellow;
case 'completed':
return Colors.AccentGreen;
case 'failed':
return Colors.AccentRed;
default:
return Colors.Gray;
}
}, [status]);
return (
●
);
};
/**
* Status indicator component
*/
const StatusIndicator: React.FC<{
status: TaskResultDisplay['status'];
}> = ({ status }) => {
switch (status) {
case 'running':
return Running;
case 'completed':
return Completed;
case 'failed':
return Failed;
default:
return Unknown;
}
};
/**
* Progress section for running executions
*/
const ProgressSection: React.FC<{
progress: {
toolCalls?: Array<{
name: string;
status: 'executing' | 'success' | 'failed';
error?: string;
args?: Record;
result?: string;
returnDisplay?: string;
}>;
};
}> = ({ progress }) => (
{progress.toolCalls && progress.toolCalls.length > 0 && (
)}
);
/**
* Clean tool calls list - format consistent with ToolInfo in ToolMessage.tsx
*/
const CleanToolCallsList: React.FC<{
toolCalls: Array<{
name: string;
status: 'executing' | 'success' | 'failed';
error?: string;
args?: Record;
result?: string;
returnDisplay?: string;
}>;
}> = ({ toolCalls }) => (
Tools:
{toolCalls.map((toolCall, index) => (
))}
);
/**
* Individual tool call item - consistent with ToolInfo format
*/
const CleanToolCallItem: React.FC<{
toolCall: {
name: string;
status: 'executing' | 'success' | 'failed';
error?: string;
args?: Record;
result?: string;
returnDisplay?: string;
};
}> = ({ toolCall }) => {
const STATUS_INDICATOR_WIDTH = 3;
// Map subagent status to ToolCallStatus-like display
const statusIcon = React.useMemo(() => {
switch (toolCall.status) {
case 'executing':
return ⊷; // Using same as ToolMessage
case 'success':
return ✔;
case 'failed':
return (
x
);
default:
return o;
}
}, [toolCall.status]);
const description = getToolDescription(toolCall);
// Get first line of returnDisplay for truncated output
const truncatedOutput = React.useMemo(() => {
if (!toolCall.returnDisplay) return '';
const firstLine = toolCall.returnDisplay.split('\n')[0];
return firstLine.length > 80
? firstLine.substring(0, 80) + '...'
: firstLine;
}, [toolCall.returnDisplay]);
return (
{/* First line: status icon + tool name + description (consistent with ToolInfo) */}
{statusIcon}
{toolCall.name}
{' '}
{description}
{toolCall.error && (
- {toolCall.error}
)}
{/* Second line: truncated returnDisplay output */}
{truncatedOutput && (
{truncatedOutput}
)}
);
};
/**
* Helper function to get tool description from args
*/
const getToolDescription = (toolCall: {
name: string;
args?: Record;
}): string => {
if (!toolCall.args) return '';
// Handle common tool patterns
if (toolCall.name === 'Glob' && toolCall.args['glob_pattern']) {
return `"${toolCall.args['glob_pattern']}"`;
}
if (toolCall.name === 'ReadFile' && toolCall.args['target_file']) {
const path = toolCall.args['target_file'] as string;
return path.split('/').pop() || path;
}
if (toolCall.name === 'SearchFileContent' && toolCall.args['pattern']) {
return `"${toolCall.args['pattern']}"`;
}
// Generic fallback
const firstArg = Object.values(toolCall.args)[0];
if (typeof firstArg === 'string' && firstArg.length < 50) {
return firstArg;
}
return '';
};
/**
* Execution summary details component
*/
const ExecutionSummaryDetails: React.FC<{
data: TaskResultDisplay;
}> = ({ data }) => {
// Parse execution summary for structured data
const summaryData = React.useMemo(() => {
if (!data.executionSummary) return null;
// Try to extract structured data from execution summary
const durationMatch = data.executionSummary.match(/Duration:\s*([^\n]+)/i);
const roundsMatch = data.executionSummary.match(/Rounds:\s*(\d+)/i);
const tokensMatch = data.executionSummary.match(/Tokens:\s*([\d,]+)/i);
return {
duration: durationMatch?.[1] || 'N/A',
rounds: roundsMatch?.[1] || 'N/A',
tokens: tokensMatch?.[1] || 'N/A',
};
}, [data.executionSummary]);
if (!summaryData) {
return (
• No summary available
);
}
return (
• Duration: {summaryData.duration}
• Rounds: {summaryData.rounds}
• Tokens: {summaryData.tokens}
);
};
/**
* Tool usage statistics component
*/
const ToolUsageStats: React.FC<{
toolCalls: Array<{
name: string;
status: 'executing' | 'success' | 'failed';
error?: string;
args?: Record;
result?: string;
returnDisplay?: string;
}>;
}> = ({ toolCalls }) => {
const stats = React.useMemo(() => {
const total = toolCalls.length;
const successful = toolCalls.filter(
(call) => call.status === 'success',
).length;
const failed = toolCalls.filter((call) => call.status === 'failed').length;
const successRate =
total > 0 ? ((successful / total) * 100).toFixed(1) : '0.0';
return { total, successful, failed, successRate };
}, [toolCalls]);
return (
• Total Calls: {stats.total}
• Success Rate:{' '}
{stats.successRate}% (
{stats.successful} success,{' '}
{stats.failed} failed)
);
};
/**
* Results section for completed executions - matches the clean layout from the image
*/
const ResultsSection: React.FC<{
data: TaskResultDisplay;
}> = ({ data }) => (
{/* Tool calls section - clean list format */}
{data.progress?.toolCalls && data.progress.toolCalls.length > 0 && (
)}
{/* Task Completed section */}
📄
Task Completed:
{data.taskDescription}
{/* Execution Summary section */}
📊
Execution Summary:
{/* Tool Usage section */}
{data.progress?.toolCalls && data.progress.toolCalls.length > 0 && (
🔧
Tool Usage:
)}
{/* Error reason for failed tasks */}
{data.status === 'failed' && data.terminateReason && (
❌ Failed:
{data.terminateReason}
)}
);