mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-29 14:34:55 -07:00
feat: Persistent "Always Allow" policies with granular shell & MCP support (#14737)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -313,6 +313,7 @@ class EditToolInvocation
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
|
||||
if (ideConfirmation) {
|
||||
const result = await ideConfirmation;
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
BaseToolInvocation,
|
||||
Kind,
|
||||
ToolConfirmationOutcome,
|
||||
type PolicyUpdateOptions,
|
||||
} from './tools.js';
|
||||
import type { CallableTool, FunctionCall, Part } from '@google/genai';
|
||||
import { ToolErrorType } from './tool-error.js';
|
||||
@@ -87,6 +88,12 @@ class DiscoveredMCPToolInvocation extends BaseToolInvocation<
|
||||
);
|
||||
}
|
||||
|
||||
protected override getPolicyUpdateOptions(
|
||||
_outcome: ToolConfirmationOutcome,
|
||||
): PolicyUpdateOptions | undefined {
|
||||
return { mcpName: this.serverName };
|
||||
}
|
||||
|
||||
protected override async getConfirmationDetails(
|
||||
_abortSignal: AbortSignal,
|
||||
): Promise<ToolCallConfirmationDetails | false> {
|
||||
@@ -115,6 +122,9 @@ class DiscoveredMCPToolInvocation extends BaseToolInvocation<
|
||||
DiscoveredMCPToolInvocation.allowlist.add(serverAllowListKey);
|
||||
} else if (outcome === ToolConfirmationOutcome.ProceedAlwaysTool) {
|
||||
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
||||
} else if (outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave) {
|
||||
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -226,6 +226,7 @@ class MemoryToolInvocation extends BaseToolInvocation<
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
MemoryToolInvocation.allowlist.add(allowlistKey);
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
},
|
||||
};
|
||||
return confirmationDetails;
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
BaseToolInvocation,
|
||||
ToolConfirmationOutcome,
|
||||
Kind,
|
||||
type PolicyUpdateOptions,
|
||||
} from './tools.js';
|
||||
import { ApprovalMode } from '../policy/types.js';
|
||||
|
||||
@@ -83,6 +84,15 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
return description;
|
||||
}
|
||||
|
||||
protected override getPolicyUpdateOptions(
|
||||
outcome: ToolConfirmationOutcome,
|
||||
): PolicyUpdateOptions | undefined {
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave) {
|
||||
return { commandPrefix: this.params.command };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected override async getConfirmationDetails(
|
||||
_abortSignal: AbortSignal,
|
||||
): Promise<ToolCallConfirmationDetails | false> {
|
||||
@@ -124,6 +134,7 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
commandsToConfirm.forEach((command) => this.allowlist.add(command));
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
},
|
||||
};
|
||||
return confirmationDetails;
|
||||
|
||||
@@ -683,6 +683,7 @@ class EditToolInvocation
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
|
||||
if (ideConfirmation) {
|
||||
const result = await ideConfirmation;
|
||||
|
||||
@@ -65,6 +65,14 @@ export interface ToolInvocation<
|
||||
): Promise<TResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for policy updates that can be customized by tool invocations.
|
||||
*/
|
||||
export interface PolicyUpdateOptions {
|
||||
commandPrefix?: string;
|
||||
mcpName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience base class for ToolInvocation.
|
||||
*/
|
||||
@@ -112,6 +120,40 @@ export abstract class BaseToolInvocation<
|
||||
return this.getConfirmationDetails(abortSignal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tool-specific options for policy updates.
|
||||
* Subclasses can override this to provide additional options like
|
||||
* commandPrefix (for shell) or mcpName (for MCP tools).
|
||||
*/
|
||||
protected getPolicyUpdateOptions(
|
||||
_outcome: ToolConfirmationOutcome,
|
||||
): PolicyUpdateOptions | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to publish a policy update when user selects
|
||||
* ProceedAlways or ProceedAlwaysAndSave.
|
||||
*/
|
||||
protected async publishPolicyUpdate(
|
||||
outcome: ToolConfirmationOutcome,
|
||||
): Promise<void> {
|
||||
if (
|
||||
outcome === ToolConfirmationOutcome.ProceedAlways ||
|
||||
outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave
|
||||
) {
|
||||
if (this.messageBus && this._toolName) {
|
||||
const options = this.getPolicyUpdateOptions(outcome);
|
||||
await this.messageBus.publish({
|
||||
type: MessageBusType.UPDATE_POLICY,
|
||||
toolName: this._toolName,
|
||||
persist: outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should override this method to provide custom confirmation UI
|
||||
* when the policy engine's decision is 'ASK_USER'.
|
||||
@@ -129,15 +171,7 @@ export abstract class BaseToolInvocation<
|
||||
title: `Confirm: ${this._toolDisplayName || this._toolName}`,
|
||||
prompt: this.getDescription(),
|
||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
if (this.messageBus && this._toolName) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.messageBus.publish({
|
||||
type: MessageBusType.UPDATE_POLICY,
|
||||
toolName: this._toolName,
|
||||
});
|
||||
}
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
},
|
||||
};
|
||||
return confirmationDetails;
|
||||
@@ -686,6 +720,7 @@ export type ToolCallConfirmationDetails =
|
||||
export enum ToolConfirmationOutcome {
|
||||
ProceedOnce = 'proceed_once',
|
||||
ProceedAlways = 'proceed_always',
|
||||
ProceedAlwaysAndSave = 'proceed_always_and_save',
|
||||
ProceedAlwaysServer = 'proceed_always_server',
|
||||
ProceedAlwaysTool = 'proceed_always_tool',
|
||||
ModifyWithEditor = 'modify_with_editor',
|
||||
|
||||
@@ -244,6 +244,7 @@ ${textContent}
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
},
|
||||
};
|
||||
return confirmationDetails;
|
||||
|
||||
@@ -224,6 +224,7 @@ class WriteFileToolInvocation extends BaseToolInvocation<
|
||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
||||
}
|
||||
await this.publishPolicyUpdate(outcome);
|
||||
|
||||
if (ideConfirmation) {
|
||||
const result = await ideConfirmation;
|
||||
|
||||
Reference in New Issue
Block a user