Skip to content

Commit 376a64b

Browse files
committed
Merge(Auto) Commit '655ab21d8': feat(plan): add experimental 'plan' approval mode (google-gemini#16753)
2 parents af44669 + 655ab21 commit 376a64b

4 files changed

Lines changed: 82 additions & 4 deletions

File tree

docs/get-started/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,10 @@ for that specific session.
13471347
- `auto_edit`: Automatically approve edit tools (replace, write_file) while
13481348
prompting for others
13491349
- `yolo`: Automatically approve all tool calls (equivalent to `--yolo`)
1350+
- `plan`: Read-only mode for tool calls (requires experimental planning to
1351+
be enabled).
1352+
> **Note:** This mode is currently under development and not yet fully
1353+
> functional.
13501354
- Cannot be used together with `--yolo`. Use `--approval-mode=yolo` instead of
13511355
`--yolo` for the new unified approach.
13521356
- Example: `auditaria --approval-mode auto_edit`

packages/cli/src/config/config.test.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,30 @@ describe('Approval mode tool exclusion logic', () => {
10111011
expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME);
10121012
});
10131013

1014+
it('should exclude all interactive tools in non-interactive mode with plan approval mode', async () => {
1015+
process.argv = [
1016+
'node',
1017+
'script.js',
1018+
'--approval-mode',
1019+
'plan',
1020+
'-p',
1021+
'test',
1022+
];
1023+
const settings = createTestMergedSettings({
1024+
experimental: {
1025+
plan: true,
1026+
},
1027+
});
1028+
const argv = await parseArguments(createTestMergedSettings());
1029+
1030+
const config = await loadCliConfig(settings, 'test-session', argv);
1031+
1032+
const excludedTools = config.getExcludeTools();
1033+
expect(excludedTools).toContain(SHELL_TOOL_NAME);
1034+
expect(excludedTools).toContain(EDIT_TOOL_NAME);
1035+
expect(excludedTools).toContain(WRITE_FILE_TOOL_NAME);
1036+
});
1037+
10141038
it('should exclude no interactive tools in non-interactive mode with legacy yolo flag', async () => {
10151039
process.argv = ['node', 'script.js', '--yolo', '-p', 'test'];
10161040
const argv = await parseArguments(createTestMergedSettings());
@@ -1099,7 +1123,7 @@ describe('Approval mode tool exclusion logic', () => {
10991123
await expect(
11001124
loadCliConfig(settings, 'test-session', invalidArgv as CliArgs),
11011125
).rejects.toThrow(
1102-
'Invalid approval mode: invalid_mode. Valid values are: yolo, auto_edit, default',
1126+
'Invalid approval mode: invalid_mode. Valid values are: yolo, auto_edit, plan, default',
11031127
);
11041128
});
11051129
});
@@ -2052,6 +2076,42 @@ describe('loadCliConfig approval mode', () => {
20522076
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.YOLO);
20532077
});
20542078

2079+
it('should set Plan approval mode when --approval-mode=plan is used and experimental.plan is enabled', async () => {
2080+
process.argv = ['node', 'script.js', '--approval-mode', 'plan'];
2081+
const argv = await parseArguments(createTestMergedSettings());
2082+
const settings = createTestMergedSettings({
2083+
experimental: {
2084+
plan: true,
2085+
},
2086+
});
2087+
const config = await loadCliConfig(settings, 'test-session', argv);
2088+
expect(config.getApprovalMode()).toBe(ServerConfig.ApprovalMode.PLAN);
2089+
});
2090+
2091+
it('should throw error when --approval-mode=plan is used but experimental.plan is disabled', async () => {
2092+
process.argv = ['node', 'script.js', '--approval-mode', 'plan'];
2093+
const argv = await parseArguments(createTestMergedSettings());
2094+
const settings = createTestMergedSettings({
2095+
experimental: {
2096+
plan: false,
2097+
},
2098+
});
2099+
2100+
await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow(
2101+
'Approval mode "plan" is only available when experimental.plan is enabled.',
2102+
);
2103+
});
2104+
2105+
it('should throw error when --approval-mode=plan is used but experimental.plan setting is missing', async () => {
2106+
process.argv = ['node', 'script.js', '--approval-mode', 'plan'];
2107+
const argv = await parseArguments(createTestMergedSettings());
2108+
const settings = createTestMergedSettings({});
2109+
2110+
await expect(loadCliConfig(settings, 'test-session', argv)).rejects.toThrow(
2111+
'Approval mode "plan" is only available when experimental.plan is enabled.',
2112+
);
2113+
});
2114+
20552115
// --- Untrusted Folder Scenarios ---
20562116
describe('when folder is NOT trusted', () => {
20572117
beforeEach(() => {

packages/cli/src/config/config.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ export async function parseArguments(
148148
.option('approval-mode', {
149149
type: 'string',
150150
nargs: 1,
151-
choices: ['default', 'auto_edit', 'yolo'],
151+
choices: ['default', 'auto_edit', 'yolo', 'plan'],
152152
description:
153-
'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools)',
153+
'Set the approval mode: default (prompt for approval), auto_edit (auto-approve edit tools), yolo (auto-approve all tools), plan (read-only mode)',
154154
})
155155
.option('experimental-acp', {
156156
type: 'boolean',
@@ -517,12 +517,20 @@ export async function loadCliConfig(
517517
case 'auto_edit':
518518
approvalMode = ApprovalMode.AUTO_EDIT;
519519
break;
520+
case 'plan':
521+
if (!(settings.experimental?.plan ?? false)) {
522+
throw new Error(
523+
'Approval mode "plan" is only available when experimental.plan is enabled.',
524+
);
525+
}
526+
approvalMode = ApprovalMode.PLAN;
527+
break;
520528
case 'default':
521529
approvalMode = ApprovalMode.DEFAULT;
522530
break;
523531
default:
524532
throw new Error(
525-
`Invalid approval mode: ${argv.approvalMode}. Valid values are: yolo, auto_edit, default`,
533+
`Invalid approval mode: ${argv.approvalMode}. Valid values are: yolo, auto_edit, plan, default`,
526534
);
527535
}
528536
} else {
@@ -603,6 +611,11 @@ export async function loadCliConfig(
603611
);
604612

605613
switch (approvalMode) {
614+
case ApprovalMode.PLAN:
615+
// In plan non-interactive mode, all tools that require approval are excluded.
616+
// TODO(#16625): Replace this default exclusion logic with specific rules for plan mode.
617+
extraExcludes.push(...defaultExcludes.filter(toolExclusionFilter));
618+
break;
606619
case ApprovalMode.DEFAULT:
607620
// In default non-interactive mode, all tools that require approval are excluded.
608621
extraExcludes.push(...defaultExcludes.filter(toolExclusionFilter));

packages/core/src/policy/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export enum ApprovalMode {
4646
DEFAULT = 'default',
4747
AUTO_EDIT = 'autoEdit',
4848
YOLO = 'yolo',
49+
PLAN = 'plan',
4950
}
5051

5152
/**

0 commit comments

Comments
 (0)