mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
refactor: deprecate legacy confirmation settings and enforce Policy Engine (#15626)
This commit is contained in:
@@ -94,7 +94,6 @@ they appear in the UI.
|
||||
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
|
||||
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `10000` |
|
||||
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `100` |
|
||||
| Enable Message Bus Integration | `tools.enableMessageBusIntegration` | Enable policy-based tool confirmation via message bus integration. | `true` |
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
@@ -684,14 +684,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
- **Default:** `1000`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.enableMessageBusIntegration`** (boolean):
|
||||
- **Description:** Enable policy-based tool confirmation via message bus
|
||||
integration. When enabled, tools automatically respect policy engine
|
||||
decisions (ALLOW/DENY/ASK_USER) without requiring individual tool
|
||||
implementations.
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`tools.enableHooks`** (boolean):
|
||||
- **Description:** Enable the hooks system for intercepting and customizing
|
||||
Gemini CLI behavior. When enabled, hooks configured in settings will execute
|
||||
|
||||
@@ -59,7 +59,6 @@ export function createMockConfig(
|
||||
getEmbeddingModel: vi.fn().mockReturnValue('text-embedding-004'),
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
getUserTier: vi.fn(),
|
||||
getEnableMessageBusIntegration: vi.fn().mockReturnValue(false),
|
||||
getMessageBus: vi.fn(),
|
||||
getPolicyEngine: vi.fn(),
|
||||
getEnableExtensionReloading: vi.fn().mockReturnValue(false),
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
WEB_FETCH_TOOL_NAME,
|
||||
type ExtensionLoader,
|
||||
debugLogger,
|
||||
ApprovalMode,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { loadCliConfig, parseArguments, type CliArgs } from './config.js';
|
||||
import type { Settings } from './settings.js';
|
||||
@@ -629,14 +630,7 @@ describe('loadCliConfig', () => {
|
||||
expect(config.getFileFilteringRespectGeminiIgnore()).toBe(
|
||||
DEFAULT_FILE_FILTERING_OPTIONS.respectGeminiIgnore,
|
||||
);
|
||||
});
|
||||
|
||||
it('should default enableMessageBusIntegration to true when unconfigured', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config['enableMessageBusIntegration']).toBe(true);
|
||||
expect(config.getApprovalMode()).toBe(ApprovalMode.DEFAULT);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -542,9 +542,6 @@ export async function loadCliConfig(
|
||||
approvalMode,
|
||||
);
|
||||
|
||||
const enableMessageBusIntegration =
|
||||
settings.tools?.enableMessageBusIntegration ?? true;
|
||||
|
||||
const allowedTools = argv.allowedTools || settings.tools?.allowed || [];
|
||||
const allowedToolsSet = new Set(allowedTools);
|
||||
|
||||
@@ -695,7 +692,6 @@ export async function loadCliConfig(
|
||||
output: {
|
||||
format: (argv.outputFormat ?? settings.output?.format) as OutputFormat,
|
||||
},
|
||||
enableMessageBusIntegration,
|
||||
codebaseInvestigatorSettings:
|
||||
settings.experimental?.codebaseInvestigatorSettings,
|
||||
introspectionAgentSettings:
|
||||
|
||||
@@ -85,7 +85,6 @@ const MIGRATION_MAP: Record<string, string> = {
|
||||
disableAutoUpdate: 'general.disableAutoUpdate',
|
||||
disableUpdateNag: 'general.disableUpdateNag',
|
||||
dnsResolutionOrder: 'advanced.dnsResolutionOrder',
|
||||
enableMessageBusIntegration: 'tools.enableMessageBusIntegration',
|
||||
enableHooks: 'tools.enableHooks',
|
||||
enablePromptCompletion: 'general.enablePromptCompletion',
|
||||
enforcedAuthType: 'security.auth.enforcedType',
|
||||
|
||||
@@ -1073,18 +1073,6 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'The number of lines to keep when truncating tool output.',
|
||||
showInDialog: true,
|
||||
},
|
||||
enableMessageBusIntegration: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Message Bus Integration',
|
||||
category: 'Tools',
|
||||
requiresRestart: true,
|
||||
default: true,
|
||||
description: oneLine`
|
||||
Enable policy-based tool confirmation via message bus integration.
|
||||
When enabled, tools automatically respect policy engine decisions (ALLOW/DENY/ASK_USER) without requiring individual tool implementations.
|
||||
`,
|
||||
showInDialog: true,
|
||||
},
|
||||
enableHooks: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Hooks System',
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('Settings Repro', () => {
|
||||
showColor: true,
|
||||
enableInteractiveShell: true,
|
||||
},
|
||||
enableMessageBusIntegration: true,
|
||||
truncateToolOutputLines: 100,
|
||||
},
|
||||
experimental: {
|
||||
useModelRouter: false,
|
||||
|
||||
@@ -96,7 +96,6 @@ describe('BuiltinCommandLoader', () => {
|
||||
vi.clearAllMocks();
|
||||
mockConfig = {
|
||||
getFolderTrust: vi.fn().mockReturnValue(true),
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getEnableExtensionReloading: () => false,
|
||||
getEnableHooks: () => false,
|
||||
} as unknown as Config;
|
||||
@@ -172,7 +171,6 @@ describe('BuiltinCommandLoader', () => {
|
||||
it('should include policies command when message bus integration is enabled', async () => {
|
||||
const mockConfigWithMessageBus = {
|
||||
...mockConfig,
|
||||
getEnableMessageBusIntegration: () => true,
|
||||
getEnableHooks: () => false,
|
||||
} as unknown as Config;
|
||||
const loader = new BuiltinCommandLoader(mockConfigWithMessageBus);
|
||||
@@ -180,18 +178,6 @@ describe('BuiltinCommandLoader', () => {
|
||||
const policiesCmd = commands.find((c) => c.name === 'policies');
|
||||
expect(policiesCmd).toBeDefined();
|
||||
});
|
||||
|
||||
it('should exclude policies command when message bus integration is disabled', async () => {
|
||||
const mockConfigWithoutMessageBus = {
|
||||
...mockConfig,
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getEnableHooks: () => false,
|
||||
} as unknown as Config;
|
||||
const loader = new BuiltinCommandLoader(mockConfigWithoutMessageBus);
|
||||
const commands = await loader.loadCommands(new AbortController().signal);
|
||||
const policiesCmd = commands.find((c) => c.name === 'policies');
|
||||
expect(policiesCmd).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BuiltinCommandLoader profile', () => {
|
||||
@@ -202,7 +188,6 @@ describe('BuiltinCommandLoader profile', () => {
|
||||
mockConfig = {
|
||||
getFolderTrust: vi.fn().mockReturnValue(false),
|
||||
getCheckpointingEnabled: () => false,
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getEnableExtensionReloading: () => false,
|
||||
getEnableHooks: () => false,
|
||||
} as unknown as Config;
|
||||
|
||||
@@ -81,9 +81,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
||||
modelCommand,
|
||||
...(this.config?.getFolderTrust() ? [permissionsCommand] : []),
|
||||
privacyCommand,
|
||||
...(this.config?.getEnableMessageBusIntegration()
|
||||
? [policiesCommand]
|
||||
: []),
|
||||
policiesCommand,
|
||||
...(isDevelopment ? [profileCommand] : []),
|
||||
quitCommand,
|
||||
restoreCommand(this.config),
|
||||
|
||||
@@ -80,7 +80,6 @@ const mockConfig = {
|
||||
getUseSmartEdit: () => false,
|
||||
getGeminiClient: () => null, // No client needed for these tests
|
||||
getShellExecutionConfig: () => ({ terminalWidth: 80, terminalHeight: 24 }),
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getMessageBus: () => null,
|
||||
getPolicyEngine: () => null,
|
||||
isInteractive: () => false,
|
||||
|
||||
@@ -61,6 +61,7 @@ vi.mock('../tools/tool-registry', () => {
|
||||
ToolRegistryMock.prototype.sortTools = vi.fn();
|
||||
ToolRegistryMock.prototype.getAllTools = vi.fn(() => []); // Mock methods if needed
|
||||
ToolRegistryMock.prototype.getTool = vi.fn();
|
||||
ToolRegistryMock.prototype.setMessageBus = vi.fn();
|
||||
ToolRegistryMock.prototype.getFunctionDeclarations = vi.fn(() => []);
|
||||
return { ToolRegistry: ToolRegistryMock };
|
||||
});
|
||||
@@ -923,7 +924,7 @@ describe('Server Config (config.ts)', () => {
|
||||
expect(DelegateToAgentToolMock).toHaveBeenCalledWith(
|
||||
expect.anything(), // AgentRegistry
|
||||
config,
|
||||
undefined,
|
||||
expect.anything(), // MessageBus
|
||||
);
|
||||
|
||||
const calls = registerToolMock.mock.calls;
|
||||
|
||||
@@ -312,7 +312,6 @@ export interface ConfigParameters {
|
||||
useWriteTodos?: boolean;
|
||||
policyEngineConfig?: PolicyEngineConfig;
|
||||
output?: OutputSettings;
|
||||
enableMessageBusIntegration?: boolean;
|
||||
disableModelRouterForAuth?: AuthType[];
|
||||
codebaseInvestigatorSettings?: CodebaseInvestigatorSettings;
|
||||
introspectionAgentSettings?: IntrospectionAgentSettings;
|
||||
@@ -436,7 +435,6 @@ export class Config {
|
||||
private readonly messageBus: MessageBus;
|
||||
private readonly policyEngine: PolicyEngine;
|
||||
private readonly outputSettings: OutputSettings;
|
||||
private readonly enableMessageBusIntegration: boolean;
|
||||
private readonly codebaseInvestigatorSettings: CodebaseInvestigatorSettings;
|
||||
private readonly introspectionAgentSettings: IntrospectionAgentSettings;
|
||||
private readonly continueOnFailedApiCall: boolean;
|
||||
@@ -579,14 +577,6 @@ export class Config {
|
||||
? params.hooks.disabled
|
||||
: undefined) ?? [];
|
||||
|
||||
// Enable MessageBus integration if:
|
||||
// 1. Explicitly enabled via setting, OR
|
||||
// 2. Hooks are enabled and hooks are configured
|
||||
const hasHooks = params.hooks && Object.keys(params.hooks).length > 0;
|
||||
const hooksNeedMessageBus = this.enableHooks && hasHooks;
|
||||
this.enableMessageBusIntegration =
|
||||
params.enableMessageBusIntegration ??
|
||||
(hooksNeedMessageBus ? true : false);
|
||||
this.codebaseInvestigatorSettings = {
|
||||
enabled: params.codebaseInvestigatorSettings?.enabled ?? true,
|
||||
maxNumTurns: params.codebaseInvestigatorSettings?.maxNumTurns ?? 10,
|
||||
@@ -1580,10 +1570,6 @@ export class Config {
|
||||
return this.policyEngine;
|
||||
}
|
||||
|
||||
getEnableMessageBusIntegration(): boolean {
|
||||
return this.enableMessageBusIntegration;
|
||||
}
|
||||
|
||||
getEnableHooks(): boolean {
|
||||
return this.enableHooks;
|
||||
}
|
||||
@@ -1600,9 +1586,7 @@ export class Config {
|
||||
const registry = new ToolRegistry(this);
|
||||
|
||||
// Set message bus on tool registry before discovery so MCP tools can access it
|
||||
if (this.getEnableMessageBusIntegration()) {
|
||||
registry.setMessageBus(this.messageBus);
|
||||
}
|
||||
registry.setMessageBus(this.messageBus);
|
||||
|
||||
// helper to create & register core tools that are enabled
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -1628,11 +1612,7 @@ export class Config {
|
||||
// Pass message bus to tools when feature flag is enabled
|
||||
// This first implementation is only focused on the general case of
|
||||
// the tool registry.
|
||||
const messageBusEnabled = this.getEnableMessageBusIntegration();
|
||||
|
||||
const toolArgs = messageBusEnabled
|
||||
? [...args, this.getMessageBus()]
|
||||
: args;
|
||||
const toolArgs = [...args, this.getMessageBus()];
|
||||
|
||||
registry.registerTool(new ToolClass(...toolArgs));
|
||||
}
|
||||
@@ -1686,11 +1666,10 @@ export class Config {
|
||||
!allowedTools || allowedTools.includes(DELEGATE_TO_AGENT_TOOL_NAME);
|
||||
|
||||
if (isAllowed) {
|
||||
const messageBusEnabled = this.getEnableMessageBusIntegration();
|
||||
const delegateTool = new DelegateToAgentTool(
|
||||
this.agentRegistry,
|
||||
this,
|
||||
messageBusEnabled ? this.getMessageBus() : undefined,
|
||||
this.getMessageBus(),
|
||||
);
|
||||
registry.registerTool(delegateTool);
|
||||
}
|
||||
|
||||
@@ -257,8 +257,7 @@ function createMockConfig(overrides: Partial<Config> = {}): Config {
|
||||
getActiveModel: () => DEFAULT_GEMINI_MODEL,
|
||||
getUseSmartEdit: () => false,
|
||||
getGeminiClient: () => null,
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getMessageBus: () => null,
|
||||
getMessageBus: () => createMockMessageBus(),
|
||||
getEnableHooks: () => false,
|
||||
getPolicyEngine: () => null,
|
||||
getExperiments: () => {},
|
||||
|
||||
@@ -148,32 +148,30 @@ export class CoreToolScheduler {
|
||||
// Use a static WeakMap to ensure we only subscribe ONCE per MessageBus instance
|
||||
// This prevents memory leaks when multiple CoreToolScheduler instances are created
|
||||
// (e.g., on every React render, or for each non-interactive tool call)
|
||||
if (this.config.getEnableMessageBusIntegration()) {
|
||||
const messageBus = this.config.getMessageBus();
|
||||
const messageBus = this.config.getMessageBus();
|
||||
|
||||
// Check if we've already subscribed a handler to this message bus
|
||||
if (!CoreToolScheduler.subscribedMessageBuses.has(messageBus)) {
|
||||
// Create a shared handler that will be used for this message bus
|
||||
const sharedHandler = (request: ToolConfirmationRequest) => {
|
||||
// When ASK_USER policy decision is made, respond with requiresUserConfirmation=true
|
||||
// to tell tools to use their legacy confirmation flow
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
messageBus.publish({
|
||||
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
|
||||
correlationId: request.correlationId,
|
||||
confirmed: false,
|
||||
requiresUserConfirmation: true,
|
||||
});
|
||||
};
|
||||
// Check if we've already subscribed a handler to this message bus
|
||||
if (!CoreToolScheduler.subscribedMessageBuses.has(messageBus)) {
|
||||
// Create a shared handler that will be used for this message bus
|
||||
const sharedHandler = (request: ToolConfirmationRequest) => {
|
||||
// When ASK_USER policy decision is made, respond with requiresUserConfirmation=true
|
||||
// to tell tools to use their legacy confirmation flow
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
messageBus.publish({
|
||||
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
|
||||
correlationId: request.correlationId,
|
||||
confirmed: false,
|
||||
requiresUserConfirmation: true,
|
||||
});
|
||||
};
|
||||
|
||||
messageBus.subscribe(
|
||||
MessageBusType.TOOL_CONFIRMATION_REQUEST,
|
||||
sharedHandler,
|
||||
);
|
||||
messageBus.subscribe(
|
||||
MessageBusType.TOOL_CONFIRMATION_REQUEST,
|
||||
sharedHandler,
|
||||
);
|
||||
|
||||
// Store the handler in the WeakMap so we don't subscribe again
|
||||
CoreToolScheduler.subscribedMessageBuses.set(messageBus, sharedHandler);
|
||||
}
|
||||
// Store the handler in the WeakMap so we don't subscribe again
|
||||
CoreToolScheduler.subscribedMessageBuses.set(messageBus, sharedHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ describe('executeToolCall', () => {
|
||||
getActiveModel: () => PREVIEW_GEMINI_MODEL,
|
||||
getUseSmartEdit: () => false,
|
||||
getGeminiClient: () => null, // No client needed for these tests
|
||||
getEnableMessageBusIntegration: () => false,
|
||||
getMessageBus: () => null,
|
||||
getPolicyEngine: () => null,
|
||||
isInteractive: () => false,
|
||||
|
||||
@@ -1128,13 +1128,6 @@
|
||||
"default": 1000,
|
||||
"type": "number"
|
||||
},
|
||||
"enableMessageBusIntegration": {
|
||||
"title": "Enable Message Bus Integration",
|
||||
"description": "Enable policy-based tool confirmation via message bus integration. When enabled, tools automatically respect policy engine decisions (ALLOW/DENY/ASK_USER) without requiring individual tool implementations.",
|
||||
"markdownDescription": "Enable policy-based tool confirmation via message bus integration. When enabled, tools automatically respect policy engine decisions (ALLOW/DENY/ASK_USER) without requiring individual tool implementations.\n\n- Category: `Tools`\n- Requires restart: `yes`\n- Default: `true`",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"enableHooks": {
|
||||
"title": "Enable Hooks System",
|
||||
"description": "Enable the hooks system for intercepting and customizing Gemini CLI behavior. When enabled, hooks configured in settings will execute at appropriate lifecycle events (BeforeTool, AfterTool, BeforeModel, etc.). Requires MessageBus integration.",
|
||||
|
||||
Reference in New Issue
Block a user