summaryrefslogtreecommitdiff
path: root/packages/core/src
diff options
context:
space:
mode:
authorTolik Malibroda <[email protected]>2025-06-02 22:05:45 +0200
committerGitHub <[email protected]>2025-06-02 22:05:45 +0200
commit0795e55f0e7d2f2822bcd83eaf066eb99c67f858 (patch)
tree3fd259976c8cfc5df79bba2d37f0a17fa3f683a4 /packages/core/src
parent42bedbc3d39265932cbd6c9b818b6a7fbcbdd022 (diff)
feat: Add --yolo mode that automatically accepts all tools executions (#695)
Co-authored-by: N. Taylor Mullen <[email protected]>
Diffstat (limited to 'packages/core/src')
-rw-r--r--packages/core/src/config/config.ts21
-rw-r--r--packages/core/src/core/coreToolScheduler.ts52
-rw-r--r--packages/core/src/tools/edit.test.ts20
-rw-r--r--packages/core/src/tools/edit.ts6
-rw-r--r--packages/core/src/tools/tool-registry.test.ts4
-rw-r--r--packages/core/src/tools/write-file.test.ts12
-rw-r--r--packages/core/src/tools/write-file.ts6
7 files changed, 65 insertions, 56 deletions
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index a6279e2e..71c4a7d2 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -22,6 +22,12 @@ import { ReadManyFilesTool } from '../tools/read-many-files.js';
import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
import { WebSearchTool } from '../tools/web-search.js';
+export enum ApprovalMode {
+ DEFAULT = 'default',
+ AUTO_EDIT = 'autoEdit',
+ YOLO = 'yolo',
+}
+
export class MCPServerConfig {
constructor(
// For stdio transport
@@ -53,7 +59,7 @@ export interface ConfigParameters {
userAgent: string;
userMemory?: string;
geminiMdFileCount?: number;
- alwaysSkipModificationConfirmation?: boolean;
+ approvalMode?: ApprovalMode;
vertexai?: boolean;
showMemoryUsage?: boolean;
contextFileName?: string;
@@ -76,7 +82,7 @@ export class Config {
private readonly userAgent: string;
private userMemory: string;
private geminiMdFileCount: number;
- private alwaysSkipModificationConfirmation: boolean;
+ private approvalMode: ApprovalMode;
private readonly vertexai: boolean | undefined;
private readonly showMemoryUsage: boolean;
@@ -96,8 +102,7 @@ export class Config {
this.userAgent = params.userAgent;
this.userMemory = params.userMemory ?? '';
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
- this.alwaysSkipModificationConfirmation =
- params.alwaysSkipModificationConfirmation ?? false;
+ this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
this.vertexai = params.vertexai;
this.showMemoryUsage = params.showMemoryUsage ?? false;
@@ -179,12 +184,12 @@ export class Config {
this.geminiMdFileCount = count;
}
- getAlwaysSkipModificationConfirmation(): boolean {
- return this.alwaysSkipModificationConfirmation;
+ getApprovalMode(): ApprovalMode {
+ return this.approvalMode;
}
- setAlwaysSkipModificationConfirmation(skip: boolean): void {
- this.alwaysSkipModificationConfirmation = skip;
+ setApprovalMode(mode: ApprovalMode): void {
+ this.approvalMode = mode;
}
getVertexAI(): boolean | undefined {
diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts
index 58f821c5..3fe6562e 100644
--- a/packages/core/src/core/coreToolScheduler.ts
+++ b/packages/core/src/core/coreToolScheduler.ts
@@ -12,6 +12,7 @@ import {
ToolCallConfirmationDetails,
ToolResult,
ToolRegistry,
+ ApprovalMode,
} from '../index.js';
import { Part, PartUnion, PartListUnion } from '@google/genai';
@@ -159,6 +160,7 @@ interface CoreToolSchedulerOptions {
outputUpdateHandler?: OutputUpdateHandler;
onAllToolCallsComplete?: AllToolCallsCompleteHandler;
onToolCallsUpdate?: ToolCallsUpdateHandler;
+ approvalMode?: ApprovalMode;
}
export class CoreToolScheduler {
@@ -168,12 +170,14 @@ export class CoreToolScheduler {
private outputUpdateHandler?: OutputUpdateHandler;
private onAllToolCallsComplete?: AllToolCallsCompleteHandler;
private onToolCallsUpdate?: ToolCallsUpdateHandler;
+ private approvalMode: ApprovalMode;
constructor(options: CoreToolSchedulerOptions) {
this.toolRegistry = options.toolRegistry;
this.outputUpdateHandler = options.outputUpdateHandler;
this.onAllToolCallsComplete = options.onAllToolCallsComplete;
this.onToolCallsUpdate = options.onToolCallsUpdate;
+ this.approvalMode = options.approvalMode ?? ApprovalMode.DEFAULT;
this.abortController = new AbortController();
}
@@ -324,29 +328,33 @@ export class CoreToolScheduler {
const { request: reqInfo, tool: toolInstance } = toolCall;
try {
- const confirmationDetails = await toolInstance.shouldConfirmExecute(
- reqInfo.args,
- this.abortController.signal,
- );
-
- if (confirmationDetails) {
- const originalOnConfirm = confirmationDetails.onConfirm;
- const wrappedConfirmationDetails: ToolCallConfirmationDetails = {
- ...confirmationDetails,
- onConfirm: (outcome: ToolConfirmationOutcome) =>
- this.handleConfirmationResponse(
- reqInfo.callId,
- originalOnConfirm,
- outcome,
- ),
- };
- this.setStatusInternal(
- reqInfo.callId,
- 'awaiting_approval',
- wrappedConfirmationDetails,
- );
- } else {
+ if (this.approvalMode === ApprovalMode.YOLO) {
this.setStatusInternal(reqInfo.callId, 'scheduled');
+ } else {
+ const confirmationDetails = await toolInstance.shouldConfirmExecute(
+ reqInfo.args,
+ this.abortController.signal,
+ );
+
+ if (confirmationDetails) {
+ const originalOnConfirm = confirmationDetails.onConfirm;
+ const wrappedConfirmationDetails: ToolCallConfirmationDetails = {
+ ...confirmationDetails,
+ onConfirm: (outcome: ToolConfirmationOutcome) =>
+ this.handleConfirmationResponse(
+ reqInfo.callId,
+ originalOnConfirm,
+ outcome,
+ ),
+ };
+ this.setStatusInternal(
+ reqInfo.callId,
+ 'awaiting_approval',
+ wrappedConfirmationDetails,
+ );
+ } else {
+ this.setStatusInternal(reqInfo.callId, 'scheduled');
+ }
}
} catch (error) {
this.setStatusInternal(
diff --git a/packages/core/src/tools/edit.test.ts b/packages/core/src/tools/edit.test.ts
index c6c2ba63..3b93708a 100644
--- a/packages/core/src/tools/edit.test.ts
+++ b/packages/core/src/tools/edit.test.ts
@@ -25,7 +25,7 @@ import { FileDiff } from './tools.js';
import path from 'path';
import fs from 'fs';
import os from 'os';
-import { Config } from '../config/config.js';
+import { ApprovalMode, Config } from '../config/config.js';
import { Content, Part, SchemaUnion } from '@google/genai';
describe('EditTool', () => {
@@ -41,8 +41,8 @@ describe('EditTool', () => {
mockConfig = {
getTargetDir: () => rootDir,
- getAlwaysSkipModificationConfirmation: vi.fn(() => false),
- setAlwaysSkipModificationConfirmation: vi.fn(),
+ getApprovalMode: vi.fn(() => false),
+ setApprovalMode: vi.fn(),
// getGeminiConfig: () => ({ apiKey: 'test-api-key' }), // This was not a real Config method
// Add other properties/methods of Config if EditTool uses them
// Minimal other methods to satisfy Config type if needed by EditTool constructor or other direct uses:
@@ -65,12 +65,10 @@ describe('EditTool', () => {
} as unknown as Config;
// Reset mocks before each test
- (mockConfig.getAlwaysSkipModificationConfirmation as Mock).mockClear();
- (mockConfig.setAlwaysSkipModificationConfirmation as Mock).mockClear();
+ (mockConfig.getApprovalMode as Mock).mockClear();
+ (mockConfig.getApprovalMode as Mock).mockClear();
// Default to not skipping confirmation
- (mockConfig.getAlwaysSkipModificationConfirmation as Mock).mockReturnValue(
- false,
- );
+ (mockConfig.getApprovalMode as Mock).mockReturnValue(ApprovalMode.DEFAULT);
// Reset mocks and set default implementation for ensureCorrectEdit
mockEnsureCorrectEdit.mockReset();
@@ -439,9 +437,9 @@ describe('EditTool', () => {
new_string: fileContent,
};
- (
- mockConfig.getAlwaysSkipModificationConfirmation as Mock
- ).mockReturnValueOnce(true);
+ (mockConfig.getApprovalMode as Mock).mockReturnValueOnce(
+ ApprovalMode.AUTO_EDIT,
+ );
const result = await tool.execute(params, new AbortController().signal);
expect(result.llmContent).toMatch(/Created new file/);
diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts
index 53b12480..b2f648f8 100644
--- a/packages/core/src/tools/edit.ts
+++ b/packages/core/src/tools/edit.ts
@@ -20,7 +20,7 @@ import { makeRelative, shortenPath } from '../utils/paths.js';
import { isNodeError } from '../utils/errors.js';
import { ReadFileTool } from './read-file.js';
import { GeminiClient } from '../core/client.js';
-import { Config } from '../config/config.js';
+import { Config, ApprovalMode } from '../config/config.js';
import { ensureCorrectEdit } from '../utils/editCorrector.js';
import { DEFAULT_DIFF_OPTIONS } from './diffOptions.js';
@@ -281,7 +281,7 @@ Expectation for required parameters:
params: EditToolParams,
abortSignal: AbortSignal,
): Promise<ToolCallConfirmationDetails | false> {
- if (this.config.getAlwaysSkipModificationConfirmation()) {
+ if (this.config.getApprovalMode() === ApprovalMode.AUTO_EDIT) {
return false;
}
const validationError = this.validateToolParams(params);
@@ -356,7 +356,7 @@ Expectation for required parameters:
fileDiff,
onConfirm: async (outcome: ToolConfirmationOutcome) => {
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
- this.config.setAlwaysSkipModificationConfirmation(true);
+ this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
}
},
};
diff --git a/packages/core/src/tools/tool-registry.test.ts b/packages/core/src/tools/tool-registry.test.ts
index 121e91c8..9aaa7e5a 100644
--- a/packages/core/src/tools/tool-registry.test.ts
+++ b/packages/core/src/tools/tool-registry.test.ts
@@ -16,7 +16,7 @@ import {
} from 'vitest';
import { ToolRegistry, DiscoveredTool } from './tool-registry.js';
import { DiscoveredMCPTool } from './mcp-tool.js';
-import { Config, ConfigParameters } from '../config/config.js';
+import { ApprovalMode, Config, ConfigParameters } from '../config/config.js';
import { BaseTool, ToolResult } from './tools.js';
import { FunctionDeclaration } from '@google/genai';
import { execSync, spawn } from 'node:child_process'; // Import spawn here
@@ -85,7 +85,7 @@ const baseConfigParams: ConfigParameters = {
userAgent: 'TestAgent/1.0',
userMemory: '',
geminiMdFileCount: 0,
- alwaysSkipModificationConfirmation: false,
+ approvalMode: ApprovalMode.DEFAULT,
vertexai: false,
};
diff --git a/packages/core/src/tools/write-file.test.ts b/packages/core/src/tools/write-file.test.ts
index 3fd97c9e..c94edfd1 100644
--- a/packages/core/src/tools/write-file.test.ts
+++ b/packages/core/src/tools/write-file.test.ts
@@ -20,7 +20,7 @@ import {
ToolEditConfirmationDetails,
} from './tools.js';
import { type EditToolParams } from './edit.js';
-import { Config } from '../config/config.js';
+import { ApprovalMode, Config } from '../config/config.js';
import { ToolRegistry } from './tool-registry.js';
import path from 'path';
import fs from 'fs';
@@ -51,8 +51,8 @@ vi.mocked(ensureCorrectFileContent).mockImplementation(
// Mock Config
const mockConfigInternal = {
getTargetDir: () => rootDir,
- getAlwaysSkipModificationConfirmation: vi.fn(() => false),
- setAlwaysSkipModificationConfirmation: vi.fn(),
+ getApprovalMode: vi.fn(() => ApprovalMode.DEFAULT),
+ setApprovalMode: vi.fn(),
getApiKey: () => 'test-key',
getModel: () => 'test-model',
getSandbox: () => false,
@@ -100,10 +100,8 @@ describe('WriteFileTool', () => {
tool = new WriteFileTool(mockConfig);
// Reset mocks before each test
- mockConfigInternal.getAlwaysSkipModificationConfirmation.mockReturnValue(
- false,
- );
- mockConfigInternal.setAlwaysSkipModificationConfirmation.mockClear();
+ mockConfigInternal.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
+ mockConfigInternal.setApprovalMode.mockClear();
mockEnsureCorrectEdit.mockReset();
mockEnsureCorrectFileContent.mockReset();
diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts
index 2285c819..2e04a10a 100644
--- a/packages/core/src/tools/write-file.ts
+++ b/packages/core/src/tools/write-file.ts
@@ -7,7 +7,7 @@
import fs from 'fs';
import path from 'path';
import * as Diff from 'diff';
-import { Config } from '../config/config.js';
+import { Config, ApprovalMode } from '../config/config.js';
import {
BaseTool,
ToolResult,
@@ -143,7 +143,7 @@ export class WriteFileTool extends BaseTool<WriteFileToolParams, ToolResult> {
params: WriteFileToolParams,
abortSignal: AbortSignal,
): Promise<ToolCallConfirmationDetails | false> {
- if (this.config.getAlwaysSkipModificationConfirmation()) {
+ if (this.config.getApprovalMode() === ApprovalMode.AUTO_EDIT) {
return false;
}
@@ -186,7 +186,7 @@ export class WriteFileTool extends BaseTool<WriteFileToolParams, ToolResult> {
fileDiff,
onConfirm: async (outcome: ToolConfirmationOutcome) => {
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
- this.config.setAlwaysSkipModificationConfirmation(true);
+ this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
}
},
};