mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 02:24:09 -07:00
feat(plan): hide plan write and edit operations on plans in Plan Mode (#19012)
This commit is contained in:
@@ -5,11 +5,101 @@
|
||||
*/
|
||||
|
||||
import { expect, describe, it } from 'vitest';
|
||||
import { doesToolInvocationMatch, getToolSuggestion } from './tool-utils.js';
|
||||
import {
|
||||
doesToolInvocationMatch,
|
||||
getToolSuggestion,
|
||||
shouldHideToolCall,
|
||||
} from './tool-utils.js';
|
||||
import type { AnyToolInvocation, Config } from '../index.js';
|
||||
import { ReadFileTool } from '../index.js';
|
||||
import {
|
||||
ReadFileTool,
|
||||
ApprovalMode,
|
||||
CoreToolCallStatus,
|
||||
ASK_USER_DISPLAY_NAME,
|
||||
WRITE_FILE_DISPLAY_NAME,
|
||||
EDIT_DISPLAY_NAME,
|
||||
READ_FILE_DISPLAY_NAME,
|
||||
} from '../index.js';
|
||||
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
|
||||
|
||||
describe('shouldHideToolCall', () => {
|
||||
it.each([
|
||||
{
|
||||
status: CoreToolCallStatus.Scheduled,
|
||||
hasResult: true,
|
||||
shouldHide: true,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.Executing,
|
||||
hasResult: true,
|
||||
shouldHide: true,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.AwaitingApproval,
|
||||
hasResult: true,
|
||||
shouldHide: true,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.Validating,
|
||||
hasResult: true,
|
||||
shouldHide: true,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.Success,
|
||||
hasResult: true,
|
||||
shouldHide: false,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.Error,
|
||||
hasResult: false,
|
||||
shouldHide: true,
|
||||
},
|
||||
{
|
||||
status: CoreToolCallStatus.Error,
|
||||
hasResult: true,
|
||||
shouldHide: false,
|
||||
},
|
||||
])(
|
||||
'AskUser: status=$status, hasResult=$hasResult -> hide=$shouldHide',
|
||||
({ status, hasResult, shouldHide }) => {
|
||||
expect(
|
||||
shouldHideToolCall({
|
||||
displayName: ASK_USER_DISPLAY_NAME,
|
||||
status,
|
||||
hasResultDisplay: hasResult,
|
||||
}),
|
||||
).toBe(shouldHide);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: WRITE_FILE_DISPLAY_NAME,
|
||||
mode: ApprovalMode.PLAN,
|
||||
visible: false,
|
||||
},
|
||||
{ name: EDIT_DISPLAY_NAME, mode: ApprovalMode.PLAN, visible: false },
|
||||
{
|
||||
name: WRITE_FILE_DISPLAY_NAME,
|
||||
mode: ApprovalMode.DEFAULT,
|
||||
visible: true,
|
||||
},
|
||||
{ name: READ_FILE_DISPLAY_NAME, mode: ApprovalMode.PLAN, visible: true },
|
||||
])(
|
||||
'Plan Mode: tool=$name, mode=$mode -> visible=$visible',
|
||||
({ name, mode, visible }) => {
|
||||
expect(
|
||||
shouldHideToolCall({
|
||||
displayName: name,
|
||||
status: CoreToolCallStatus.Success,
|
||||
approvalMode: mode,
|
||||
hasResultDisplay: true,
|
||||
}),
|
||||
).toBe(!visible);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('getToolSuggestion', () => {
|
||||
it('should suggest the top N closest tool names for a typo', () => {
|
||||
const allToolNames = ['list_files', 'read_file', 'write_file'];
|
||||
|
||||
@@ -8,6 +8,61 @@ import type { AnyDeclarativeTool, AnyToolInvocation } from '../index.js';
|
||||
import { isTool } from '../index.js';
|
||||
import { SHELL_TOOL_NAMES } from './shell-utils.js';
|
||||
import levenshtein from 'fast-levenshtein';
|
||||
import { ApprovalMode } from '../policy/types.js';
|
||||
import { CoreToolCallStatus } from '../scheduler/types.js';
|
||||
import {
|
||||
ASK_USER_DISPLAY_NAME,
|
||||
WRITE_FILE_DISPLAY_NAME,
|
||||
EDIT_DISPLAY_NAME,
|
||||
} from '../tools/tool-names.js';
|
||||
|
||||
/**
|
||||
* Options for determining if a tool call should be hidden in the CLI history.
|
||||
*/
|
||||
export interface ShouldHideToolCallParams {
|
||||
/** The display name of the tool. */
|
||||
displayName: string;
|
||||
/** The current status of the tool call. */
|
||||
status: CoreToolCallStatus;
|
||||
/** The approval mode active when the tool was called. */
|
||||
approvalMode?: ApprovalMode;
|
||||
/** Whether the tool has produced a result for display. */
|
||||
hasResultDisplay: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a tool call should be hidden from the standard tool history UI.
|
||||
*
|
||||
* We hide tools in several cases:
|
||||
* 1. Ask User tools that are in progress, displayed via specialized UI.
|
||||
* 2. Ask User tools that errored without result display, typically param
|
||||
* validation errors that the agent automatically recovers from.
|
||||
* 3. WriteFile and Edit tools when in Plan Mode, redundant because the
|
||||
* resulting plans are displayed separately upon exiting plan mode.
|
||||
*/
|
||||
export function shouldHideToolCall(params: ShouldHideToolCallParams): boolean {
|
||||
const { displayName, status, approvalMode, hasResultDisplay } = params;
|
||||
|
||||
switch (displayName) {
|
||||
case ASK_USER_DISPLAY_NAME:
|
||||
switch (status) {
|
||||
case CoreToolCallStatus.Scheduled:
|
||||
case CoreToolCallStatus.Validating:
|
||||
case CoreToolCallStatus.Executing:
|
||||
case CoreToolCallStatus.AwaitingApproval:
|
||||
return true;
|
||||
case CoreToolCallStatus.Error:
|
||||
return !hasResultDisplay;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case WRITE_FILE_DISPLAY_NAME:
|
||||
case EDIT_DISPLAY_NAME:
|
||||
return approvalMode === ApprovalMode.PLAN;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a suggestion string for a tool name that was not found in the registry.
|
||||
|
||||
Reference in New Issue
Block a user