mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 11:34:44 -07:00
feat(plan): hide plan write and edit operations on plans in Plan Mode (#19012)
This commit is contained in:
@@ -2274,4 +2274,87 @@ describe('CoreToolScheduler Sequential Execution', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ApprovalMode Preservation', () => {
|
||||
it('should preserve approvalMode throughout tool lifecycle', async () => {
|
||||
// Arrange
|
||||
const executeFn = vi.fn().mockResolvedValue({
|
||||
llmContent: 'Tool executed',
|
||||
returnDisplay: 'Tool executed',
|
||||
});
|
||||
const mockTool = new MockTool({
|
||||
name: 'mockTool',
|
||||
execute: executeFn,
|
||||
shouldConfirmExecute: MOCK_TOOL_SHOULD_CONFIRM_EXECUTE,
|
||||
});
|
||||
|
||||
const mockToolRegistry = {
|
||||
getTool: () => mockTool,
|
||||
getAllToolNames: () => ['mockTool'],
|
||||
} as unknown as ToolRegistry;
|
||||
|
||||
const onAllToolCallsComplete = vi.fn();
|
||||
const onToolCallsUpdate = vi.fn();
|
||||
|
||||
// Set approval mode to PLAN
|
||||
const mockConfig = createMockConfig({
|
||||
getToolRegistry: () => mockToolRegistry,
|
||||
getApprovalMode: () => ApprovalMode.PLAN,
|
||||
// Ensure policy engine returns ASK_USER to trigger AwaitingApproval state
|
||||
getPolicyEngine: () =>
|
||||
({
|
||||
check: async () => ({ decision: PolicyDecision.ASK_USER }),
|
||||
}) as unknown as PolicyEngine,
|
||||
});
|
||||
mockConfig.getHookSystem = vi.fn().mockReturnValue(undefined);
|
||||
|
||||
const scheduler = new CoreToolScheduler({
|
||||
config: mockConfig,
|
||||
onAllToolCallsComplete,
|
||||
onToolCallsUpdate,
|
||||
getPreferredEditor: () => 'vscode',
|
||||
});
|
||||
|
||||
const abortController = new AbortController();
|
||||
const request = {
|
||||
callId: '1',
|
||||
name: 'mockTool',
|
||||
args: { param: 'value' },
|
||||
isClientInitiated: false,
|
||||
prompt_id: 'test-prompt',
|
||||
};
|
||||
|
||||
// Act - Schedule
|
||||
const schedulePromise = scheduler.schedule(
|
||||
request,
|
||||
abortController.signal,
|
||||
);
|
||||
|
||||
// Assert - Check AwaitingApproval state
|
||||
const awaitingCall = (await waitForStatus(
|
||||
onToolCallsUpdate,
|
||||
CoreToolCallStatus.AwaitingApproval,
|
||||
)) as WaitingToolCall;
|
||||
|
||||
expect(awaitingCall).toBeDefined();
|
||||
expect(awaitingCall.approvalMode).toBe(ApprovalMode.PLAN);
|
||||
|
||||
// Act - Confirm
|
||||
|
||||
await (
|
||||
awaitingCall.confirmationDetails as ToolCallConfirmationDetails
|
||||
).onConfirm(ToolConfirmationOutcome.ProceedOnce);
|
||||
|
||||
// Wait for completion
|
||||
await schedulePromise;
|
||||
|
||||
// Assert - Check Success state
|
||||
expect(onAllToolCallsComplete).toHaveBeenCalled();
|
||||
const completedCalls = onAllToolCallsComplete.mock
|
||||
.calls[0][0] as ToolCall[];
|
||||
expect(completedCalls).toHaveLength(1);
|
||||
expect(completedCalls[0].status).toBe(CoreToolCallStatus.Success);
|
||||
expect(completedCalls[0].approvalMode).toBe(ApprovalMode.PLAN);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,6 +217,7 @@ export class CoreToolScheduler {
|
||||
const invocation = currentCall.invocation;
|
||||
|
||||
const outcome = currentCall.outcome;
|
||||
const approvalMode = currentCall.approvalMode;
|
||||
|
||||
switch (newStatus) {
|
||||
case CoreToolCallStatus.Success: {
|
||||
@@ -232,6 +233,7 @@ export class CoreToolScheduler {
|
||||
response: auxiliaryData as ToolCallResponseInfo,
|
||||
durationMs,
|
||||
outcome,
|
||||
approvalMode,
|
||||
} as SuccessfulToolCall;
|
||||
}
|
||||
case CoreToolCallStatus.Error: {
|
||||
@@ -246,6 +248,7 @@ export class CoreToolScheduler {
|
||||
response: auxiliaryData as ToolCallResponseInfo,
|
||||
durationMs,
|
||||
outcome,
|
||||
approvalMode,
|
||||
} as ErroredToolCall;
|
||||
}
|
||||
case CoreToolCallStatus.AwaitingApproval:
|
||||
@@ -259,6 +262,7 @@ export class CoreToolScheduler {
|
||||
startTime: existingStartTime,
|
||||
outcome,
|
||||
invocation,
|
||||
approvalMode,
|
||||
} as WaitingToolCall;
|
||||
case CoreToolCallStatus.Scheduled:
|
||||
return {
|
||||
@@ -268,6 +272,7 @@ export class CoreToolScheduler {
|
||||
startTime: existingStartTime,
|
||||
outcome,
|
||||
invocation,
|
||||
approvalMode,
|
||||
} as ScheduledToolCall;
|
||||
case CoreToolCallStatus.Cancelled: {
|
||||
const durationMs = existingStartTime
|
||||
@@ -316,6 +321,7 @@ export class CoreToolScheduler {
|
||||
},
|
||||
durationMs,
|
||||
outcome,
|
||||
approvalMode,
|
||||
} as CancelledToolCall;
|
||||
}
|
||||
case CoreToolCallStatus.Validating:
|
||||
@@ -326,6 +332,7 @@ export class CoreToolScheduler {
|
||||
startTime: existingStartTime,
|
||||
outcome,
|
||||
invocation,
|
||||
approvalMode,
|
||||
} as ValidatingToolCall;
|
||||
case CoreToolCallStatus.Executing:
|
||||
return {
|
||||
@@ -335,6 +342,7 @@ export class CoreToolScheduler {
|
||||
startTime: existingStartTime,
|
||||
outcome,
|
||||
invocation,
|
||||
approvalMode,
|
||||
} as ExecutingToolCall;
|
||||
default: {
|
||||
const exhaustiveCheck: never = newStatus;
|
||||
@@ -373,6 +381,7 @@ export class CoreToolScheduler {
|
||||
status: CoreToolCallStatus.Error,
|
||||
tool: call.tool,
|
||||
response,
|
||||
approvalMode: call.approvalMode,
|
||||
} as ErroredToolCall;
|
||||
}
|
||||
|
||||
@@ -496,6 +505,7 @@ export class CoreToolScheduler {
|
||||
);
|
||||
}
|
||||
const requestsToProcess = Array.isArray(request) ? request : [request];
|
||||
const currentApprovalMode = this.config.getApprovalMode();
|
||||
this.completedToolCallsForBatch = [];
|
||||
|
||||
const newToolCalls: ToolCall[] = requestsToProcess.map(
|
||||
@@ -518,6 +528,7 @@ export class CoreToolScheduler {
|
||||
ToolErrorType.TOOL_NOT_REGISTERED,
|
||||
),
|
||||
durationMs: 0,
|
||||
approvalMode: currentApprovalMode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -536,6 +547,7 @@ export class CoreToolScheduler {
|
||||
ToolErrorType.INVALID_TOOL_PARAMS,
|
||||
),
|
||||
durationMs: 0,
|
||||
approvalMode: currentApprovalMode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -545,6 +557,7 @@ export class CoreToolScheduler {
|
||||
tool: toolInstance,
|
||||
invocation: invocationOrError,
|
||||
startTime: Date.now(),
|
||||
approvalMode: currentApprovalMode,
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -920,7 +933,7 @@ export class CoreToolScheduler {
|
||||
|
||||
this.toolCalls = this.toolCalls.map((tc) =>
|
||||
tc.request.callId === completedCall.request.callId
|
||||
? completedCall
|
||||
? { ...completedCall, approvalMode: tc.approvalMode }
|
||||
: tc,
|
||||
);
|
||||
this.notifyToolCallsUpdate();
|
||||
@@ -1049,6 +1062,7 @@ export class CoreToolScheduler {
|
||||
},
|
||||
durationMs,
|
||||
outcome: ToolConfirmationOutcome.Cancel,
|
||||
approvalMode: queuedCall.approvalMode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user