From f87468c64403203b4a099ee8a3c1aebf25bd3207 Mon Sep 17 00:00:00 2001 From: Jerop Kipruto Date: Fri, 13 Feb 2026 17:20:14 -0500 Subject: [PATCH] refactor: use `CoreToolCallStatus` in the the history data model (#19033) --- packages/cli/src/nonInteractiveCli.test.ts | 25 ++++----- packages/cli/src/ui/App.test.tsx | 6 +-- packages/cli/src/ui/AppContainer.test.tsx | 3 +- packages/cli/src/ui/AppContainer.tsx | 8 +-- .../AlternateBufferQuittingDisplay.test.tsx | 10 ++-- .../cli/src/ui/components/Composer.test.tsx | 10 ++-- packages/cli/src/ui/components/Composer.tsx | 16 +++--- .../ui/components/HistoryItemDisplay.test.tsx | 11 ++-- .../src/ui/components/MainContent.test.tsx | 4 +- .../components/ToolConfirmationQueue.test.tsx | 20 +++---- .../messages/ShellToolMessage.test.tsx | 31 ++++++----- .../components/messages/ShellToolMessage.tsx | 11 ++-- .../src/ui/components/messages/Todo.test.tsx | 4 +- .../messages/ToolGroupMessage.test.tsx | 49 +++++++++-------- .../components/messages/ToolGroupMessage.tsx | 28 ++++++---- .../components/messages/ToolMessage.test.tsx | 25 +++++---- .../messages/ToolMessageFocusHint.test.tsx | 10 ++-- .../messages/ToolMessageRawMarkdown.test.tsx | 6 ++- .../ToolResultDisplayOverflow.test.tsx | 9 ++-- .../src/ui/components/messages/ToolShared.tsx | 21 ++++---- .../ToolStickyHeaderRegression.test.tsx | 6 +-- .../ToolGroupMessage.test.tsx.snap | 4 +- .../ui/contexts/ToolActionsContext.test.tsx | 7 +-- .../src/ui/hooks/atCommandProcessor.test.ts | 12 +++-- .../cli/src/ui/hooks/atCommandProcessor.ts | 14 ++--- .../ui/hooks/shellCommandProcessor.test.tsx | 10 ++-- .../cli/src/ui/hooks/shellCommandProcessor.ts | 23 ++++---- .../ui/hooks/slashCommandProcessor.test.tsx | 2 +- .../cli/src/ui/hooks/slashCommandProcessor.ts | 5 +- packages/cli/src/ui/hooks/toolMapping.test.ts | 16 +++--- packages/cli/src/ui/hooks/toolMapping.ts | 28 +--------- .../cli/src/ui/hooks/useConfirmingTool.ts | 4 +- .../cli/src/ui/hooks/useGeminiStream.test.tsx | 6 +-- packages/cli/src/ui/hooks/useGeminiStream.ts | 35 +++++++------ .../src/ui/hooks/useSessionBrowser.test.ts | 13 ++--- .../cli/src/ui/hooks/useToolScheduler.test.ts | 19 +++---- .../ui/hooks/useTurnActivityMonitor.test.ts | 6 +-- packages/cli/src/ui/types.ts | 52 ++++++++++++++----- packages/cli/src/utils/sessionUtils.ts | 11 ++-- .../cli/src/zed-integration/acpResume.test.ts | 10 ++-- 40 files changed, 322 insertions(+), 268 deletions(-) diff --git a/packages/cli/src/nonInteractiveCli.test.ts b/packages/cli/src/nonInteractiveCli.test.ts index bc9cd192cf..206d011e63 100644 --- a/packages/cli/src/nonInteractiveCli.test.ts +++ b/packages/cli/src/nonInteractiveCli.test.ts @@ -20,6 +20,7 @@ import { uiTelemetryService, FatalInputError, CoreEvent, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import type { Part } from '@google/genai'; import { runNonInteractive } from './nonInteractiveCli.js'; @@ -327,7 +328,7 @@ describe('runNonInteractive', () => { const toolResponse: Part[] = [{ text: 'Tool response' }]; mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: { callId: 'tool-1', name: 'testTool', @@ -403,7 +404,7 @@ describe('runNonInteractive', () => { // 2. Mock the execution of the tools. We just need them to succeed. mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: toolCallEvent.value, // This is generic enough for both calls tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -469,7 +470,7 @@ describe('runNonInteractive', () => { }; mockSchedulerSchedule.mockResolvedValue([ { - status: 'error', + status: CoreToolCallStatus.Error, request: { callId: 'tool-1', name: 'errorTool', @@ -573,7 +574,7 @@ describe('runNonInteractive', () => { }; mockSchedulerSchedule.mockResolvedValue([ { - status: 'error', + status: CoreToolCallStatus.Error, request: { callId: 'tool-1', name: 'nonexistentTool', @@ -748,7 +749,7 @@ describe('runNonInteractive', () => { const toolResponse: Part[] = [{ text: 'Tool executed successfully' }]; mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: { callId: 'tool-1', name: 'testTool', @@ -1344,7 +1345,7 @@ describe('runNonInteractive', () => { const toolResponse: Part[] = [{ text: 'file.txt' }]; mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: { callId: 'tool-shell-1', name: 'ShellTool', @@ -1543,7 +1544,7 @@ describe('runNonInteractive', () => { mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -1735,7 +1736,7 @@ describe('runNonInteractive', () => { }; mockSchedulerSchedule.mockResolvedValue([ { - status: 'success', + status: CoreToolCallStatus.Success, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -1818,7 +1819,7 @@ describe('runNonInteractive', () => { // Mock tool execution returning STOP_EXECUTION mockSchedulerSchedule.mockResolvedValue([ { - status: 'error', + status: CoreToolCallStatus.Error, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -1880,7 +1881,7 @@ describe('runNonInteractive', () => { mockSchedulerSchedule.mockResolvedValue([ { - status: 'error', + status: CoreToolCallStatus.Error, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -1944,7 +1945,7 @@ describe('runNonInteractive', () => { mockSchedulerSchedule.mockResolvedValue([ { - status: 'error', + status: CoreToolCallStatus.Error, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, @@ -2187,7 +2188,7 @@ describe('runNonInteractive', () => { // Mock the scheduler to return a cancelled status mockSchedulerSchedule.mockResolvedValue([ { - status: 'cancelled', + status: CoreToolCallStatus.Cancelled, request: toolCallEvent.value, tool: {} as AnyDeclarativeTool, invocation: {} as AnyToolInvocation, diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 475a04e18e..a668734328 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -10,8 +10,8 @@ import { renderWithProviders } from '../test-utils/render.js'; import { Text, useIsScreenReaderEnabled, type DOMElement } from 'ink'; import { App } from './App.js'; import { type UIState } from './contexts/UIStateContext.js'; -import { StreamingState, ToolCallStatus } from './types.js'; -import { makeFakeConfig } from '@google/gemini-cli-core'; +import { StreamingState } from './types.js'; +import { makeFakeConfig, CoreToolCallStatus } from '@google/gemini-cli-core'; vi.mock('ink', async (importOriginal) => { const original = await importOriginal(); @@ -202,7 +202,7 @@ describe('App', () => { callId: 'call-1', name: 'ls', description: 'list directory', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: '', confirmationDetails: { type: 'exec' as const, diff --git a/packages/cli/src/ui/AppContainer.test.tsx b/packages/cli/src/ui/AppContainer.test.tsx index 028584537d..065195a14a 100644 --- a/packages/cli/src/ui/AppContainer.test.tsx +++ b/packages/cli/src/ui/AppContainer.test.tsx @@ -29,6 +29,7 @@ import { type ResumedSessionData, AuthType, type AgentDefinition, + CoreToolCallStatus, } from '@google/gemini-cli-core'; // Mock coreEvents @@ -1412,7 +1413,7 @@ describe('AppContainer State Management', () => { name: 'run_shell_command', args: { command: 'ls > out' }, }, - status: 'executing', + status: CoreToolCallStatus.Executing, } as unknown as TrackedToolCall, ], activePtyId: 'pty-1', diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 1d91d44256..aaad9464f1 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -30,7 +30,6 @@ import { import { ConfigContext } from './contexts/ConfigContext.js'; import { type HistoryItem, - ToolCallStatus, type HistoryItemWithoutId, type HistoryItemToolGroup, AuthState, @@ -79,6 +78,7 @@ import { type ConsentRequestPayload, type AgentsDiscoveredPayload, ChangeAuthRequestedError, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { validateAuthMethod } from '../config/auth.js'; import process from 'node:process'; @@ -161,7 +161,7 @@ function isToolExecuting(pendingHistoryItems: HistoryItemWithoutId[]) { return pendingHistoryItems.some((item) => { if (item && item.type === 'tool_group') { return item.tools.some( - (tool) => ToolCallStatus.Executing === tool.status, + (tool) => CoreToolCallStatus.Executing === tool.status, ); } return false; @@ -174,7 +174,9 @@ function isToolAwaitingConfirmation( return pendingHistoryItems .filter((item): item is HistoryItemToolGroup => item.type === 'tool_group') .some((item) => - item.tools.some((tool) => ToolCallStatus.Confirming === tool.status), + item.tools.some( + (tool) => CoreToolCallStatus.AwaitingApproval === tool.status, + ), ); } diff --git a/packages/cli/src/ui/components/AlternateBufferQuittingDisplay.test.tsx b/packages/cli/src/ui/components/AlternateBufferQuittingDisplay.test.tsx index 4edd41d66c..8b81f1fa42 100644 --- a/packages/cli/src/ui/components/AlternateBufferQuittingDisplay.test.tsx +++ b/packages/cli/src/ui/components/AlternateBufferQuittingDisplay.test.tsx @@ -10,9 +10,9 @@ import { } from '../../test-utils/render.js'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AlternateBufferQuittingDisplay } from './AlternateBufferQuittingDisplay.js'; -import { ToolCallStatus } from '../types.js'; import type { HistoryItem, HistoryItemWithoutId } from '../types.js'; import { Text } from 'ink'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; vi.mock('../utils/terminalSetup.js', () => ({ getTerminalProgram: () => null, @@ -51,7 +51,7 @@ const mockHistory: HistoryItem[] = [ callId: 'call1', name: 'tool1', description: 'Description for tool 1', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: undefined, confirmationDetails: undefined, }, @@ -65,7 +65,7 @@ const mockHistory: HistoryItem[] = [ callId: 'call2', name: 'tool2', description: 'Description for tool 2', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: undefined, confirmationDetails: undefined, }, @@ -81,7 +81,7 @@ const mockPendingHistoryItems: HistoryItemWithoutId[] = [ callId: 'call3', name: 'tool3', description: 'Description for tool 3', - status: ToolCallStatus.Pending, + status: CoreToolCallStatus.Scheduled, resultDisplay: undefined, confirmationDetails: undefined, }, @@ -176,7 +176,7 @@ describe('AlternateBufferQuittingDisplay', () => { callId: 'call4', name: 'confirming_tool', description: 'Confirming tool description', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: undefined, confirmationDetails: { type: 'info', diff --git a/packages/cli/src/ui/components/Composer.test.tsx b/packages/cli/src/ui/components/Composer.test.tsx index 29e077e126..45ccc684b7 100644 --- a/packages/cli/src/ui/components/Composer.test.tsx +++ b/packages/cli/src/ui/components/Composer.test.tsx @@ -24,9 +24,13 @@ vi.mock('../contexts/VimModeContext.js', () => ({ vimMode: 'INSERT', })), })); -import { ApprovalMode, tokenLimit } from '@google/gemini-cli-core'; +import { + ApprovalMode, + tokenLimit, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import type { Config } from '@google/gemini-cli-core'; -import { StreamingState, ToolCallStatus } from '../types.js'; +import { StreamingState } from '../types.js'; import { TransientMessageType } from '../../utils/events.js'; import type { LoadedSettings } from '../../config/settings.js'; import type { SessionMetrics } from '../contexts/SessionContext.js'; @@ -426,7 +430,7 @@ describe('Composer', () => { callId: 'call-1', name: 'edit', description: 'edit file', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: undefined, confirmationDetails: undefined, }, diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx index 1905f7798b..6470661e41 100644 --- a/packages/cli/src/ui/components/Composer.tsx +++ b/packages/cli/src/ui/components/Composer.tsx @@ -6,7 +6,11 @@ import { useState, useEffect, useMemo } from 'react'; import { Box, Text, useIsScreenReaderEnabled } from 'ink'; -import { ApprovalMode, tokenLimit } from '@google/gemini-cli-core'; +import { + ApprovalMode, + tokenLimit, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import { LoadingIndicator } from './LoadingIndicator.js'; import { StatusDisplay } from './StatusDisplay.js'; import { ToastDisplay, shouldShowToast } from './ToastDisplay.js'; @@ -30,11 +34,7 @@ import { useVimMode } from '../contexts/VimModeContext.js'; import { useConfig } from '../contexts/ConfigContext.js'; import { useSettings } from '../contexts/SettingsContext.js'; import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js'; -import { - StreamingState, - type HistoryItemToolGroup, - ToolCallStatus, -} from '../types.js'; +import { StreamingState, type HistoryItemToolGroup } from '../types.js'; import { ConfigInitDisplay } from '../components/ConfigInitDisplay.js'; import { TodoTray } from './messages/Todo.js'; import { getInlineThinkingMode } from '../utils/inlineThinkingMode.js'; @@ -67,7 +67,9 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => { (item): item is HistoryItemToolGroup => item.type === 'tool_group', ) .some((item) => - item.tools.some((tool) => tool.status === ToolCallStatus.Confirming), + item.tools.some( + (tool) => tool.status === CoreToolCallStatus.AwaitingApproval, + ), ), [uiState.pendingHistoryItems], ); diff --git a/packages/cli/src/ui/components/HistoryItemDisplay.test.tsx b/packages/cli/src/ui/components/HistoryItemDisplay.test.tsx index 089140d1ff..8d995cf467 100644 --- a/packages/cli/src/ui/components/HistoryItemDisplay.test.tsx +++ b/packages/cli/src/ui/components/HistoryItemDisplay.test.tsx @@ -6,12 +6,13 @@ import { describe, it, expect, vi } from 'vitest'; import { HistoryItemDisplay } from './HistoryItemDisplay.js'; -import { type HistoryItem, ToolCallStatus } from '../types.js'; +import { type HistoryItem } from '../types.js'; import { MessageType } from '../types.js'; import { SessionStatsProvider } from '../contexts/SessionContext.js'; -import type { - Config, - ToolExecuteConfirmationDetails, +import { + type Config, + type ToolExecuteConfirmationDetails, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { ToolGroupMessage } from './messages/ToolGroupMessage.js'; import { renderWithProviders } from '../../test-utils/render.js'; @@ -203,7 +204,7 @@ describe('', () => { name: 'run_shell_command', description: 'Run a shell command', resultDisplay: 'blank', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'exec', title: 'Run Shell Command', diff --git a/packages/cli/src/ui/components/MainContent.test.tsx b/packages/cli/src/ui/components/MainContent.test.tsx index 4e6d8c7803..8b69a0f187 100644 --- a/packages/cli/src/ui/components/MainContent.test.tsx +++ b/packages/cli/src/ui/components/MainContent.test.tsx @@ -11,13 +11,13 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { Box, Text } from 'ink'; import { act, useState, type JSX } from 'react'; import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js'; -import { ToolCallStatus } from '../types.js'; import { SHELL_COMMAND_NAME } from '../constants.js'; import { UIStateContext, useUIState, type UIState, } from '../contexts/UIStateContext.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; // Mock dependencies vi.mock('../contexts/SettingsContext.js', async () => { @@ -264,7 +264,7 @@ describe('MainContent', () => { { callId: 'call_1', name: SHELL_COMMAND_NAME, - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, description: 'Running a long command...', // 20 lines of output. // Default max is 15, so Line 1-5 will be truncated/scrolled out if not expanded. diff --git a/packages/cli/src/ui/components/ToolConfirmationQueue.test.tsx b/packages/cli/src/ui/components/ToolConfirmationQueue.test.tsx index 8ddbcbce4d..6e0bb87136 100644 --- a/packages/cli/src/ui/components/ToolConfirmationQueue.test.tsx +++ b/packages/cli/src/ui/components/ToolConfirmationQueue.test.tsx @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; import { Box } from 'ink'; import { ToolConfirmationQueue } from './ToolConfirmationQueue.js'; -import { ToolCallStatus, StreamingState } from '../types.js'; +import { StreamingState } from '../types.js'; import { renderWithProviders } from '../../test-utils/render.js'; import { waitFor } from '../../test-utils/async.js'; -import type { Config } from '@google/gemini-cli-core'; +import { type Config, CoreToolCallStatus } from '@google/gemini-cli-core'; import type { ConfirmingToolState } from '../hooks/useConfirmingTool.js'; import { theme } from '../semantic-colors.js'; @@ -63,7 +63,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'ls', description: 'list files', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'exec' as const, title: 'Confirm execution', @@ -105,7 +105,7 @@ describe('ToolConfirmationQueue', () => { tool: { callId: 'call-1', name: 'ls', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: undefined, }, index: 1, @@ -134,7 +134,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'replace', description: 'edit file', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'edit' as const, title: 'Confirm edit', @@ -181,7 +181,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'replace', description: 'edit file', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'edit' as const, title: 'Confirm edit', @@ -230,7 +230,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'replace', description: 'edit file', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'edit' as const, title: 'Confirm edit', @@ -271,7 +271,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'ask_user', description: 'ask user', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'ask_user' as const, questions: [], @@ -307,7 +307,7 @@ describe('ToolConfirmationQueue', () => { callId: 'call-1', name: 'exit_plan_mode', description: 'exit plan mode', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'exit_plan_mode' as const, planPath: '/path/to/plan', diff --git a/packages/cli/src/ui/components/messages/ShellToolMessage.test.tsx b/packages/cli/src/ui/components/messages/ShellToolMessage.test.tsx index bdd2c77809..c698445f8f 100644 --- a/packages/cli/src/ui/components/messages/ShellToolMessage.test.tsx +++ b/packages/cli/src/ui/components/messages/ShellToolMessage.test.tsx @@ -9,12 +9,15 @@ import { ShellToolMessage, type ShellToolMessageProps, } from './ShellToolMessage.js'; -import { StreamingState, ToolCallStatus } from '../../types.js'; -import type { Config } from '@google/gemini-cli-core'; +import { StreamingState } from '../../types.js'; +import { + type Config, + SHELL_TOOL_NAME, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import { renderWithProviders } from '../../../test-utils/render.js'; import { waitFor } from '../../../test-utils/async.js'; import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { SHELL_TOOL_NAME } from '@google/gemini-cli-core'; import { SHELL_COMMAND_NAME, ACTIVE_SHELL_MAX_LINES } from '../../constants.js'; describe('', () => { @@ -23,7 +26,7 @@ describe('', () => { name: SHELL_COMMAND_NAME, description: 'A shell command', resultDisplay: 'Test result', - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, terminalWidth: 80, confirmationDetails: undefined, emphasis: 'medium', @@ -78,10 +81,12 @@ describe('', () => { }); }); it('resets focus when shell finishes', async () => { - let updateStatus: (s: ToolCallStatus) => void = () => {}; + let updateStatus: (s: CoreToolCallStatus) => void = () => {}; const Wrapper = () => { - const [status, setStatus] = React.useState(ToolCallStatus.Executing); + const [status, setStatus] = React.useState( + CoreToolCallStatus.Executing, + ); updateStatus = setStatus; return ( ', () => { // Now update status to Success await act(async () => { - updateStatus(ToolCallStatus.Success); + updateStatus(CoreToolCallStatus.Success); }); // Should call setEmbeddedShellFocused(false) because isThisShellFocused became false @@ -121,23 +126,23 @@ describe('', () => { it.each([ [ 'renders in Executing state', - { status: ToolCallStatus.Executing }, + { status: CoreToolCallStatus.Executing }, undefined, ], [ 'renders in Success state (history mode)', - { status: ToolCallStatus.Success }, + { status: CoreToolCallStatus.Success }, undefined, ], [ 'renders in Error state', - { status: ToolCallStatus.Error, resultDisplay: 'Error output' }, + { status: CoreToolCallStatus.Error, resultDisplay: 'Error output' }, undefined, ], [ 'renders in Alternate Buffer mode while focused', { - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, embeddedShellFocused: true, activeShellPtyId: 1, ptyId: 1, @@ -147,7 +152,7 @@ describe('', () => { [ 'renders in Alternate Buffer mode while unfocused', { - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, embeddedShellFocused: false, activeShellPtyId: 1, ptyId: 1, @@ -196,7 +201,7 @@ describe('', () => { availableTerminalHeight, activeShellPtyId: 1, ptyId: focused ? 1 : 2, - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, embeddedShellFocused: focused, }, { useAlternateBuffer: true }, diff --git a/packages/cli/src/ui/components/messages/ShellToolMessage.tsx b/packages/cli/src/ui/components/messages/ShellToolMessage.tsx index 80e5e0ff8e..cb6f27b317 100644 --- a/packages/cli/src/ui/components/messages/ShellToolMessage.tsx +++ b/packages/cli/src/ui/components/messages/ShellToolMessage.tsx @@ -22,13 +22,12 @@ import { FocusHint, } from './ToolShared.js'; import type { ToolMessageProps } from './ToolMessage.js'; -import { ToolCallStatus } from '../../types.js'; import { ACTIVE_SHELL_MAX_LINES, COMPLETED_SHELL_MAX_LINES, } from '../../constants.js'; import { useAlternateBuffer } from '../../hooks/useAlternateBuffer.js'; -import type { Config } from '@google/gemini-cli-core'; +import { type Config, CoreToolCallStatus } from '@google/gemini-cli-core'; export interface ShellToolMessageProps extends ToolMessageProps { activeShellPtyId?: number | null; @@ -190,15 +189,15 @@ export const ShellToolMessage: React.FC = ({ * This function ensures a finite number of lines is always returned to prevent performance issues. */ function getShellMaxLines( - status: ToolCallStatus, + status: CoreToolCallStatus, isAlternateBuffer: boolean, isThisShellFocused: boolean, availableTerminalHeight: number | undefined, ): number { if ( - status === ToolCallStatus.Success || - status === ToolCallStatus.Error || - status === ToolCallStatus.Canceled + status === CoreToolCallStatus.Success || + status === CoreToolCallStatus.Error || + status === CoreToolCallStatus.Cancelled ) { return COMPLETED_SHELL_MAX_LINES; } diff --git a/packages/cli/src/ui/components/messages/Todo.test.tsx b/packages/cli/src/ui/components/messages/Todo.test.tsx index 6d2906dfcb..64e1f872cb 100644 --- a/packages/cli/src/ui/components/messages/Todo.test.tsx +++ b/packages/cli/src/ui/components/messages/Todo.test.tsx @@ -12,7 +12,7 @@ import type { Todo } from '@google/gemini-cli-core'; import type { UIState } from '../../contexts/UIStateContext.js'; import { UIStateContext } from '../../contexts/UIStateContext.js'; import type { HistoryItem } from '../../types.js'; -import { ToolCallStatus } from '../../types.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; const createTodoHistoryItem = (todos: Todo[]): HistoryItem => ({ @@ -22,7 +22,7 @@ const createTodoHistoryItem = (todos: Todo[]): HistoryItem => { name: 'write_todos', callId: 'tool-1', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: { todos, }, diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx index 3ce4fc54eb..43825c3168 100644 --- a/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.test.tsx @@ -8,9 +8,12 @@ import { renderWithProviders } from '../../../test-utils/render.js'; import { describe, it, expect, vi, afterEach } from 'vitest'; import { ToolGroupMessage } from './ToolGroupMessage.js'; import type { IndividualToolCallDisplay } from '../../types.js'; -import { ToolCallStatus } from '../../types.js'; import { Scrollable } from '../shared/Scrollable.js'; -import { ASK_USER_DISPLAY_NAME, makeFakeConfig } from '@google/gemini-cli-core'; +import { + ASK_USER_DISPLAY_NAME, + makeFakeConfig, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import os from 'node:os'; describe('', () => { @@ -25,7 +28,7 @@ describe('', () => { name: 'test-tool', description: 'A tool for testing', resultDisplay: 'Test result', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, confirmationDetails: undefined, renderOutputAsMarkdown: false, ...overrides, @@ -65,7 +68,7 @@ describe('', () => { const toolCalls = [ createToolCall({ callId: 'confirm-tool', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, confirmationDetails: { type: 'info', title: 'Confirm tool', @@ -90,19 +93,19 @@ describe('', () => { callId: 'tool-1', name: 'successful-tool', description: 'This tool succeeded', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), createToolCall({ callId: 'tool-2', name: 'pending-tool', description: 'This tool is pending', - status: ToolCallStatus.Pending, + status: CoreToolCallStatus.Scheduled, }), createToolCall({ callId: 'tool-3', name: 'error-tool', description: 'This tool failed', - status: ToolCallStatus.Error, + status: CoreToolCallStatus.Error, }), ]; @@ -130,19 +133,19 @@ describe('', () => { callId: 'tool-1', name: 'read_file', description: 'Read a file', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), createToolCall({ callId: 'tool-2', name: 'run_shell_command', description: 'Run command', - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, }), createToolCall({ callId: 'tool-3', name: 'write_file', description: 'Write to file', - status: ToolCallStatus.Pending, + status: CoreToolCallStatus.Scheduled, }), ]; @@ -273,7 +276,7 @@ describe('', () => { callId: 'tool-output-file', name: 'tool-with-file', description: 'Tool that saved output to file', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, outputFile: '/path/to/output.txt', }), ]; @@ -333,7 +336,7 @@ describe('', () => { const toolCalls = [ createToolCall({ name: 'run_shell_command', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), ]; const { lastFrame, unmount } = renderWithProviders( @@ -351,11 +354,11 @@ describe('', () => { it('uses gray border when all tools are successful and no shell commands', () => { const toolCalls = [ - createToolCall({ status: ToolCallStatus.Success }), + createToolCall({ status: CoreToolCallStatus.Success }), createToolCall({ callId: 'tool-2', name: 'another-tool', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), ]; const { lastFrame, unmount } = renderWithProviders( @@ -409,28 +412,28 @@ describe('', () => { describe('Ask User Filtering', () => { it.each([ { - status: ToolCallStatus.Pending, + status: CoreToolCallStatus.Scheduled, resultDisplay: 'test result', shouldHide: true, }, { - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, resultDisplay: 'test result', shouldHide: true, }, { - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: 'test result', shouldHide: true, }, { - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: 'test result', shouldHide: false, }, - { status: ToolCallStatus.Error, resultDisplay: '', shouldHide: true }, + { status: CoreToolCallStatus.Error, resultDisplay: '', shouldHide: true }, { - status: ToolCallStatus.Error, + status: CoreToolCallStatus.Error, resultDisplay: 'error message', shouldHide: false, }, @@ -465,12 +468,12 @@ describe('', () => { createToolCall({ callId: 'other-tool', name: 'other-tool', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), createToolCall({ callId: 'ask-user-pending', name: ASK_USER_DISPLAY_NAME, - status: ToolCallStatus.Pending, + status: CoreToolCallStatus.Scheduled, }), ]; @@ -491,7 +494,7 @@ describe('', () => { createToolCall({ callId: 'ask-user-tool', name: ASK_USER_DISPLAY_NAME, - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, }), ]; diff --git a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx index a553679562..35e6c33b9a 100644 --- a/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx +++ b/packages/cli/src/ui/components/messages/ToolGroupMessage.tsx @@ -8,13 +8,16 @@ import type React from 'react'; import { useMemo } from 'react'; import { Box, Text } from 'ink'; import type { IndividualToolCallDisplay } from '../../types.js'; -import { ToolCallStatus } from '../../types.js'; +import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js'; import { ToolMessage } from './ToolMessage.js'; import { ShellToolMessage } from './ShellToolMessage.js'; import { theme } from '../../semantic-colors.js'; import { useConfig } from '../../contexts/ConfigContext.js'; import { isShellTool, isThisShellFocused } from './ToolShared.js'; -import { shouldHideAskUserTool } from '@google/gemini-cli-core'; +import { + shouldHideAskUserTool, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import { ShowMoreLines } from '../ShowMoreLines.js'; import { useUIState } from '../../contexts/UIStateContext.js'; @@ -45,9 +48,10 @@ export const ToolGroupMessage: React.FC = ({ // Filter out Ask User tools that should be hidden (e.g. in-progress or errors without result) const toolCalls = useMemo( () => - allToolCalls.filter( - (t) => !shouldHideAskUserTool(t.name, t.status, !!t.resultDisplay), - ), + allToolCalls.filter((t) => { + const displayStatus = mapCoreStatusToDisplayStatus(t.status); + return !shouldHideAskUserTool(t.name, displayStatus, !!t.resultDisplay); + }), [allToolCalls], ); @@ -61,11 +65,13 @@ export const ToolGroupMessage: React.FC = ({ // appear in the Global Queue until they are approved and start executing. const visibleToolCalls = useMemo( () => - toolCalls.filter( - (t) => - t.status !== ToolCallStatus.Pending && - t.status !== ToolCallStatus.Confirming, - ), + toolCalls.filter((t) => { + const displayStatus = mapCoreStatusToDisplayStatus(t.status); + return ( + displayStatus !== ToolCallStatus.Pending && + displayStatus !== ToolCallStatus.Confirming + ); + }), [toolCalls], ); @@ -80,7 +86,7 @@ export const ToolGroupMessage: React.FC = ({ ); const hasPending = !visibleToolCalls.every( - (t) => t.status === ToolCallStatus.Success, + (t) => t.status === CoreToolCallStatus.Success, ); const isShellCommand = toolCalls.some((t) => isShellTool(t.name)); diff --git a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx index b5f05eff3c..dd74a70970 100644 --- a/packages/cli/src/ui/components/messages/ToolMessage.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessage.test.tsx @@ -7,9 +7,9 @@ import type React from 'react'; import { ToolMessage, type ToolMessageProps } from './ToolMessage.js'; import { describe, it, expect, vi } from 'vitest'; -import { StreamingState, ToolCallStatus } from '../../types.js'; +import { StreamingState } from '../../types.js'; import { Text } from 'ink'; -import type { AnsiOutput } from '@google/gemini-cli-core'; +import { type AnsiOutput, CoreToolCallStatus } from '@google/gemini-cli-core'; import { renderWithProviders } from '../../../test-utils/render.js'; import { tryParseJSON } from '../../../utils/jsonoutput.js'; @@ -33,7 +33,7 @@ describe('', () => { name: 'test-tool', description: 'A tool for testing', resultDisplay: 'Test result', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, terminalWidth: 80, confirmationDetails: undefined, emphasis: 'medium', @@ -195,7 +195,7 @@ describe('', () => { describe('ToolStatusIndicator rendering', () => { it('shows ✓ for Success status', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -203,7 +203,7 @@ describe('', () => { it('shows o for Pending status', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -211,7 +211,10 @@ describe('', () => { it('shows ? for Confirming status', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -219,7 +222,7 @@ describe('', () => { it('shows - for Canceled status', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -227,7 +230,7 @@ describe('', () => { it('shows x for Error status', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -235,7 +238,7 @@ describe('', () => { it('shows paused spinner for Executing status when streamingState is Idle', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Idle, ); expect(lastFrame()).toMatchSnapshot(); @@ -243,7 +246,7 @@ describe('', () => { it('shows paused spinner for Executing status when streamingState is WaitingForConfirmation', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.WaitingForConfirmation, ); expect(lastFrame()).toMatchSnapshot(); @@ -251,7 +254,7 @@ describe('', () => { it('shows MockRespondingSpinner for Executing status when streamingState is Responding', () => { const { lastFrame } = renderWithContext( - , + , StreamingState.Responding, // Simulate app still responding ); expect(lastFrame()).toMatchSnapshot(); diff --git a/packages/cli/src/ui/components/messages/ToolMessageFocusHint.test.tsx b/packages/cli/src/ui/components/messages/ToolMessageFocusHint.test.tsx index 24ba10350b..ce80085bcf 100644 --- a/packages/cli/src/ui/components/messages/ToolMessageFocusHint.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessageFocusHint.test.tsx @@ -7,14 +7,18 @@ import { act } from 'react'; import { ToolMessage } from './ToolMessage.js'; import { ShellToolMessage } from './ShellToolMessage.js'; -import { ToolCallStatus, StreamingState } from '../../types.js'; +import { StreamingState } from '../../types.js'; import { renderWithProviders } from '../../../test-utils/render.js'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { SHELL_COMMAND_NAME, SHELL_FOCUS_HINT_DELAY_MS, } from '../../constants.js'; -import type { Config, ToolResultDisplay } from '@google/gemini-cli-core'; +import { + type Config, + type ToolResultDisplay, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; vi.mock('../GeminiRespondingSpinner.js', () => ({ GeminiRespondingSpinner: () => null, @@ -34,7 +38,7 @@ describe('Focus Hint', () => { name: SHELL_COMMAND_NAME, description: 'A tool for testing', resultDisplay: undefined as ToolResultDisplay | undefined, - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, terminalWidth: 80, confirmationDetails: undefined, emphasis: 'medium' as const, diff --git a/packages/cli/src/ui/components/messages/ToolMessageRawMarkdown.test.tsx b/packages/cli/src/ui/components/messages/ToolMessageRawMarkdown.test.tsx index b7c14f36bb..ff18de9133 100644 --- a/packages/cli/src/ui/components/messages/ToolMessageRawMarkdown.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolMessageRawMarkdown.test.tsx @@ -4,11 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { describe, it, expect } from 'vitest'; import type { ToolMessageProps } from './ToolMessage.js'; import { ToolMessage } from './ToolMessage.js'; -import { StreamingState, ToolCallStatus } from '../../types.js'; +import { StreamingState } from '../../types.js'; import { StreamingContext } from '../../contexts/StreamingContext.js'; import { renderWithProviders } from '../../../test-utils/render.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; describe(' - Raw Markdown Display Snapshots', () => { const baseProps: ToolMessageProps = { @@ -16,7 +18,7 @@ describe(' - Raw Markdown Display Snapshots', () => { name: 'test-tool', description: 'A tool for testing', resultDisplay: 'Test **bold** and `code` markdown', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, terminalWidth: 80, confirmationDetails: undefined, emphasis: 'medium', diff --git a/packages/cli/src/ui/components/messages/ToolResultDisplayOverflow.test.tsx b/packages/cli/src/ui/components/messages/ToolResultDisplayOverflow.test.tsx index f991171861..bc805d1f1c 100644 --- a/packages/cli/src/ui/components/messages/ToolResultDisplayOverflow.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolResultDisplayOverflow.test.tsx @@ -7,13 +7,10 @@ import { describe, it, expect } from 'vitest'; import { ToolGroupMessage } from './ToolGroupMessage.js'; import { renderWithProviders } from '../../../test-utils/render.js'; -import { - StreamingState, - ToolCallStatus, - type IndividualToolCallDisplay, -} from '../../types.js'; +import { StreamingState, type IndividualToolCallDisplay } from '../../types.js'; import { OverflowProvider } from '../../contexts/OverflowContext.js'; import { waitFor } from '../../../test-utils/async.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; describe('ToolResultDisplay Overflow', () => { it('should display "press ctrl-o" hint when content overflows in ToolGroupMessage', async () => { @@ -29,7 +26,7 @@ describe('ToolResultDisplay Overflow', () => { callId: 'call-1', name: 'test-tool', description: 'a test tool', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay, confirmationDetails: undefined, }, diff --git a/packages/cli/src/ui/components/messages/ToolShared.tsx b/packages/cli/src/ui/components/messages/ToolShared.tsx index eaf3c73bfb..fc0e546cc9 100644 --- a/packages/cli/src/ui/components/messages/ToolShared.tsx +++ b/packages/cli/src/ui/components/messages/ToolShared.tsx @@ -6,7 +6,7 @@ import React, { useState, useEffect } from 'react'; import { Box, Text } from 'ink'; -import { ToolCallStatus } from '../../types.js'; +import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js'; import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js'; import { SHELL_COMMAND_NAME, @@ -20,6 +20,7 @@ import { SHELL_TOOL_NAME, isCompletedAskUserTool, type ToolResultDisplay, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { useInactivityTimer } from '../../hooks/useInactivityTimer.js'; import { formatCommand } from '../../utils/keybindingUtils.js'; @@ -43,12 +44,12 @@ export function isShellTool(name: string): boolean { */ export function isThisShellFocusable( name: string, - status: ToolCallStatus, + status: CoreToolCallStatus, config?: Config, ): boolean { return !!( isShellTool(name) && - status === ToolCallStatus.Executing && + status === CoreToolCallStatus.Executing && config?.getEnableInteractiveShell() ); } @@ -58,14 +59,14 @@ export function isThisShellFocusable( */ export function isThisShellFocused( name: string, - status: ToolCallStatus, + status: CoreToolCallStatus, ptyId?: number, activeShellPtyId?: number | null, embeddedShellFocused?: boolean, ): boolean { return !!( isShellTool(name) && - status === ToolCallStatus.Executing && + status === CoreToolCallStatus.Executing && ptyId === activeShellPtyId && embeddedShellFocused ); @@ -130,14 +131,15 @@ export const FocusHint: React.FC<{ export type TextEmphasis = 'high' | 'medium' | 'low'; type ToolStatusIndicatorProps = { - status: ToolCallStatus; + status: CoreToolCallStatus; name: string; }; export const ToolStatusIndicator: React.FC = ({ - status, + status: coreStatus, name, }) => { + const status = mapCoreStatusToDisplayStatus(coreStatus); const isShell = isShellTool(name); const statusColor = isShell ? theme.ui.symbol : theme.status.warning; @@ -179,16 +181,17 @@ export const ToolStatusIndicator: React.FC = ({ type ToolInfoProps = { name: string; description: string; - status: ToolCallStatus; + status: CoreToolCallStatus; emphasis: TextEmphasis; }; export const ToolInfo: React.FC = ({ name, description, - status, + status: coreStatus, emphasis, }) => { + const status = mapCoreStatusToDisplayStatus(coreStatus); const nameColor = React.useMemo(() => { switch (emphasis) { case 'high': diff --git a/packages/cli/src/ui/components/messages/ToolStickyHeaderRegression.test.tsx b/packages/cli/src/ui/components/messages/ToolStickyHeaderRegression.test.tsx index eaba97a8eb..9ca59ee03b 100644 --- a/packages/cli/src/ui/components/messages/ToolStickyHeaderRegression.test.tsx +++ b/packages/cli/src/ui/components/messages/ToolStickyHeaderRegression.test.tsx @@ -7,7 +7,6 @@ import { renderWithProviders } from '../../../test-utils/render.js'; import { describe, it, expect, vi, afterEach } from 'vitest'; import { ToolGroupMessage } from './ToolGroupMessage.js'; -import { ToolCallStatus } from '../../types.js'; import { ScrollableList, type ScrollableListRef, @@ -16,6 +15,7 @@ import { Box, Text } from 'ink'; import { act, useRef, useEffect } from 'react'; import { waitFor } from '../../../test-utils/async.js'; import { SHELL_COMMAND_NAME } from '../../constants.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; // Mock child components that might be complex vi.mock('../TerminalOutput.js', () => ({ @@ -51,7 +51,7 @@ describe('ToolMessage Sticky Header Regression', () => { { length: 10 }, (_, i) => `${resultPrefix}-${String(i + 1).padStart(2, '0')}`, ).join('\n'), - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, confirmationDetails: undefined, renderOutputAsMarkdown: false, }); @@ -144,7 +144,7 @@ describe('ToolMessage Sticky Header Regression', () => { const toolCalls = [ { ...createToolCall('1', SHELL_COMMAND_NAME, 'shell'), - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }, ]; diff --git a/packages/cli/src/ui/components/messages/__snapshots__/ToolGroupMessage.test.tsx.snap b/packages/cli/src/ui/components/messages/__snapshots__/ToolGroupMessage.test.tsx.snap index 7cda673c07..05c832d46d 100644 --- a/packages/cli/src/ui/components/messages/__snapshots__/ToolGroupMessage.test.tsx.snap +++ b/packages/cli/src/ui/components/messages/__snapshots__/ToolGroupMessage.test.tsx.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[` > Ask User Filtering > filtering logic for status='Error' and hasResult='error message' 1`] = ` +exports[` > Ask User Filtering > filtering logic for status='error' and hasResult='error message' 1`] = ` "╭──────────────────────────────────────────────────────────────────────────╮ │ x Ask User │ │ │ @@ -8,7 +8,7 @@ exports[` > Ask User Filtering > filtering logic for status= ╰──────────────────────────────────────────────────────────────────────────╯" `; -exports[` > Ask User Filtering > filtering logic for status='Success' and hasResult='test result' 1`] = ` +exports[` > Ask User Filtering > filtering logic for status='success' and hasResult='test result' 1`] = ` "╭──────────────────────────────────────────────────────────────────────────╮ │ ✓ Ask User │ │ │ diff --git a/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx b/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx index 5a0cb5143e..69e9e284ed 100644 --- a/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx +++ b/packages/cli/src/ui/contexts/ToolActionsContext.test.tsx @@ -14,8 +14,9 @@ import { ToolConfirmationOutcome, MessageBusType, IdeClient, + CoreToolCallStatus, } from '@google/gemini-cli-core'; -import { ToolCallStatus, type IndividualToolCallDisplay } from '../types.js'; +import { type IndividualToolCallDisplay } from '../types.js'; // Mock IdeClient vi.mock('@google/gemini-cli-core', async (importOriginal) => { @@ -45,7 +46,7 @@ describe('ToolActionsContext', () => { correlationId: 'corr-123', name: 'test-tool', description: 'desc', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: undefined, confirmationDetails: { type: 'info', title: 'title', prompt: 'prompt' }, }, @@ -54,7 +55,7 @@ describe('ToolActionsContext', () => { correlationId: 'corr-edit', name: 'edit-tool', description: 'desc', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: undefined, confirmationDetails: { type: 'edit', diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts index 4a6af6f9a1..9e3a056edd 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.test.ts @@ -17,10 +17,10 @@ import { COMMON_IGNORE_PATTERNS, GEMINI_IGNORE_FILE_NAME, // DEFAULT_FILE_EXCLUDES, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import * as core from '@google/gemini-cli-core'; import * as os from 'node:os'; -import { ToolCallStatus } from '../types.js'; import type { UseHistoryManagerReturn } from './useHistoryManager.js'; import * as fsPromises from 'node:fs/promises'; import * as path from 'node:path'; @@ -212,7 +212,9 @@ describe('handleAtCommand', () => { expect(mockAddItem).toHaveBeenCalledWith( expect.objectContaining({ type: 'tool_group', - tools: [expect.objectContaining({ status: ToolCallStatus.Success })], + tools: [ + expect.objectContaining({ status: CoreToolCallStatus.Success }), + ], }), 125, ); @@ -314,7 +316,9 @@ describe('handleAtCommand', () => { expect(mockAddItem).toHaveBeenCalledWith( expect.objectContaining({ type: 'tool_group', - tools: [expect.objectContaining({ status: ToolCallStatus.Success })], + tools: [ + expect.objectContaining({ status: CoreToolCallStatus.Success }), + ], }), 125, ); @@ -1431,7 +1435,7 @@ describe('handleAtCommand', () => { expect(mockAddItem).toHaveBeenCalledWith( expect.objectContaining({ type: 'tool_group', - tools: [expect.objectContaining({ status: ToolCallStatus.Error })], + tools: [expect.objectContaining({ status: CoreToolCallStatus.Error })], }), 134, ); diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index e30f9abbc9..ebf7707573 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -18,10 +18,10 @@ import { ReadManyFilesTool, REFERENCE_CONTENT_START, REFERENCE_CONTENT_END, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { Buffer } from 'node:buffer'; import type { HistoryItem, IndividualToolCallDisplay } from '../types.js'; -import { ToolCallStatus } from '../types.js'; import type { UseHistoryManagerReturn } from './useHistoryManager.js'; const REF_CONTENT_HEADER = `\n${REFERENCE_CONTENT_START}`; @@ -409,7 +409,7 @@ async function readMcpResources( callId: `mcp-resource-${resource.serverName}-${resource.uri}`, name: `resources/read (${resource.serverName})`, description: resource.uri, - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: `Successfully read resource ${resource.uri}`, confirmationDetails: undefined, } as IndividualToolCallDisplay, @@ -423,7 +423,7 @@ async function readMcpResources( callId: `mcp-resource-${resource.serverName}-${resource.uri}`, name: `resources/read (${resource.serverName})`, description: resource.uri, - status: ToolCallStatus.Error, + status: CoreToolCallStatus.Error, resultDisplay: `Error reading resource ${resource.uri}: ${getErrorMessage(error)}`, confirmationDetails: undefined, } as IndividualToolCallDisplay, @@ -447,7 +447,9 @@ async function readMcpResources( } if (hasError) { - const firstError = displays.find((d) => d.status === ToolCallStatus.Error); + const firstError = displays.find( + (d) => d.status === CoreToolCallStatus.Error, + ); return { parts: [], displays, @@ -500,7 +502,7 @@ async function readLocalFiles( callId: `client-read-${userMessageTimestamp}`, name: readManyFilesTool.displayName, description: invocation.getDescription(), - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: result.returnDisplay || `Successfully read: ${fileLabelsForDisplay.join(', ')}`, @@ -559,7 +561,7 @@ async function readLocalFiles( description: invocation?.getDescription() ?? 'Error attempting to execute tool to read files', - status: ToolCallStatus.Error, + status: CoreToolCallStatus.Error, resultDisplay: `Error reading files (${fileLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`, confirmationDetails: undefined, }; diff --git a/packages/cli/src/ui/hooks/shellCommandProcessor.test.tsx b/packages/cli/src/ui/hooks/shellCommandProcessor.test.tsx index d262651590..377cac9b7c 100644 --- a/packages/cli/src/ui/hooks/shellCommandProcessor.test.tsx +++ b/packages/cli/src/ui/hooks/shellCommandProcessor.test.tsx @@ -75,12 +75,12 @@ import { type GeminiClient, type ShellExecutionResult, type ShellOutputEvent, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; import * as crypto from 'node:crypto'; -import { ToolCallStatus } from '../types.js'; describe('useShellCommandProcessor', () => { let addItemToHistoryMock: Mock; @@ -201,7 +201,7 @@ describe('useShellCommandProcessor', () => { tools: [ expect.objectContaining({ name: 'Shell Command', - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, }), ], }); @@ -240,7 +240,7 @@ describe('useShellCommandProcessor', () => { expect.objectContaining({ tools: [ expect.objectContaining({ - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: 'ok', }), ], @@ -269,7 +269,7 @@ describe('useShellCommandProcessor', () => { await act(async () => await execPromise); const finalHistoryItem = addItemToHistoryMock.mock.calls[1][0]; - expect(finalHistoryItem.tools[0].status).toBe(ToolCallStatus.Error); + expect(finalHistoryItem.tools[0].status).toBe(CoreToolCallStatus.Error); expect(finalHistoryItem.tools[0].resultDisplay).toContain( 'Command exited with code 127', ); @@ -483,7 +483,7 @@ describe('useShellCommandProcessor', () => { await act(async () => await execPromise); const finalHistoryItem = addItemToHistoryMock.mock.calls[1][0]; - expect(finalHistoryItem.tools[0].status).toBe(ToolCallStatus.Success); + expect(finalHistoryItem.tools[0].status).toBe(CoreToolCallStatus.Success); expect(finalHistoryItem.tools[0].resultDisplay).toBe( '[Command produced binary output, which is not shown.]', ); diff --git a/packages/cli/src/ui/hooks/shellCommandProcessor.ts b/packages/cli/src/ui/hooks/shellCommandProcessor.ts index 860bece5d8..3c85d3b6a4 100644 --- a/packages/cli/src/ui/hooks/shellCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/shellCommandProcessor.ts @@ -8,10 +8,13 @@ import type { HistoryItemWithoutId, IndividualToolCallDisplay, } from '../types.js'; -import { ToolCallStatus } from '../types.js'; import { useCallback, useReducer, useRef, useEffect } from 'react'; import type { AnsiOutput, Config, GeminiClient } from '@google/gemini-cli-core'; -import { isBinary, ShellExecutionService } from '@google/gemini-cli-core'; +import { + isBinary, + ShellExecutionService, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import { type PartListUnion } from '@google/genai'; import type { UseHistoryManagerReturn } from './useHistoryManager.js'; import { SHELL_COMMAND_NAME } from '../constants.js'; @@ -301,7 +304,7 @@ export const useShellCommandProcessor = ( callId, name: SHELL_COMMAND_NAME, description: rawQuery, - status: ToolCallStatus.Executing, + status: CoreToolCallStatus.Executing, resultDisplay: '', confirmationDetails: undefined, }; @@ -447,22 +450,22 @@ export const useShellCommandProcessor = ( } let finalOutput = mainContent; - let finalStatus = ToolCallStatus.Success; + let finalStatus = CoreToolCallStatus.Success; if (result.error) { - finalStatus = ToolCallStatus.Error; + finalStatus = CoreToolCallStatus.Error; finalOutput = `${result.error.message}\n${finalOutput}`; } else if (result.aborted) { - finalStatus = ToolCallStatus.Canceled; + finalStatus = CoreToolCallStatus.Cancelled; finalOutput = `Command was cancelled.\n${finalOutput}`; } else if (result.backgrounded) { - finalStatus = ToolCallStatus.Success; + finalStatus = CoreToolCallStatus.Success; finalOutput = `Command moved to background (PID: ${result.pid}). Output hidden. Press Ctrl+B to view.`; } else if (result.signal) { - finalStatus = ToolCallStatus.Error; + finalStatus = CoreToolCallStatus.Error; finalOutput = `Command terminated by signal: ${result.signal}.\n${finalOutput}`; } else if (result.exitCode !== 0) { - finalStatus = ToolCallStatus.Error; + finalStatus = CoreToolCallStatus.Error; finalOutput = `Command exited with code ${result.exitCode}.\n${finalOutput}`; } @@ -480,7 +483,7 @@ export const useShellCommandProcessor = ( resultDisplay: finalOutput, }; - if (finalStatus !== ToolCallStatus.Canceled) { + if (finalStatus !== CoreToolCallStatus.Cancelled) { addItemToHistory( { type: 'tool_group', diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx index 11f47e12d3..7fc947ba48 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.tsx @@ -1003,7 +1003,7 @@ describe('useSlashCommandProcessor', () => { command: '/fail', expectedLog: { command: 'fail', - status: 'error', + status: SlashCommandStatus.ERROR, subcommand: undefined, }, desc: 'failure event for failed command', diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index 2c6c463e42..4f9bb8def0 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -34,6 +34,7 @@ import { addMCPStatusChangeListener, removeMCPStatusChangeListener, MCPDiscoveryState, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { useSessionStats } from '../contexts/SessionContext.js'; import type { @@ -44,7 +45,7 @@ import type { ConfirmationRequest, IndividualToolCallDisplay, } from '../types.js'; -import { MessageType, ToolCallStatus } from '../types.js'; +import { MessageType } from '../types.js'; import type { LoadedSettings } from '../../config/settings.js'; import { type CommandContext, type SlashCommand } from '../commands/types.js'; import { CommandService } from '../../services/CommandService.js'; @@ -554,7 +555,7 @@ export const useSlashCommandProcessor = ( callId, name: 'Expansion', description: 'Command expansion needs shell access', - status: ToolCallStatus.Confirming, + status: CoreToolCallStatus.AwaitingApproval, resultDisplay: undefined, confirmationDetails, }; diff --git a/packages/cli/src/ui/hooks/toolMapping.test.ts b/packages/cli/src/ui/hooks/toolMapping.test.ts index ad7b147b20..241b5d94f0 100644 --- a/packages/cli/src/ui/hooks/toolMapping.test.ts +++ b/packages/cli/src/ui/hooks/toolMapping.test.ts @@ -5,7 +5,7 @@ */ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { mapCoreStatusToDisplayStatus, mapToDisplay } from './toolMapping.js'; +import { mapToDisplay } from './toolMapping.js'; import { type AnyDeclarativeTool, type AnyToolInvocation, @@ -20,7 +20,7 @@ import { type CancelledToolCall, CoreToolCallStatus, } from '@google/gemini-cli-core'; -import { ToolCallStatus } from '../types.js'; +import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../types.js'; describe('toolMapping', () => { beforeEach(() => { @@ -131,7 +131,7 @@ describe('toolMapping', () => { name: 'Test Tool', description: 'Calling test_tool with args...', renderOutputAsMarkdown: true, - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: 'Success output', outputFile: '/tmp/output.txt', }), @@ -151,7 +151,7 @@ describe('toolMapping', () => { const result = mapToDisplay(toolCall); const displayTool = result.tools[0]; - expect(displayTool.status).toBe(ToolCallStatus.Executing); + expect(displayTool.status).toBe(CoreToolCallStatus.Executing); expect(displayTool.resultDisplay).toBe('Loading...'); expect(displayTool.ptyId).toBe(12345); }); @@ -178,7 +178,7 @@ describe('toolMapping', () => { const result = mapToDisplay(toolCall); const displayTool = result.tools[0]; - expect(displayTool.status).toBe(ToolCallStatus.Confirming); + expect(displayTool.status).toBe(CoreToolCallStatus.AwaitingApproval); expect(displayTool.confirmationDetails).toEqual(confirmationDetails); }); @@ -221,7 +221,7 @@ describe('toolMapping', () => { const result = mapToDisplay(toolCall); const displayTool = result.tools[0]; - expect(displayTool.status).toBe(ToolCallStatus.Error); + expect(displayTool.status).toBe(CoreToolCallStatus.Error); expect(displayTool.name).toBe('test_tool'); // falls back to request.name expect(displayTool.description).toBe('{"arg1":"val1"}'); // falls back to stringified args expect(displayTool.resultDisplay).toBe('Tool not found'); @@ -243,7 +243,7 @@ describe('toolMapping', () => { const result = mapToDisplay(toolCall); const displayTool = result.tools[0]; - expect(displayTool.status).toBe(ToolCallStatus.Canceled); + expect(displayTool.status).toBe(CoreToolCallStatus.Cancelled); expect(displayTool.resultDisplay).toBe('User cancelled'); }); @@ -273,7 +273,7 @@ describe('toolMapping', () => { const result = mapToDisplay(toolCall); expect(result.tools[0].resultDisplay).toBeUndefined(); - expect(result.tools[0].status).toBe(ToolCallStatus.Pending); + expect(result.tools[0].status).toBe(CoreToolCallStatus.Scheduled); }); }); }); diff --git a/packages/cli/src/ui/hooks/toolMapping.ts b/packages/cli/src/ui/hooks/toolMapping.ts index 9de82754eb..46b374070d 100644 --- a/packages/cli/src/ui/hooks/toolMapping.ts +++ b/packages/cli/src/ui/hooks/toolMapping.ts @@ -6,42 +6,16 @@ import { type ToolCall, - type Status as CoreStatus, type SerializableConfirmationDetails, type ToolResultDisplay, debugLogger, CoreToolCallStatus, - checkExhaustive, } from '@google/gemini-cli-core'; import { - ToolCallStatus, type HistoryItemToolGroup, type IndividualToolCallDisplay, } from '../types.js'; -export function mapCoreStatusToDisplayStatus( - coreStatus: CoreStatus, -): ToolCallStatus { - switch (coreStatus) { - case CoreToolCallStatus.Validating: - return ToolCallStatus.Pending; - case CoreToolCallStatus.AwaitingApproval: - return ToolCallStatus.Confirming; - case CoreToolCallStatus.Executing: - return ToolCallStatus.Executing; - case CoreToolCallStatus.Success: - return ToolCallStatus.Success; - case CoreToolCallStatus.Cancelled: - return ToolCallStatus.Canceled; - case CoreToolCallStatus.Error: - return ToolCallStatus.Error; - case CoreToolCallStatus.Scheduled: - return ToolCallStatus.Pending; - default: - return checkExhaustive(coreStatus); - } -} - /** * Transforms `ToolCall` objects into `HistoryItemToolGroup` objects for UI * display. This is a pure projection layer and does not track interaction @@ -115,7 +89,7 @@ export function mapToDisplay( return { ...baseDisplayProperties, - status: mapCoreStatusToDisplayStatus(call.status), + status: call.status, resultDisplay, confirmationDetails, outputFile, diff --git a/packages/cli/src/ui/hooks/useConfirmingTool.ts b/packages/cli/src/ui/hooks/useConfirmingTool.ts index 115473ae9f..a7cd2939f1 100644 --- a/packages/cli/src/ui/hooks/useConfirmingTool.ts +++ b/packages/cli/src/ui/hooks/useConfirmingTool.ts @@ -7,10 +7,10 @@ import { useMemo } from 'react'; import { useUIState } from '../contexts/UIStateContext.js'; import { - ToolCallStatus, type IndividualToolCallDisplay, type HistoryItemToolGroup, } from '../types.js'; +import { CoreToolCallStatus } from '@google/gemini-cli-core'; export interface ConfirmingToolState { tool: IndividualToolCallDisplay; @@ -37,7 +37,7 @@ export function useConfirmingTool(): ConfirmingToolState | null { // 2. Filter for those requiring confirmation const confirmingTools = allPendingTools.filter( - (t) => t.status === ToolCallStatus.Confirming, + (t) => t.status === CoreToolCallStatus.AwaitingApproval, ); if (confirmingTools.length === 0) { diff --git a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx index 5220b80fad..8b5a312d37 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx @@ -43,7 +43,7 @@ import { import type { Part, PartListUnion } from '@google/genai'; import type { UseHistoryManagerReturn } from './useHistoryManager.js'; import type { SlashCommandProcessorResult } from '../types.js'; -import { MessageType, StreamingState, ToolCallStatus } from '../types.js'; +import { MessageType, StreamingState } from '../types.js'; import type { LoadedSettings } from '../../config/settings.js'; // --- MOCKS --- @@ -2380,7 +2380,7 @@ describe('useGeminiStream', () => { callId: 'client-read-123', name: 'read_file', description: toolExecutionMessage, - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, resultDisplay: toolExecutionMessage, confirmationDetails: undefined, }, @@ -2434,7 +2434,7 @@ describe('useGeminiStream', () => { tools: expect.arrayContaining([ expect.objectContaining({ name: 'read_file', - status: ToolCallStatus.Success, + status: CoreToolCallStatus.Success, }), ]), }), diff --git a/packages/cli/src/ui/hooks/useGeminiStream.ts b/packages/cli/src/ui/hooks/useGeminiStream.ts index 13f6d8cf70..f23574858f 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.ts +++ b/packages/cli/src/ui/hooks/useGeminiStream.ts @@ -33,6 +33,7 @@ import { ValidationRequiredError, coreEvents, CoreEvent, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import type { Config, @@ -58,7 +59,7 @@ import type { SlashCommandProcessorResult, HistoryItemModel, } from '../types.js'; -import { StreamingState, MessageType, ToolCallStatus } from '../types.js'; +import { StreamingState, MessageType } from '../types.js'; import { isAtCommand, isSlashCommand } from '../utils/commandUtils.js'; import { useShellCommandProcessor } from './shellCommandProcessor.js'; import { handleAtCommand } from './atCommandProcessor.js'; @@ -126,16 +127,18 @@ function calculateStreamingState( isResponding: boolean, toolCalls: TrackedToolCall[], ): StreamingState { - if (toolCalls.some((tc) => tc.status === 'awaiting_approval')) { + if ( + toolCalls.some((tc) => tc.status === CoreToolCallStatus.AwaitingApproval) + ) { return StreamingState.WaitingForConfirmation; } const isAnyToolActive = toolCalls.some((tc) => { // These statuses indicate active processing if ( - tc.status === 'executing' || - tc.status === 'scheduled' || - tc.status === 'validating' + tc.status === CoreToolCallStatus.Executing || + tc.status === CoreToolCallStatus.Scheduled || + tc.status === CoreToolCallStatus.Validating ) { return true; } @@ -143,9 +146,9 @@ function calculateStreamingState( // Terminal statuses (success, error, cancelled) still count as "Responding" // if the result hasn't been submitted back to Gemini yet. if ( - tc.status === 'success' || - tc.status === 'error' || - tc.status === 'cancelled' + tc.status === CoreToolCallStatus.Success || + tc.status === CoreToolCallStatus.Error || + tc.status === CoreToolCallStatus.Cancelled ) { return !(tc as TrackedCompletedToolCall | TrackedCancelledToolCall) .responseSubmittedToGemini; @@ -562,7 +565,7 @@ export const useGeminiStream = ( if (tool.name === SHELL_COMMAND_NAME) { return { ...tool, - status: ToolCallStatus.Canceled, + status: CoreToolCallStatus.Cancelled, resultDisplay: tool.resultDisplay, }; } @@ -830,12 +833,14 @@ export const useGeminiStream = ( if (pendingHistoryItemRef.current.type === 'tool_group') { const updatedTools = pendingHistoryItemRef.current.tools.map( (tool) => - tool.status === ToolCallStatus.Pending || - tool.status === ToolCallStatus.Confirming || - tool.status === ToolCallStatus.Executing - ? { ...tool, status: ToolCallStatus.Canceled } + tool.status === CoreToolCallStatus.Validating || + tool.status === CoreToolCallStatus.Scheduled || + tool.status === CoreToolCallStatus.AwaitingApproval || + tool.status === CoreToolCallStatus.Executing + ? { ...tool, status: CoreToolCallStatus.Cancelled } : tool, ); + const pendingItem: HistoryItemToolGroup = { ...pendingHistoryItemRef.current, tools: updatedTools, @@ -1530,7 +1535,7 @@ export const useGeminiStream = ( // If all the tools were cancelled, don't submit a response to Gemini. const allToolsCancelled = geminiTools.every( - (tc) => tc.status === 'cancelled', + (tc) => tc.status === CoreToolCallStatus.Cancelled, ); if (allToolsCancelled) { @@ -1618,7 +1623,7 @@ export const useGeminiStream = ( const restorableToolCalls = toolCalls.filter( (toolCall) => EDIT_TOOL_NAMES.has(toolCall.request.name) && - toolCall.status === 'awaiting_approval', + toolCall.status === CoreToolCallStatus.AwaitingApproval, ); if (restorableToolCalls.length > 0) { diff --git a/packages/cli/src/ui/hooks/useSessionBrowser.test.ts b/packages/cli/src/ui/hooks/useSessionBrowser.test.ts index 7e53d3c437..d6326bdfef 100644 --- a/packages/cli/src/ui/hooks/useSessionBrowser.test.ts +++ b/packages/cli/src/ui/hooks/useSessionBrowser.test.ts @@ -14,10 +14,11 @@ import { import * as fs from 'node:fs/promises'; import path from 'node:path'; import { getSessionFiles, type SessionInfo } from '../../utils/sessionUtils.js'; -import type { - Config, - ConversationRecord, - MessageRecord, +import { + type Config, + type ConversationRecord, + type MessageRecord, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { coreEvents } from '@google/gemini-cli-core'; @@ -238,7 +239,7 @@ describe('convertSessionToHistoryFormats', () => { id: 'call_1', name: 'get_time', args: {}, - status: 'success', + status: CoreToolCallStatus.Success, result: '12:00', }, ], @@ -258,7 +259,7 @@ describe('convertSessionToHistoryFormats', () => { expect.objectContaining({ callId: 'call_1', name: 'get_time', - status: 'Success', + status: CoreToolCallStatus.Success, }), ], }); diff --git a/packages/cli/src/ui/hooks/useToolScheduler.test.ts b/packages/cli/src/ui/hooks/useToolScheduler.test.ts index 8ebf439630..ddf43944f6 100644 --- a/packages/cli/src/ui/hooks/useToolScheduler.test.ts +++ b/packages/cli/src/ui/hooks/useToolScheduler.test.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { act } from 'react'; import { renderHook } from '../../test-utils/render.js'; import { useToolScheduler } from './useToolScheduler.js'; @@ -18,6 +18,7 @@ import { type AnyDeclarativeTool, type AnyToolInvocation, ROOT_SCHEDULER_ID, + CoreToolCallStatus, } from '@google/gemini-cli-core'; import { createMockMessageBus } from '@google/gemini-cli-core/src/test-utils/mock-message-bus.js'; @@ -98,7 +99,7 @@ describe('useToolScheduler', () => { ); const mockToolCall = { - status: 'executing' as const, + status: CoreToolCallStatus.Executing as const, request: { callId: 'call-1', name: 'test_tool', @@ -124,7 +125,7 @@ describe('useToolScheduler', () => { // Expect Core Object structure, not Display Object expect(toolCalls[0]).toMatchObject({ request: { callId: 'call-1', name: 'test_tool' }, - status: 'executing', // Core status + status: CoreToolCallStatus.Executing, liveOutput: 'Loading...', responseSubmittedToGemini: false, }); @@ -140,7 +141,7 @@ describe('useToolScheduler', () => { ); const mockToolCall = { - status: 'success' as const, + status: CoreToolCallStatus.Success as const, request: { callId: 'call-1', name: 'test', @@ -206,7 +207,7 @@ describe('useToolScheduler', () => { type: MessageBusType.TOOL_CALLS_UPDATE, toolCalls: [ { - status: 'executing' as const, + status: CoreToolCallStatus.Executing as const, request: { callId: 'call-1', name: 'test', @@ -252,7 +253,7 @@ describe('useToolScheduler', () => { const onComplete = vi.fn().mockResolvedValue(undefined); const completedToolCall = { - status: 'success' as const, + status: CoreToolCallStatus.Success as const, request: { callId: 'call-1', name: 'test', @@ -316,7 +317,7 @@ describe('useToolScheduler', () => { ); const callRoot = { - status: 'success' as const, + status: CoreToolCallStatus.Success as const, request: { callId: 'call-root', name: 'test', @@ -390,7 +391,7 @@ describe('useToolScheduler', () => { act(() => { void mockMessageBus.publish({ type: MessageBusType.TOOL_CALLS_UPDATE, - toolCalls: [{ ...callRoot, status: 'executing' }], + toolCalls: [{ ...callRoot, status: CoreToolCallStatus.Executing }], schedulerId: ROOT_SCHEDULER_ID, } as ToolCallsUpdateMessage); }); @@ -399,7 +400,7 @@ describe('useToolScheduler', () => { expect(toolCalls).toHaveLength(2); expect( toolCalls.find((t) => t.request.callId === 'call-root')?.status, - ).toBe('executing'); + ).toBe(CoreToolCallStatus.Executing); expect( toolCalls.find((t) => t.request.callId === 'call-sub')?.schedulerId, ).toBe('subagent-1'); diff --git a/packages/cli/src/ui/hooks/useTurnActivityMonitor.test.ts b/packages/cli/src/ui/hooks/useTurnActivityMonitor.test.ts index f77ab7504d..f3791d1b32 100644 --- a/packages/cli/src/ui/hooks/useTurnActivityMonitor.test.ts +++ b/packages/cli/src/ui/hooks/useTurnActivityMonitor.test.ts @@ -8,7 +8,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { renderHook } from '../../test-utils/render.js'; import { useTurnActivityMonitor } from './useTurnActivityMonitor.js'; import { StreamingState } from '../types.js'; -import { hasRedirection } from '@google/gemini-cli-core'; +import { hasRedirection, CoreToolCallStatus } from '@google/gemini-cli-core'; import { type TrackedToolCall } from './useToolScheduler.js'; vi.mock('@google/gemini-cli-core', async (importOriginal) => { @@ -93,7 +93,7 @@ describe('useTurnActivityMonitor', () => { name: 'run_shell_command', args: { command: 'ls -la' }, }, - status: 'executing', + status: CoreToolCallStatus.Executing, } as unknown as TrackedToolCall, ], }); @@ -108,7 +108,7 @@ describe('useTurnActivityMonitor', () => { name: 'run_shell_command', args: { command: 'ls > tool_out.txt' }, }, - status: 'executing', + status: CoreToolCallStatus.Executing, } as unknown as TrackedToolCall, ], }); diff --git a/packages/cli/src/ui/types.ts b/packages/cli/src/ui/types.ts index ca9a992f80..562fcbcb4c 100644 --- a/packages/cli/src/ui/types.ts +++ b/packages/cli/src/ui/types.ts @@ -4,16 +4,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import type { - CompressionStatus, - GeminiCLIExtension, - MCPServerConfig, - ThoughtSummary, - SerializableConfirmationDetails, - ToolResultDisplay, - RetrieveUserQuotaResponse, - SkillDefinition, - AgentDefinition, +import { + type CompressionStatus, + type GeminiCLIExtension, + type MCPServerConfig, + type ThoughtSummary, + type SerializableConfirmationDetails, + type ToolResultDisplay, + type RetrieveUserQuotaResponse, + type SkillDefinition, + type AgentDefinition, + CoreToolCallStatus, + checkExhaustive, } from '@google/gemini-cli-core'; import type { PartListUnion } from '@google/genai'; import { type ReactNode } from 'react'; @@ -56,9 +58,35 @@ export enum ToolCallStatus { Error = 'Error', } +/** + * Maps core tool call status to a simplified UI status. + */ +export function mapCoreStatusToDisplayStatus( + coreStatus: CoreToolCallStatus, +): ToolCallStatus { + switch (coreStatus) { + case CoreToolCallStatus.Validating: + return ToolCallStatus.Pending; + case CoreToolCallStatus.AwaitingApproval: + return ToolCallStatus.Confirming; + case CoreToolCallStatus.Executing: + return ToolCallStatus.Executing; + case CoreToolCallStatus.Success: + return ToolCallStatus.Success; + case CoreToolCallStatus.Cancelled: + return ToolCallStatus.Canceled; + case CoreToolCallStatus.Error: + return ToolCallStatus.Error; + case CoreToolCallStatus.Scheduled: + return ToolCallStatus.Pending; + default: + return checkExhaustive(coreStatus); + } +} + export interface ToolCallEvent { type: 'tool_call'; - status: ToolCallStatus; + status: CoreToolCallStatus; callId: string; name: string; args: Record; @@ -72,7 +100,7 @@ export interface IndividualToolCallDisplay { name: string; description: string; resultDisplay: ToolResultDisplay | undefined; - status: ToolCallStatus; + status: CoreToolCallStatus; confirmationDetails: SerializableConfirmationDetails | undefined; renderOutputAsMarkdown?: boolean; ptyId?: number; diff --git a/packages/cli/src/utils/sessionUtils.ts b/packages/cli/src/utils/sessionUtils.ts index 6a132f42cc..bc2cfbc0e2 100644 --- a/packages/cli/src/utils/sessionUtils.ts +++ b/packages/cli/src/utils/sessionUtils.ts @@ -8,6 +8,7 @@ import { checkExhaustive, partListUnionToString, SESSION_FILE_PREFIX, + CoreToolCallStatus, type Config, type ConversationRecord, type MessageRecord, @@ -16,11 +17,7 @@ import * as fs from 'node:fs/promises'; import path from 'node:path'; import { stripUnsafeCharacters } from '../ui/utils/textUtils.js'; import type { Part } from '@google/genai'; -import { - MessageType, - ToolCallStatus, - type HistoryItemWithoutId, -} from '../ui/types.js'; +import { MessageType, type HistoryItemWithoutId } from '../ui/types.js'; /** * Constant for the resume "latest" identifier. @@ -585,8 +582,8 @@ export function convertSessionToHistoryFormats( renderOutputAsMarkdown: tool.renderOutputAsMarkdown ?? true, status: tool.status === 'success' - ? ToolCallStatus.Success - : ToolCallStatus.Error, + ? CoreToolCallStatus.Success + : CoreToolCallStatus.Error, resultDisplay: tool.resultDisplay, confirmationDetails: undefined, })), diff --git a/packages/cli/src/zed-integration/acpResume.test.ts b/packages/cli/src/zed-integration/acpResume.test.ts index 667a846896..d6e12aec25 100644 --- a/packages/cli/src/zed-integration/acpResume.test.ts +++ b/packages/cli/src/zed-integration/acpResume.test.ts @@ -15,7 +15,11 @@ import { } from 'vitest'; import { GeminiAgent } from './zedIntegration.js'; import * as acp from '@agentclientprotocol/sdk'; -import { AuthType, type Config } from '@google/gemini-cli-core'; +import { + AuthType, + type Config, + CoreToolCallStatus, +} from '@google/gemini-cli-core'; import { loadCliConfig, type CliArgs } from '../config/config.js'; import { SessionSelector, @@ -98,7 +102,7 @@ describe('GeminiAgent Session Resume', () => { id: 'call-1', name: 'test_tool', displayName: 'Test Tool', - status: 'success', + status: CoreToolCallStatus.Success, resultDisplay: 'Tool output', }, ], @@ -111,7 +115,7 @@ describe('GeminiAgent Session Resume', () => { id: 'call-2', name: 'write_file', displayName: 'Write File', - status: 'error', + status: CoreToolCallStatus.Error, resultDisplay: 'Permission denied', }, ],