From 2212cb295d0752be508c48a4bbbb1f3b3f640bbd Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 20 Apr 2026 20:13:13 +0500 Subject: [PATCH] chat: add "Don't show again" to Autopilot and Bypass Approvals warning dialogs (#311262) * Add 'Don't show again' to autopilot/bypass approvals warning dialogs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add developer command to reset chat permission warning dialogs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Always show AutoApprove warning independently of Autopilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Extract permission storage keys to shared constants module Move AUTOPILOT_DONT_SHOW_AGAIN_KEY and AUTO_APPROVE_DONT_SHOW_AGAIN_KEY to chat/common/chatPermissionStorageKeys.ts so both chatDeveloperActions and permissionPickerActionItem import from a lightweight shared module. This avoids chatDeveloperActions pulling in heavy widget/UI dependencies at startup via the permissionPickerActionItem import. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../browser/actions/chatDeveloperActions.ts | 23 +++++++++++ .../input/permissionPickerActionItem.ts | 39 ++++++++++++++++--- .../chat/common/chatPermissionStorageKeys.ts | 9 +++++ 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/common/chatPermissionStorageKeys.ts diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts index 8f0a5ef2760..b784174eaa7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts @@ -17,6 +17,8 @@ import { ChatContextKeys } from '../../common/actions/chatContextKeys.js'; import { IChatService } from '../../common/chatService/chatService.js'; import { ILanguageModelsService } from '../../common/languageModels.js'; import { IChatWidgetService } from '../chat.js'; +import { IStorageService, StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { AUTOPILOT_DONT_SHOW_AGAIN_KEY, AUTO_APPROVE_DONT_SHOW_AGAIN_KEY } from '../../common/chatPermissionStorageKeys.js'; function uriReplacer(_key: string, value: unknown): unknown { if (URI.isUri(value)) { @@ -37,6 +39,7 @@ export function registerChatDeveloperActions() { registerAction2(InspectChatModelAction); registerAction2(InspectChatModelReferencesAction); registerAction2(ClearRecentlyUsedLanguageModelsAction); + registerAction2(ResetChatPermissionWarningDialogsAction); } function formatChatModelReferenceInspection(accessor: ServicesAccessor): string { @@ -233,3 +236,23 @@ class ClearRecentlyUsedLanguageModelsAction extends Action2 { accessor.get(ILanguageModelsService).clearRecentlyUsedList(); } } + +class ResetChatPermissionWarningDialogsAction extends Action2 { + static readonly ID = 'workbench.action.chat.resetPermissionWarningDialogs'; + + constructor() { + super({ + id: ResetChatPermissionWarningDialogsAction.ID, + title: localize2('workbench.action.chat.resetPermissionWarningDialogs.label', "Reset Permission Warning Dialogs (Autopilot, Bypass Approvals)"), + category: Categories.Developer, + f1: true, + precondition: ChatContextKeys.enabled + }); + } + + override run(accessor: ServicesAccessor): void { + const storageService = accessor.get(IStorageService); + storageService.remove(AUTOPILOT_DONT_SHOW_AGAIN_KEY, StorageScope.PROFILE); + storageService.remove(AUTO_APPROVE_DONT_SHOW_AGAIN_KEY, StorageScope.PROFILE); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts index 201d43b78d5..0e2fdbf924c 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts @@ -24,17 +24,29 @@ import { MarkdownString } from '../../../../../../base/common/htmlContent.js'; import { ChatInputPickerActionViewItem, IChatInputPickerOptions } from './chatInputPickerActionItem.js'; import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; import { URI } from '../../../../../../base/common/uri.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; // Track whether warnings have been shown this VS Code session const shownWarnings = new Set(); -function hasShownElevatedWarning(level: ChatPermissionLevel): boolean { +import { AUTOPILOT_DONT_SHOW_AGAIN_KEY, AUTO_APPROVE_DONT_SHOW_AGAIN_KEY } from '../../../common/chatPermissionStorageKeys.js'; + +function dontShowAgainKey(level: ChatPermissionLevel): string | undefined { + if (level === ChatPermissionLevel.Autopilot) { + return AUTOPILOT_DONT_SHOW_AGAIN_KEY; + } + if (level === ChatPermissionLevel.AutoApprove) { + return AUTO_APPROVE_DONT_SHOW_AGAIN_KEY; + } + return undefined; +} + +function hasShownElevatedWarning(level: ChatPermissionLevel, storageService: IStorageService): boolean { if (shownWarnings.has(level)) { return true; } - // Autopilot is stricter than AutoApprove, so confirming Autopilot - // implies the user already accepted the AutoApprove risks. - if (level === ChatPermissionLevel.AutoApprove && shownWarnings.has(ChatPermissionLevel.Autopilot)) { + const key = dontShowAgainKey(level); + if (key && storageService.getBoolean(key, StorageScope.PROFILE, false)) { return true; } return false; @@ -57,6 +69,7 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { @IConfigurationService configurationService: IConfigurationService, @IDialogService private readonly dialogService: IDialogService, @IOpenerService openerService: IOpenerService, + @IStorageService storageService: IStorageService, ) { const isAutoApprovePolicyRestricted = () => configurationService.inspect(ChatConfiguration.GlobalAutoApprove).policyValue === false; const isAutopilotEnabled = () => configurationService.getValue(ChatConfiguration.AutopilotEnabled) !== false; @@ -98,7 +111,7 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { : localize('permissions.autoApprove.description', "Auto-approve all tool calls and retry on errors"), }, run: async () => { - if (!hasShownElevatedWarning(ChatPermissionLevel.AutoApprove)) { + if (!hasShownElevatedWarning(ChatPermissionLevel.AutoApprove, storageService)) { const result = await this.dialogService.prompt({ type: Severity.Warning, message: localize('permissions.autoApprove.warning.title', "Enable Bypass Approvals?"), @@ -112,6 +125,10 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { run: () => false }, ], + checkbox: { + label: localize('permissions.warning.dontShowAgain', "Don't show again"), + checked: false, + }, custom: { icon: Codicon.warning, markdownDetails: [{ @@ -122,6 +139,9 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { if (result.result !== true) { return; } + if (result.checkboxChecked) { + storageService.store(AUTO_APPROVE_DONT_SHOW_AGAIN_KEY, true, StorageScope.PROFILE, StorageTarget.USER); + } shownWarnings.add(ChatPermissionLevel.AutoApprove); } delegate.setPermissionLevel(ChatPermissionLevel.AutoApprove); @@ -147,7 +167,7 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { : localize('permissions.autopilot.description', "Auto-approve all tool calls and continue until the task is done"), }, run: async () => { - if (!hasShownElevatedWarning(ChatPermissionLevel.Autopilot)) { + if (!hasShownElevatedWarning(ChatPermissionLevel.Autopilot, storageService)) { const result = await this.dialogService.prompt({ type: Severity.Warning, message: localize('permissions.autopilot.warning.title', "Enable Autopilot?"), @@ -161,6 +181,10 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { run: () => false }, ], + checkbox: { + label: localize('permissions.warning.dontShowAgain', "Don't show again"), + checked: false, + }, custom: { icon: Codicon.rocket, markdownDetails: [{ @@ -171,6 +195,9 @@ export class PermissionPickerActionItem extends ChatInputPickerActionViewItem { if (result.result !== true) { return; } + if (result.checkboxChecked) { + storageService.store(AUTOPILOT_DONT_SHOW_AGAIN_KEY, true, StorageScope.PROFILE, StorageTarget.USER); + } shownWarnings.add(ChatPermissionLevel.Autopilot); } delegate.setPermissionLevel(ChatPermissionLevel.Autopilot); diff --git a/src/vs/workbench/contrib/chat/common/chatPermissionStorageKeys.ts b/src/vs/workbench/contrib/chat/common/chatPermissionStorageKeys.ts new file mode 100644 index 00000000000..85cf1b8cd08 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatPermissionStorageKeys.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Storage keys for persisting the user's choice to skip the warning dialog +// across sessions when "Don't show again" is checked. +export const AUTOPILOT_DONT_SHOW_AGAIN_KEY = 'chat.permissions.autopilot.dontShowWarningAgain'; +export const AUTO_APPROVE_DONT_SHOW_AGAIN_KEY = 'chat.permissions.autoApprove.dontShowWarningAgain';