feat(ui): Put "Allow for all future sessions" behind a setting off by default. (#15322)

This commit is contained in:
Jacob Richman
2025-12-19 09:25:23 -08:00
committed by GitHub
parent 2e229d3bb6
commit 419464a8c2
8 changed files with 216 additions and 39 deletions

View File

@@ -731,6 +731,11 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`security.enablePermanentToolApproval`** (boolean):
- **Description:** Enable the "Allow for all future sessions" option in tool
confirmation dialogs.
- **Default:** `false`
- **`security.blockGitExtensions`** (boolean):
- **Description:** Blocks installing and loading extensions from Git.
- **Default:** `false`

View File

@@ -1164,6 +1164,16 @@ const SETTINGS_SCHEMA = {
description: 'Disable YOLO mode, even if enabled by a flag.',
showInDialog: true,
},
enablePermanentToolApproval: {
type: 'boolean',
label: 'Allow Permanent Tool Approval',
category: 'Security',
requiresRestart: false,
default: false,
description:
'Enable the "Allow for all future sessions" option in tool confirmation dialogs.',
showInDialog: true,
},
blockGitExtensions: {
type: 'boolean',
label: 'Blocks extensions from Git',

View File

@@ -10,7 +10,10 @@ import type {
ToolCallConfirmationDetails,
Config,
} from '@google/gemini-cli-core';
import { renderWithProviders } from '../../../test-utils/render.js';
import {
renderWithProviders,
createMockSettings,
} from '../../../test-utils/render.js';
describe('ToolConfirmationMessage', () => {
const mockConfig = {
@@ -159,4 +162,63 @@ describe('ToolConfirmationMessage', () => {
});
});
});
describe('enablePermanentToolApproval setting', () => {
const editConfirmationDetails: ToolCallConfirmationDetails = {
type: 'edit',
title: 'Confirm Edit',
fileName: 'test.txt',
filePath: '/test.txt',
fileDiff: '...diff...',
originalContent: 'a',
newContent: 'b',
onConfirm: vi.fn(),
};
it('should NOT show "Allow for all future sessions" when setting is false (default)', () => {
const mockConfig = {
isTrustedFolder: () => true,
getIdeMode: () => false,
} as unknown as Config;
const { lastFrame } = renderWithProviders(
<ToolConfirmationMessage
confirmationDetails={editConfirmationDetails}
config={mockConfig}
availableTerminalHeight={30}
terminalWidth={80}
/>,
{
settings: createMockSettings({
security: { enablePermanentToolApproval: false },
}),
},
);
expect(lastFrame()).not.toContain('Allow for all future sessions');
});
it('should show "Allow for all future sessions" when setting is true', () => {
const mockConfig = {
isTrustedFolder: () => true,
getIdeMode: () => false,
} as unknown as Config;
const { lastFrame } = renderWithProviders(
<ToolConfirmationMessage
confirmationDetails={editConfirmationDetails}
config={mockConfig}
availableTerminalHeight={30}
terminalWidth={80}
/>,
{
settings: createMockSettings({
security: { enablePermanentToolApproval: true },
}),
},
);
expect(lastFrame()).toContain('Allow for all future sessions');
});
});
});

View File

