feat(policy): add --policy flag for user defined policies (#18500)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Allen Hutchison
2026-02-12 16:25:23 -08:00
committed by GitHub
parent 5b4884692b
commit 696198be87
13 changed files with 326 additions and 49 deletions

View File

@@ -141,6 +141,10 @@ vi.mock('@google/gemini-cli-core', async () => {
defaultDecision: ServerConfig.PolicyDecision.ASK_USER,
approvalMode: ServerConfig.ApprovalMode.DEFAULT,
})),
getAdminErrorMessage: vi.fn(
(_feature) =>
`YOLO mode is disabled by your administrator. To enable it, please request an update to the settings at: https://goo.gle/manage-gemini-cli`,
),
isHeadlessMode: vi.fn((opts) => {
if (process.env['VITEST'] === 'true') {
return (
@@ -3192,6 +3196,26 @@ describe('Policy Engine Integration in loadCliConfig', () => {
expect.anything(),
);
});
it('should pass user-provided policy paths from --policy flag to createPolicyEngineConfig', async () => {
process.argv = [
'node',
'script.js',
'--policy',
'/path/to/policy1.toml,/path/to/policy2.toml',
];
const settings = createTestMergedSettings();
const argv = await parseArguments(settings);
await loadCliConfig(settings, 'test-session', argv);
expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith(
expect.objectContaining({
policyPaths: ['/path/to/policy1.toml', '/path/to/policy2.toml'],
}),
expect.anything(),
);
});
});
describe('loadCliConfig disableYoloMode', () => {

View File

@@ -75,6 +75,7 @@ export interface CliArgs {
yolo: boolean | undefined;
approvalMode: string | undefined;
policy: string[] | undefined;
allowedMcpServerNames: string[] | undefined;
allowedTools: string[] | undefined;
experimentalAcp: boolean | undefined;
@@ -158,6 +159,21 @@ export async function parseArguments(
description:
'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools), plan (read-only mode)',
})
.option('policy', {
type: 'array',
string: true,
nargs: 1,
description:
'Additional policy files or directories to load (comma-separated or multiple --policy)',
coerce: (policies: string[]) =>
// Handle comma-separated values
policies.flatMap((p) =>
p
.split(',')
.map((s) => s.trim())
.filter(Boolean),
),
})
.option('experimental-acp', {
type: 'boolean',
description: 'Starts the agent in ACP mode',
@@ -670,6 +686,7 @@ export async function loadCliConfig(
...settings.mcp,
allowed: argv.allowedMcpServerNames ?? settings.mcp?.allowed,
},
policyPaths: argv.policy,
};
const policyEngineConfig = await createPolicyEngineConfig(

View File

@@ -25,6 +25,7 @@ export async function createPolicyEngineConfig(
mcp: settings.mcp,
tools: settings.tools,
mcpServers: settings.mcpServers,
policyPaths: settings.policyPaths,
};
return createCorePolicyEngineConfig(policySettings, approvalMode);

View File

@@ -152,6 +152,18 @@ const SETTINGS_SCHEMA = {
},
},
policyPaths: {
type: 'array',
label: 'Policy Paths',
category: 'Advanced',
requiresRestart: true,
default: [] as string[],
description: 'Additional policy files or directories to load.',
showInDialog: false,
items: { type: 'string' },
mergeStrategy: MergeStrategy.UNION,
},
general: {
type: 'object',
label: 'General',

View File

@@ -464,6 +464,7 @@ describe('gemini.tsx main function kitty protocol', () => {
query: undefined,
yolo: undefined,
approvalMode: undefined,
policy: undefined,
allowedMcpServerNames: undefined,
allowedTools: undefined,
experimentalAcp: undefined,

View File

@@ -14,7 +14,18 @@ import type { ExtensionUpdateAction } from '../state/extensions.js';
*/
export function createNonInteractiveUI(): CommandContext['ui'] {
return {
addItem: (_item, _timestamp) => 0,
addItem: (item, _timestamp) => {
if ('text' in item && item.text) {
if (item.type === 'error') {
process.stderr.write(`Error: ${item.text}\n`);
} else if (item.type === 'warning') {
process.stderr.write(`Warning: ${item.text}\n`);
} else if (item.type === 'info') {
process.stdout.write(`${item.text}\n`);
}
}
return 0;
},
clear: () => {},
setDebugMessage: (_message) => {},
loadHistory: (_newHistory) => {},