diff --git a/src/renderer/src/components/native-chat/NativeChatMessageList.test.tsx b/src/renderer/src/components/native-chat/NativeChatMessageList.test.tsx index 2a387d16e65..5b71136b86f 100644 --- a/src/renderer/src/components/native-chat/NativeChatMessageList.test.tsx +++ b/src/renderer/src/components/native-chat/NativeChatMessageList.test.tsx @@ -124,7 +124,7 @@ describe('NativeChatMessageList assistant messages', () => { ) }) - it('animates the running tool and the tail spinner without pulsing the activity text', () => { + it('keeps the broad fallback distinct from the running tool row', () => { render( { ] }} isWorking - turnActivity={{ kind: 'description', text: 'Verifying the change' }} expandSignal={false} fontScale={1} /> ) - expect(screen.getByText('Running pnpm test')).toHaveClass('animate-pulse') - const activity = screen.getByText('Verifying the change') + const toolLabel = screen.getByText('Running pnpm test') + expect(toolLabel).toHaveClass('animate-pulse') + expect(screen.getAllByText('Running pnpm test')).toHaveLength(1) + const activity = screen.getByText('Working…') + expect(activity.textContent).not.toBe(toolLabel.textContent) + expect(activity).not.toHaveTextContent('shell') + expect(activity).not.toHaveTextContent('pnpm test') const spinner = activity.closest('[data-native-chat-turn-activity]')?.querySelector('svg') expect(activity).not.toHaveClass('animate-pulse', 'animate-spin') expect(spinner).toHaveClass('animate-spin', 'motion-reduce:animate-none') }) - it('restates a completed tool without claiming that it is still running', () => { + it('uses the broad fallback after a tool settles', () => { render( ) - const activity = screen.getByText('Continuing after pnpm test…') + const settledTool = screen.getByText('shell pnpm test') + const activity = screen.getByText('Working…') + expect(activity.textContent).not.toBe(settledTool.textContent) + expect(activity).not.toHaveTextContent('shell') + expect(activity).not.toHaveTextContent('pnpm test') expect(activity).not.toHaveClass('animate-pulse', 'animate-spin') expect(activity.closest('[data-native-chat-turn-activity]')?.querySelector('svg')).toHaveClass( 'animate-spin' ) - expect(screen.queryByText('Running pnpm test')).toBeNull() }) it('keeps a completed tool row static while the turn tail spins, then removes the tail', () => { diff --git a/src/renderer/src/components/native-chat/NativeChatTurnActivityLine.tsx b/src/renderer/src/components/native-chat/NativeChatTurnActivityLine.tsx index 0adc967a6f7..da11773105d 100644 --- a/src/renderer/src/components/native-chat/NativeChatTurnActivityLine.tsx +++ b/src/renderer/src/components/native-chat/NativeChatTurnActivityLine.tsx @@ -1,31 +1,13 @@ import { Loader2 } from 'lucide-react' import { translate } from '@/i18n/i18n' import type { NativeChatTurnActivity } from './native-chat-turn-activity' -import { nativeChatToolActivityLabel } from './native-chat-tool-activity-label' -import { describeActiveToolCall } from '../../../../shared/native-chat-tool-activity' - -function completedToolActivityLabel(activity: Extract) { - const { preview, toolName } = describeActiveToolCall(activity.call) - return translate( - 'components.native-chat.activity.continuingAfter', - 'Continuing after {{activity}}…', - { activity: preview || toolName } - ) -} export function NativeChatTurnActivityLine({ activity }: { activity?: NativeChatTurnActivity | null }): React.JSX.Element { - const label = - activity?.kind === 'description' - ? activity.text - : activity?.kind === 'tool' - ? activity.call.state === 'running' - ? nativeChatToolActivityLabel(activity.call) - : completedToolActivityLabel(activity) - : translate('components.native-chat.status.working', 'Working…') + const label = activity?.text ?? translate('components.native-chat.status.working', 'Working…') return (
{ expect(activity).toEqual({ kind: 'description', text: 'Preparing the answer' }) }) - it('returns the latest tool for a safe present-tense restatement', () => { + it('ignores active and settled tools so the tail can use a broad fallback', () => { const activity = selectStructuredAgentTurnActivity( [ turnStart, @@ -42,16 +42,19 @@ describe('selectStructuredAgentTurnActivity', () => { kind: 'tool-call', name: 'shell', input: { command: 'pnpm test' }, + state: 'running' + }), + item(3, { + kind: 'tool-call', + name: 'shell', + input: { command: 'pnpm lint' }, state: 'completed' }) ], 'turn-1' ) - expect(activity).toMatchObject({ - kind: 'tool', - call: { name: 'shell', input: { command: 'pnpm test' }, state: 'completed' } - }) + expect(activity).toBeNull() }) it('ignores diagnostic provider frames and returns nothing after the turn settles', () => { diff --git a/src/renderer/src/components/native-chat/native-chat-turn-activity.ts b/src/renderer/src/components/native-chat/native-chat-turn-activity.ts index f7d06752c6f..37f9fc75015 100644 --- a/src/renderer/src/components/native-chat/native-chat-turn-activity.ts +++ b/src/renderer/src/components/native-chat/native-chat-turn-activity.ts @@ -1,12 +1,7 @@ import type { AgentJournalRenderItem } from '../../../../shared/agent-session-journal-types' import { normalizePromptField } from '../../../../shared/agent-status-field-normalization' -import type { NativeChatBlock } from '../../../../shared/native-chat-types' -type ToolCall = Extract - -export type NativeChatTurnActivity = - | { kind: 'description'; text: string } - | { kind: 'tool'; call: ToolCall } +export type NativeChatTurnActivity = { kind: 'description'; text: string } function activityLine(text: string): string | null { const lines = text @@ -17,7 +12,7 @@ function activityLine(text: string): string | null { return latest ? normalizePromptField(latest) || null : null } -/** Prefer provider-authored activity copy, then restate the latest tool in present tense. */ +/** Prefer provider-authored activity copy; callers provide the broad fallback. */ export function selectStructuredAgentTurnActivity( items: readonly AgentJournalRenderItem[], turnId: string | null @@ -42,20 +37,5 @@ export function selectStructuredAgentTurnActivity( return { kind: 'description', text } } } - for (let index = turnItems.length - 1; index >= 0; index -= 1) { - const body = turnItems[index]?.body - if (body?.kind === 'tool-call') { - return { - kind: 'tool', - call: { type: 'tool-call', name: body.name, input: body.input, state: body.state } - } - } - if (body?.kind === 'diff') { - return { - kind: 'tool', - call: { type: 'tool-call', name: 'Diff', input: { path: body.path } } - } - } - } return null }