feat(plan): hide plan write and edit operations on plans in Plan Mode (#19012)

This commit is contained in:
Jerop Kipruto
2026-02-13 18:15:21 -05:00
committed by GitHub
parent 4e1b3b5f57
commit 9df604b01b
20 changed files with 373 additions and 108 deletions
+92 -2
View File
@@ -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'];
+55
View 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.