From ce5625a09eeca651c0b1c3ba60a32d028026c4af Mon Sep 17 00:00:00 2001 From: Eric Barroca Date: Fri, 13 Mar 2026 11:41:21 +0100 Subject: [PATCH] feat: enhance tool status handling with error badge and compute display status --- .../chat/ModernAgentOutput/ToolCallGroup.tsx | 6 ++- .../chat/ModernAgentOutput/WorkstreamTabs.tsx | 2 - .../agent/chat/ModernAgentOutput/utils.ts | 45 ++++++++++++------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/packages/ui/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx b/packages/ui/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx index 0a34cc696..dc496a983 100644 --- a/packages/ui/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx +++ b/packages/ui/src/features/agent/chat/ModernAgentOutput/ToolCallGroup.tsx @@ -217,6 +217,7 @@ const getFilesFromDetails = (details: { files?: string[]; outputFiles?: string[] }; const TOOL_BADGE_CLASS = "text-[10px] px-1.5 py-0.5 rounded-md bg-purple-50 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 border border-purple-200 dark:border-purple-800 font-medium"; +const TOOL_ERROR_BADGE_CLASS = "text-[10px] px-1.5 py-0.5 rounded-md bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-300 border border-red-200 dark:border-red-800 font-medium"; const ASSISTANT_BADGE_CLASS = "text-[10px] px-1.5 py-0.5 rounded-md bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-200 dark:border-gray-700 font-medium"; const isToolPreambleMessage = (message: AgentMessage): boolean => { @@ -244,7 +245,10 @@ const getMessageActivityLabel = (message: AgentMessage): string => { }; const getMessageBadgeClass = (message: AgentMessage): string => { - return isToolPreambleMessage(message) ? ASSISTANT_BADGE_CLASS : TOOL_BADGE_CLASS; + if (isToolPreambleMessage(message)) return ASSISTANT_BADGE_CLASS; + const toolStatus = message.details?.tool_status; + if (toolStatus === 'error') return TOOL_ERROR_BADGE_CLASS; + return TOOL_BADGE_CLASS; }; function ToolCallItem({ message, isExpanded, onToggle, artifactRunId, classNames = {} }: ToolCallItemProps) { diff --git a/packages/ui/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx b/packages/ui/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx index 7cc6f7b2b..811d2ffb2 100644 --- a/packages/ui/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx +++ b/packages/ui/src/features/agent/chat/ModernAgentOutput/WorkstreamTabs.tsx @@ -142,8 +142,6 @@ export function extractWorkstreams( workstreams.set("main", t('agent.main')); } - console.log("Final workstreams map:", workstreams); - return workstreams; } diff --git a/packages/ui/src/features/agent/chat/ModernAgentOutput/utils.ts b/packages/ui/src/features/agent/chat/ModernAgentOutput/utils.ts index 47f6de4c7..702281dda 100644 --- a/packages/ui/src/features/agent/chat/ModernAgentOutput/utils.ts +++ b/packages/ui/src/features/agent/chat/ModernAgentOutput/utils.ts @@ -631,10 +631,7 @@ export function groupMessagesWithStreaming( const sortedMessages = [...item.messages].sort( (a, b) => getTimestampMs(a.timestamp) - getTimestampMs(b.timestamp) ); - const latestStatus = sortedMessages.reduce( - (status, msg) => getToolStatus(msg) || status, - undefined - ); + const latestStatus = computeGroupDisplayStatus(sortedMessages); groups.push({ type: 'tool_group', messages: sortedMessages, @@ -650,10 +647,7 @@ export function groupMessagesWithStreaming( (a, b) => getTimestampMs(a.timestamp) - getTimestampMs(b.timestamp) ); // Get the latest status from the group - const latestStatus = sortedMessages.reduce( - (status, msg) => getToolStatus(msg) || status, - undefined - ); + const latestStatus = computeGroupDisplayStatus(sortedMessages); groups.push({ type: 'tool_group', messages: sortedMessages, @@ -668,10 +662,7 @@ export function groupMessagesWithStreaming( (a, b) => getTimestampMs(a.timestamp) - getTimestampMs(b.timestamp) ); // Get the latest status from the group - const latestStatus = sortedMessages.reduce( - (status, msg) => getToolStatus(msg) || status, - undefined - ); + const latestStatus = computeGroupDisplayStatus(sortedMessages); groups.push({ type: 'tool_group', messages: sortedMessages, @@ -697,23 +688,45 @@ export function groupMessagesWithStreaming( /** * Merge the ToolExecutionStatus of two groups. - * Priority: error > warning > running > completed > undefined + * Priority: warning > running > completed > error > undefined + * + * Error has the lowest priority so that individual tool errors do not + * turn an entire group red – only the specific tool badge is coloured. */ export function mergeToolStatus( a: ToolExecutionStatus | undefined, b: ToolExecutionStatus | undefined, ): ToolExecutionStatus | undefined { const priority: Record = { - error: 4, + error: 0, warning: 3, running: 2, completed: 1, }; - const pa = a ? priority[a] : 0; - const pb = b ? priority[b] : 0; + const pa = a ? priority[a] : -1; + const pb = b ? priority[b] : -1; return pa >= pb ? (a ?? b) : b; } +/** + * Compute a display status for a tool group. + * Individual errors are shown on the badge only; the group turns red + * only when every tool in the group has errored. + */ +export function computeGroupDisplayStatus( + messages: AgentMessage[] +): ToolExecutionStatus | undefined { + const statuses = messages.map(getToolStatus).filter(Boolean) as ToolExecutionStatus[]; + if (statuses.length === 0) return undefined; + if (statuses.every(s => s === 'error')) return 'error'; + return statuses + .filter(s => s !== 'error') + .reduce( + (acc, s) => mergeToolStatus(acc, s), + undefined + ); +} + /** * Post-processing step: merge consecutive tool_group entries into a single * larger group so the UI shows one block instead of many.