From 10fa9eca3a32cc5d051f00ced31eee3da4e55d2f Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 8 Jun 2026 04:00:30 +0100 Subject: [PATCH] Mark Assistant provider retries as live activity --- .../v6/internal/subsystems/ai-runtime.md | 4 ++ .../src/components/AI/Chat/MessageItem.tsx | 38 ++++++++++++-- .../AI/Chat/__tests__/AIChat.test.tsx | 3 ++ .../AI/Chat/__tests__/MessageItem.test.tsx | 2 + .../Chat/__tests__/activeTurnStatus.test.ts | 8 +-- .../components/AI/Chat/activeTurnStatus.ts | 14 +++-- .../src/components/AI/Chat/index.tsx | 52 ++++++++++++++----- 7 files changed, 97 insertions(+), 24 deletions(-) diff --git a/docs/release-control/v6/internal/subsystems/ai-runtime.md b/docs/release-control/v6/internal/subsystems/ai-runtime.md index 15a879f53..4625529b7 100644 --- a/docs/release-control/v6/internal/subsystems/ai-runtime.md +++ b/docs/release-control/v6/internal/subsystems/ai-runtime.md @@ -348,6 +348,10 @@ deriving an older display status from `workflowStatusHistory`. export status lines must normalize internal tool identifiers such as `pulse_query`, `pulse_read`, and `pulse_exec` into operator-facing activity labels, while preserving the raw typed event payload for Details/debug paths. + Provider retry states are a distinct live activity kind: a `provider_retry` + workflow status must render as retrying in the active-turn footer and live + workflow row so the operator sees selected-route recovery in progress rather + than another generic waiting/thinking state. Assistant-authored visible answer prose is part of the same boundary: model text may describe the action in operator vocabulary such as "read command" or "inventory lookup", but raw internal identifiers such as `pulse_read` diff --git a/frontend-modern/src/components/AI/Chat/MessageItem.tsx b/frontend-modern/src/components/AI/Chat/MessageItem.tsx index d0e6e8b5b..85a37df7d 100644 --- a/frontend-modern/src/components/AI/Chat/MessageItem.tsx +++ b/frontend-modern/src/components/AI/Chat/MessageItem.tsx @@ -26,7 +26,7 @@ import { ThinkingBlock } from './ThinkingBlock'; import { getAssistantAnswerText } from './assistantAnswerText'; import { stripAssistantOutputArtifacts } from './assistantOutputHygiene'; import { formatAssistantTurnDuration } from './assistantTurnSummary'; -import { formatAssistantWorkflowStatus } from './activeTurnStatus'; +import { assistantWorkflowStatusKind, formatAssistantWorkflowStatus } from './activeTurnStatus'; import { groupStreamEventsForDisplay } from './streamEventGrouping'; import { createPacedWorkflowStatus, @@ -429,6 +429,22 @@ export const MessageItem: Component = (props) => { } return `${message}${elapsedSuffix}`; }; + const workflowStatusToneClass = (status?: WorkflowStatus) => { + const kind = assistantWorkflowStatusKind(status); + if (kind === 'retrying') { + return 'border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900/60 dark:bg-amber-950/30 dark:text-amber-200'; + } + if (kind === 'tool') { + return 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/60 dark:bg-blue-950/30 dark:text-blue-200'; + } + return 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/60 dark:bg-blue-950/30 dark:text-blue-200'; + }; + const workflowStatusDotClass = (status?: WorkflowStatus) => { + const kind = assistantWorkflowStatusKind(status); + if (kind === 'retrying') return 'bg-amber-500'; + if (kind === 'tool') return 'bg-blue-500'; + return 'bg-blue-500'; + }; const workflowStatusText = createMemo(() => formatWorkflowStatus(currentWorkflowStatus(), true), ); @@ -578,11 +594,18 @@ export const MessageItem: Component = (props) => { - + {workflowStatusText()} @@ -660,16 +683,21 @@ export const MessageItem: Component = (props) => { } >