diff --git a/packages/cli/src/ui/components/MainContent.test.tsx b/packages/cli/src/ui/components/MainContent.test.tsx index ec75573d75..2bc6ee27bc 100644 --- a/packages/cli/src/ui/components/MainContent.test.tsx +++ b/packages/cli/src/ui/components/MainContent.test.tsx @@ -6,11 +6,7 @@ import { renderWithProviders } from '../../test-utils/render.js'; import { createMockSettings } from '../../test-utils/settings.js'; -import { - makeFakeConfig, - CoreToolCallStatus, - UPDATE_TOPIC_TOOL_NAME, -} from '@google/gemini-cli-core'; +import { makeFakeConfig, CoreToolCallStatus } from '@google/gemini-cli-core'; import { waitFor } from '../../test-utils/async.js'; import { MainContent } from './MainContent.js'; import { getToolGroupBorderAppearance } from '../utils/borderStyles.js'; @@ -732,158 +728,6 @@ describe('MainContent', () => { unmount(); }); - describe('Narration Suppression', () => { - const settingsWithNarration = createMockSettings({ - merged: { - ui: { inlineThinkingMode: 'expanded' }, - experimental: { topicUpdateNarration: true }, - }, - }); - - it('suppresses thinking ALWAYS when narration is enabled', async () => { - mockUseSettings.mockReturnValue(settingsWithNarration); - const uiState = { - ...defaultMockUiState, - history: [ - { id: 1, type: 'user' as const, text: 'Hello' }, - { - id: 2, - type: 'thinking' as const, - thought: { - subject: 'Thinking...', - description: 'Thinking about hello', - }, - }, - { id: 3, type: 'gemini' as const, text: 'I am helping.' }, - ], - }; - - const { lastFrame, unmount } = await renderWithProviders( - , - { - uiState: uiState as Partial, - settings: settingsWithNarration, - }, - ); - - const output = lastFrame(); - expect(output).not.toContain('Thinking...'); - expect(output).toContain('I am helping.'); - unmount(); - }); - - it('suppresses text in intermediate turns (contains non-topic tools)', async () => { - mockUseSettings.mockReturnValue(settingsWithNarration); - const uiState = { - ...defaultMockUiState, - history: [ - { id: 100, type: 'user' as const, text: 'Search' }, - { - id: 101, - type: 'gemini' as const, - text: 'I will now search the files.', - }, - { - id: 102, - type: 'tool_group' as const, - tools: [ - { - callId: '1', - name: 'ls', - args: { path: '.' }, - status: CoreToolCallStatus.Success, - }, - ], - }, - ], - }; - - const { lastFrame, unmount } = await renderWithProviders( - , - { - uiState: uiState as Partial, - settings: settingsWithNarration, - }, - ); - - const output = lastFrame(); - expect(output).not.toContain('I will now search the files.'); - unmount(); - }); - - it('suppresses text that precedes a topic tool in the same turn', async () => { - mockUseSettings.mockReturnValue(settingsWithNarration); - const uiState = { - ...defaultMockUiState, - history: [ - { id: 200, type: 'user' as const, text: 'Hello' }, - { id: 201, type: 'gemini' as const, text: 'I will now help you.' }, - { - id: 202, - type: 'tool_group' as const, - tools: [ - { - callId: '1', - name: UPDATE_TOPIC_TOOL_NAME, - args: { title: 'Helping', summary: 'Helping the user' }, - status: CoreToolCallStatus.Success, - }, - ], - }, - ], - }; - - const { lastFrame, unmount } = await renderWithProviders( - , - { - uiState: uiState as Partial, - settings: settingsWithNarration, - }, - ); - - const output = lastFrame(); - expect(output).not.toContain('I will now help you.'); - expect(output).toContain('Helping'); - expect(output).toContain('Helping the user'); - unmount(); - }); - - it('shows text in the final turn if it comes AFTER the topic tool', async () => { - mockUseSettings.mockReturnValue(settingsWithNarration); - const uiState = { - ...defaultMockUiState, - history: [ - { id: 300, type: 'user' as const, text: 'Hello' }, - { - id: 301, - type: 'tool_group' as const, - tools: [ - { - callId: '1', - name: UPDATE_TOPIC_TOOL_NAME, - args: { title: 'Final Answer', summary: 'I have finished' }, - status: CoreToolCallStatus.Success, - }, - ], - }, - { id: 302, type: 'gemini' as const, text: 'Here is your answer.' }, - ], - }; - - const { lastFrame, unmount } = await renderWithProviders( - , - { - uiState: uiState as Partial, - settings: settingsWithNarration, - }, - ); - - const output = lastFrame(); - expect(output).toContain('Here is your answer.'); - unmount(); - }); - }); - it('renders multiple thinking messages sequentially correctly', async () => { mockUseSettings.mockReturnValue({ merged: { diff --git a/packages/cli/src/ui/components/MainContent.tsx b/packages/cli/src/ui/components/MainContent.tsx index 527462be28..b46af4965b 100644 --- a/packages/cli/src/ui/components/MainContent.tsx +++ b/packages/cli/src/ui/components/MainContent.tsx @@ -91,47 +91,20 @@ export const MainContent = () => { const flags = new Array(combinedHistory.length).fill(false); if (topicUpdateNarrationEnabled) { - let turnIsIntermediate = false; - let hasTopicToolInTurn = false; - + let toolGroupInTurn = false; for (let i = combinedHistory.length - 1; i >= 0; i--) { const item = combinedHistory[i]; if (item.type === 'user' || item.type === 'user_shell') { - turnIsIntermediate = false; - hasTopicToolInTurn = false; + toolGroupInTurn = false; } else if (item.type === 'tool_group') { - const hasTopic = item.tools.some((t) => isTopicTool(t.name)); - const hasNonTopic = item.tools.some((t) => !isTopicTool(t.name)); - if (hasTopic) { - hasTopicToolInTurn = true; - } - if (hasNonTopic) { - turnIsIntermediate = true; - } + toolGroupInTurn = item.tools.some((t) => isTopicTool(t.name)); } else if ( - item.type === 'thinking' || - item.type === 'gemini' || - item.type === 'gemini_content' + (item.type === 'thinking' || + item.type === 'gemini' || + item.type === 'gemini_content') && + toolGroupInTurn ) { - // Rule 1: Always suppress thinking when narration is enabled to avoid - // "flashing" as the model starts its response, and because the Topic - // UI provides the necessary high-level intent. - if (item.type === 'thinking') { - flags[i] = true; - continue; - } - - // Rule 2: Suppress text in intermediate turns (turns containing non-topic - // tools) to hide mechanical narration. - if (turnIsIntermediate) { - flags[i] = true; - } - - // Rule 3: Suppress text that precedes a topic tool in the same turn, - // as the topic tool "replaces" it. - if (hasTopicToolInTurn) { - flags[i] = true; - } + flags[i] = true; } } }