@@ -20,6 +20,7 @@ import { MaxSizedBox } from '../shared/MaxSizedBox.js';
import { useKeypress } from '../../hooks/useKeypress.js';
import { theme } from '../../semantic-colors.js';
import { useAlternateBuffer } from '../../hooks/useAlternateBuffer.js';
import { useSettings } from '../../contexts/SettingsContext.js';
export interface ToolConfirmationMessageProps {
confirmationDetails: ToolCallConfirmationDetails;
@@ -41,6 +42,9 @@ export const ToolConfirmationMessage: React.FC<
const { onConfirm } = confirmationDetails;
const isAlternateBuffer = useAlternateBuffer();
const settings = useSettings();
const allowPermanentApproval =
settings.merged.security?.enablePermanentToolApproval ?? false;
const [ideClient, setIdeClient] = useState<IdeClient | null>(null);
const [isDiffingEnabled, setIsDiffingEnabled] = useState(false);
@@ -112,11 +116,13 @@ export const ToolConfirmationMessage: React.FC<
value: ToolConfirmationOutcome.ProceedAlways,
key: 'Allow for this session',
});
options.push({
label: 'Allow for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow for all future sessions',
});
if (allowPermanentApproval) {
options.push({
label: 'Allow for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow for all future sessions',
});
}
}
if (!config.getIdeMode() || !isDiffingEnabled) {
options.push({
@@ -147,11 +153,13 @@ export const ToolConfirmationMessage: React.FC<
value: ToolConfirmationOutcome.ProceedAlways,
key: `Allow for this session`,
});
options.push({
label: `Allow for all future sessions`,
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: `Allow for all future sessions`,
});
if (allowPermanentApproval) {
options.push({
label: `Allow for all future sessions`,
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: `Allow for all future sessions`,
});
}
}
options.push({
label: 'No, suggest changes (esc)',
@@ -171,11 +179,13 @@ export const ToolConfirmationMessage: React.FC<
value: ToolConfirmationOutcome.ProceedAlways,
key: 'Allow for this session',
});
options.push({
label: 'Allow for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow for all future sessions',
});
if (allowPermanentApproval) {
options.push({
label: 'Allow for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow for all future sessions',
});
}
}
options.push({
label: 'No, suggest changes (esc)',
@@ -202,11 +212,13 @@ export const ToolConfirmationMessage: React.FC<
value: ToolConfirmationOutcome.ProceedAlwaysServer,
key: 'Allow all server tools for this session',
});
options.push({
label: 'Allow tool for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow tool for all future sessions',
});
if (allowPermanentApproval) {
options.push({
label: 'Allow tool for all future sessions',
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: 'Allow tool for all future sessions',
});
}
}
options.push({
label: 'No, suggest changes (esc)',
@@ -327,6 +339,7 @@ export const ToolConfirmationMessage: React.FC<
availableTerminalHeight,
terminalWidth,
isAlternateBuffer,
allowPermanentApproval,
]);
if (confirmationDetails.type === 'edit') {

View File

@@ -4,7 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { renderWithProviders } from '../../../test-utils/render.js';
import {
renderWithProviders,
createMockSettings,
} from '../../../test-utils/render.js';
import { describe, it, expect, vi } from 'vitest';
import { ToolGroupMessage } from './ToolGroupMessage.js';
import type { IndividualToolCallDisplay } from '../../types.js';
@@ -376,5 +379,57 @@ describe('<ToolGroupMessage />', () => {
expect(lastFrame()).toMatchSnapshot();
unmount();
});
it('renders confirmation with permanent approval enabled', () => {
const toolCalls = [
createToolCall({
callId: 'tool-1',
name: 'confirm-tool',
status: ToolCallStatus.Confirming,
confirmationDetails: {
type: 'info',
title: 'Confirm Tool',
prompt: 'Do you want to proceed?',
onConfirm: vi.fn(),
},
}),
];
const settings = createMockSettings({
security: { enablePermanentToolApproval: true },
});
const { lastFrame, unmount } = renderWithProviders(
<ToolGroupMessage {...baseProps} toolCalls={toolCalls} />,
{ settings },
);
expect(lastFrame()).toContain('Allow for all future sessions');
expect(lastFrame()).toMatchSnapshot();
unmount();
});
it('renders confirmation with permanent approval disabled', () => {
const toolCalls = [
createToolCall({
callId: 'tool-1',
name: 'confirm-tool',
status: ToolCallStatus.Confirming,
confirmationDetails: {
type: 'info',
title: 'Confirm Tool',
prompt: 'Do you want to proceed?',
onConfirm: vi.fn(),
},
}),
];
const settings = createMockSettings({
security: { enablePermanentToolApproval: false },
});
const { lastFrame, unmount } = renderWithProviders(
<ToolGroupMessage {...baseProps} toolCalls={toolCalls} />,
{ settings },
);
expect(lastFrame()).not.toContain('Allow for all future sessions');
expect(lastFrame()).toMatchSnapshot();
unmount();
});
});
});

