fix: yolo mode not respected (#4972)

This commit is contained in:
Leeroy Ding 2025-07-27 22:42:26 +01:00 committed by GitHub
parent 0b5cc96362
commit 9ca48c00a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 5 deletions

View File

@ -138,7 +138,6 @@ export function useReactToolScheduler(
outputUpdateHandler, outputUpdateHandler,
onAllToolCallsComplete: allToolCallsCompleteHandler, onAllToolCallsComplete: allToolCallsCompleteHandler,
onToolCallsUpdate: toolCallsUpdateHandler, onToolCallsUpdate: toolCallsUpdateHandler,
approvalMode: config.getApprovalMode(),
getPreferredEditor, getPreferredEditor,
config, config,
}), }),

View File

@ -20,6 +20,7 @@ import {
ToolResult, ToolResult,
Config, Config,
Icon, Icon,
ApprovalMode,
} from '../index.js'; } from '../index.js';
import { Part, PartListUnion } from '@google/genai'; import { Part, PartListUnion } from '@google/genai';
@ -126,6 +127,7 @@ describe('CoreToolScheduler', () => {
getSessionId: () => 'test-session-id', getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true, getUsageStatisticsEnabled: () => true,
getDebugMode: () => false, getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
} as unknown as Config; } as unknown as Config;
const scheduler = new CoreToolScheduler({ const scheduler = new CoreToolScheduler({
@ -194,6 +196,7 @@ describe('CoreToolScheduler with payload', () => {
getSessionId: () => 'test-session-id', getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true, getUsageStatisticsEnabled: () => true,
getDebugMode: () => false, getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
} as unknown as Config; } as unknown as Config;
const scheduler = new CoreToolScheduler({ const scheduler = new CoreToolScheduler({
@ -470,6 +473,7 @@ describe('CoreToolScheduler edit cancellation', () => {
getSessionId: () => 'test-session-id', getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true, getUsageStatisticsEnabled: () => true,
getDebugMode: () => false, getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.DEFAULT,
} as unknown as Config; } as unknown as Config;
const scheduler = new CoreToolScheduler({ const scheduler = new CoreToolScheduler({
@ -527,3 +531,85 @@ describe('CoreToolScheduler edit cancellation', () => {
expect(cancelledCall.response.resultDisplay.fileName).toBe('test.txt'); expect(cancelledCall.response.resultDisplay.fileName).toBe('test.txt');
}); });
}); });
describe('CoreToolScheduler YOLO mode', () => {
it('should execute tool requiring confirmation directly without waiting', async () => {
// Arrange
const mockTool = new MockTool();
// This tool would normally require confirmation.
mockTool.shouldConfirm = true;
const toolRegistry = {
getTool: () => mockTool,
getToolByName: () => mockTool,
// Other properties are not needed for this test but are included for type consistency.
getFunctionDeclarations: () => [],
tools: new Map(),
discovery: {} as any,
registerTool: () => {},
getToolByDisplayName: () => mockTool,
getTools: () => [],
discoverTools: async () => {},
getAllTools: () => [],
getToolsByServer: () => [],
};
const onAllToolCallsComplete = vi.fn();
const onToolCallsUpdate = vi.fn();
// Configure the scheduler for YOLO mode.
const mockConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getApprovalMode: () => ApprovalMode.YOLO,
} as unknown as Config;
const scheduler = new CoreToolScheduler({
config: mockConfig,
toolRegistry: Promise.resolve(toolRegistry as any),
onAllToolCallsComplete,
onToolCallsUpdate,
getPreferredEditor: () => 'vscode',
});
const abortController = new AbortController();
const request = {
callId: '1',
name: 'mockTool',
args: { param: 'value' },
isClientInitiated: false,
prompt_id: 'prompt-id-yolo',
};
// Act
await scheduler.schedule([request], abortController.signal);
// Assert
// 1. The tool's execute method was called directly.
expect(mockTool.executeFn).toHaveBeenCalledWith({ param: 'value' });
// 2. The tool call status never entered 'awaiting_approval'.
const statusUpdates = onToolCallsUpdate.mock.calls
.map((call) => (call[0][0] as ToolCall)?.status)
.filter(Boolean);
expect(statusUpdates).not.toContain('awaiting_approval');
expect(statusUpdates).toEqual([
'validating',
'scheduled',
'executing',
'success',
]);
// 3. The final callback indicates the tool call was successful.
expect(onAllToolCallsComplete).toHaveBeenCalled();
const completedCalls = onAllToolCallsComplete.mock
.calls[0][0] as ToolCall[];
expect(completedCalls).toHaveLength(1);
const completedCall = completedCalls[0];
expect(completedCall.status).toBe('success');
if (completedCall.status === 'success') {
expect(completedCall.response.resultDisplay).toBe('Tool executed');
}
});
});

View File

@ -219,7 +219,6 @@ interface CoreToolSchedulerOptions {
outputUpdateHandler?: OutputUpdateHandler; outputUpdateHandler?: OutputUpdateHandler;
onAllToolCallsComplete?: AllToolCallsCompleteHandler; onAllToolCallsComplete?: AllToolCallsCompleteHandler;
onToolCallsUpdate?: ToolCallsUpdateHandler; onToolCallsUpdate?: ToolCallsUpdateHandler;
approvalMode?: ApprovalMode;
getPreferredEditor: () => EditorType | undefined; getPreferredEditor: () => EditorType | undefined;
config: Config; config: Config;
} }
@ -230,7 +229,6 @@ export class CoreToolScheduler {
private outputUpdateHandler?: OutputUpdateHandler; private outputUpdateHandler?: OutputUpdateHandler;
private onAllToolCallsComplete?: AllToolCallsCompleteHandler; private onAllToolCallsComplete?: AllToolCallsCompleteHandler;
private onToolCallsUpdate?: ToolCallsUpdateHandler; private onToolCallsUpdate?: ToolCallsUpdateHandler;
private approvalMode: ApprovalMode;
private getPreferredEditor: () => EditorType | undefined; private getPreferredEditor: () => EditorType | undefined;
private config: Config; private config: Config;
@ -240,7 +238,6 @@ export class CoreToolScheduler {
this.outputUpdateHandler = options.outputUpdateHandler; this.outputUpdateHandler = options.outputUpdateHandler;
this.onAllToolCallsComplete = options.onAllToolCallsComplete; this.onAllToolCallsComplete = options.onAllToolCallsComplete;
this.onToolCallsUpdate = options.onToolCallsUpdate; this.onToolCallsUpdate = options.onToolCallsUpdate;
this.approvalMode = options.approvalMode ?? ApprovalMode.DEFAULT;
this.getPreferredEditor = options.getPreferredEditor; this.getPreferredEditor = options.getPreferredEditor;
} }
@ -462,7 +459,7 @@ export class CoreToolScheduler {
const { request: reqInfo, tool: toolInstance } = toolCall; const { request: reqInfo, tool: toolInstance } = toolCall;
try { try {
if (this.approvalMode === ApprovalMode.YOLO) { if (this.config.getApprovalMode() === ApprovalMode.YOLO) {
this.setStatusInternal(reqInfo.callId, 'scheduled'); this.setStatusInternal(reqInfo.callId, 'scheduled');
} else { } else {
const confirmationDetails = await toolInstance.shouldConfirmExecute( const confirmationDetails = await toolInstance.shouldConfirmExecute(