refactor: use CoreToolCallStatus in the the history data model (#19033)

This commit is contained in:
Jerop Kipruto
2026-02-13 17:20:14 -05:00
committed by GitHub
parent e7e4c68c5c
commit f87468c644
40 changed files with 322 additions and 268 deletions

View File

@@ -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',

View File

@@ -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,
},

View File

@@ -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],
);

View File

@@ -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('<HistoryItemDisplay />', () => {
name: 'run_shell_command',
description: 'Run a shell command',
resultDisplay: 'blank',
status: ToolCallStatus.Confirming,
status: CoreToolCallStatus.AwaitingApproval,
confirmationDetails: {
type: 'exec',
title: 'Run Shell Command',

View File

@@ -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.

View File

@@ -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',

View File

@@ -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('<ShellToolMessage />', () => {
@@ -23,7 +26,7 @@ describe('<ShellToolMessage />', () => {
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('<ShellToolMessage />', () => {
});
});
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 (
<ShellToolMessage
@@ -106,7 +111,7 @@ describe('<ShellToolMessage />', () => {
// 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('<ShellToolMessage />', () => {
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('<ShellToolMessage />', () => {
[
'renders in Alternate Buffer mode while unfocused',
{
status: ToolCallStatus.Executing,
status: CoreToolCallStatus.Executing,
embeddedShellFocused: false,
activeShellPtyId: 1,
ptyId: 1,
@@ -196,7 +201,7 @@ describe('<ShellToolMessage />', () => {
availableTerminalHeight,
activeShellPtyId: 1,
ptyId: focused ? 1 : 2,
status: ToolCallStatus.Executing,
status: CoreToolCallStatus.Executing,
embeddedShellFocused: focused,
},
{ useAlternateBuffer: true },

View File

@@ -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<ShellToolMessageProps> = ({
* 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;
}

View File

@@ -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,
},

View File

@@ -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('<ToolGroupMessage />', () => {
@@ -25,7 +28,7 @@ describe('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
const toolCalls = [
createToolCall({
callId: 'confirm-tool',
status: ToolCallStatus.Confirming,
status: CoreToolCallStatus.AwaitingApproval,
confirmationDetails: {
type: 'info',
title: 'Confirm tool',
@@ -90,19 +93,19 @@ describe('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
const toolCalls = [
createToolCall({
name: 'run_shell_command',
status: ToolCallStatus.Success,
status: CoreToolCallStatus.Success,
}),
];
const { lastFrame, unmount } = renderWithProviders(
@@ -351,11 +354,11 @@ describe('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
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('<ToolGroupMessage />', () => {
createToolCall({
callId: 'ask-user-tool',
name: ASK_USER_DISPLAY_NAME,
status: ToolCallStatus.Executing,
status: CoreToolCallStatus.Executing,
}),
];

View File

@@ -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<ToolGroupMessageProps> = ({
// 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<ToolGroupMessageProps> = ({
// 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<ToolGroupMessageProps> = ({
);
const hasPending = !visibleToolCalls.every(
(t) => t.status === ToolCallStatus.Success,
(t) => t.status === CoreToolCallStatus.Success,
);
const isShellCommand = toolCalls.some((t) => isShellTool(t.name));

View File

@@ -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('<ToolMessage />', () => {
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('<ToolMessage />', () => {
describe('ToolStatusIndicator rendering', () => {
it('shows ✓ for Success status', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Success} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Success} />,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -203,7 +203,7 @@ describe('<ToolMessage />', () => {
it('shows o for Pending status', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Pending} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Scheduled} />,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -211,7 +211,10 @@ describe('<ToolMessage />', () => {
it('shows ? for Confirming status', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Confirming} />,
<ToolMessage
{...baseProps}
status={CoreToolCallStatus.AwaitingApproval}
/>,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -219,7 +222,7 @@ describe('<ToolMessage />', () => {
it('shows - for Canceled status', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Canceled} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Cancelled} />,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -227,7 +230,7 @@ describe('<ToolMessage />', () => {
it('shows x for Error status', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Error} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Error} />,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -235,7 +238,7 @@ describe('<ToolMessage />', () => {
it('shows paused spinner for Executing status when streamingState is Idle', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Executing} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Executing} />,
StreamingState.Idle,
);
expect(lastFrame()).toMatchSnapshot();
@@ -243,7 +246,7 @@ describe('<ToolMessage />', () => {
it('shows paused spinner for Executing status when streamingState is WaitingForConfirmation', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Executing} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Executing} />,
StreamingState.WaitingForConfirmation,
);
expect(lastFrame()).toMatchSnapshot();
@@ -251,7 +254,7 @@ describe('<ToolMessage />', () => {
it('shows MockRespondingSpinner for Executing status when streamingState is Responding', () => {
const { lastFrame } = renderWithContext(
<ToolMessage {...baseProps} status={ToolCallStatus.Executing} />,
<ToolMessage {...baseProps} status={CoreToolCallStatus.Executing} />,
StreamingState.Responding, // Simulate app still responding
);
expect(lastFrame()).toMatchSnapshot();

View File

@@ -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,

View File

@@ -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('<ToolMessage /> - Raw Markdown Display Snapshots', () => {
const baseProps: ToolMessageProps = {
@@ -16,7 +18,7 @@ describe('<ToolMessage /> - 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',

View File

@@ -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,
},

View File

@@ -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<ToolStatusIndicatorProps> = ({
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<ToolStatusIndicatorProps> = ({
type ToolInfoProps = {
name: string;
description: string;
status: ToolCallStatus;
status: CoreToolCallStatus;
emphasis: TextEmphasis;
};
export const ToolInfo: React.FC<ToolInfoProps> = ({
name,
description,
status,
status: coreStatus,
emphasis,
}) => {
const status = mapCoreStatusToDisplayStatus(coreStatus);
const nameColor = React.useMemo<string>(() => {
switch (emphasis) {
case 'high':

View File

@@ -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,
},
];

View File

@@ -1,6 +1,6 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<ToolGroupMessage /> > Ask User Filtering > filtering logic for status='Error' and hasResult='error message' 1`] = `
exports[`<ToolGroupMessage /> > Ask User Filtering > filtering logic for status='error' and hasResult='error message' 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ x Ask User │
│ │
@@ -8,7 +8,7 @@ exports[`<ToolGroupMessage /> > Ask User Filtering > filtering logic for status=
╰──────────────────────────────────────────────────────────────────────────╯"
`;
exports[`<ToolGroupMessage /> > Ask User Filtering > filtering logic for status='Success' and hasResult='test result' 1`] = `
exports[`<ToolGroupMessage /> > Ask User Filtering > filtering logic for status='success' and hasResult='test result' 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ Ask User │
│ │