View File

@@ -10,8 +10,7 @@ Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
3. No, suggest changes (esc)
"
`;
@@ -22,8 +21,7 @@ Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
3. No, suggest changes (esc)
"
`;
@@ -53,9 +51,8 @@ Apply this change?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. Modify with external editor
5. No, suggest changes (esc)
3. Modify with external editor
4. No, suggest changes (esc)
"
`;
@@ -76,8 +73,7 @@ Allow execution of: 'echo'?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
3. No, suggest changes (esc)
"
`;
@@ -98,8 +94,7 @@ Do you want to proceed?
● 1. Allow once
2. Allow for this session
3. Allow for all future sessions
4. No, suggest changes (esc)
3. No, suggest changes (esc)
"
`;
@@ -123,7 +118,6 @@ Allow execution of MCP tool "test-tool" from server "test-server"?
● 1. Allow once
2. Allow tool for this session
3. Allow all server tools for this session
4. Allow tool for all future sessions
5. No, suggest changes (esc)
4. No, suggest changes (esc)
"
`;

View File

@@ -28,6 +28,39 @@ exports[`<ToolGroupMessage /> > Border Color Logic > uses yellow border when too
╰──────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > renders confirmation with permanent approval disabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ? confirm-tool A tool for testing ← │
│ │
│ Test result │
│ Do you want to proceed? │
│ │
│ Do you want to proceed? │
│ │
│ ● 1. Allow once │
│ 2. Allow for this session │
│ 3. No, suggest changes (esc) │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > renders confirmation with permanent approval enabled 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ? confirm-tool A tool for testing ← │
│ │
│ Test result │
│ Do you want to proceed? │
│ │
│ Do you want to proceed? │
│ │
│ ● 1. Allow once │
│ 2. Allow for this session │
│ 3. Allow for all future sessions │
│ 4. No, suggest changes (esc) │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`<ToolGroupMessage /> > Confirmation Handling > shows confirmation dialog for first confirming tool only 1`] = `
"╭──────────────────────────────────────────────────────────────────────────────╮
│ ? first-confirm A tool for testing ← │
@@ -39,8 +72,7 @@ exports[`<ToolGroupMessage /> > Confirmation Handling > shows confirmation dialo
│ │
│ ● 1. Allow once │
│ 2. Allow for this session │
│ 3. Allow for all future sessions
│ 4. No, suggest changes (esc) │
│ 3. No, suggest changes (esc)
│ │
│ │
│ ? second-confirm A tool for testing │
@@ -123,8 +155,7 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders tool call awaiting co
│ │
│ ● 1. Allow once │
│ 2. Allow for this session │
│ 3. Allow for all future sessions
│ 4. No, suggest changes (esc) │
│ 3. No, suggest changes (esc)
│ │
╰──────────────────────────────────────────────────────────────────────────────╯"
`;

View File

@@ -1200,6 +1200,13 @@
"default": false,
"type": "boolean"
},
"enablePermanentToolApproval": {
"title": "Allow Permanent Tool Approval",
"description": "Enable the \"Allow for all future sessions\" option in tool confirmation dialogs.",
"markdownDescription": "Enable the \"Allow for all future sessions\" option in tool confirmation dialogs.\n\n- Category: `Security`\n- Requires restart: `no`\n- Default: `false`",
"default": false,
"type": "boolean"
},
"blockGitExtensions": {
"title": "Blocks extensions from Git",
"description": "Blocks installing and loading extensions from Git.",