chat: Explain disabled permission modes instead of hiding them #343302

Merged
rwoll opened 1:35pm on October 21, 2026 wanted to merge 918 Δ into microsoft/vscode main from
agents/enterprise-disabled-permission-modes

Pull Request Overview

  • Opened on October 21, 2026
  • Status Merged
  • Commit count 2 with first commit October 21, 2026

Total Delta

918 Total Diff Delta

Open Days

Open 9 weekdays

Test Delta

854 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
0.0 days Authoring 1 commit before pull request opened for review
 
0.0 days Awaiting first review
 
0.4 days Revising work with 1 commit in response to 5 reviews that left 19 comments
 
9.0 days Merge to deploy (still awaiting deployment)

Total time for pull request 0.4 business days from first commit authored to merge
Still awaiting deployment

Author avatar

chat: Explain disabled permission modes instead of hiding them

Users see Assisted and Allow All promoted in documentation, but enterprise restrictions currently make these options disappear. That makes an intentional restriction look like a missing feature.

Keep supported, restricted modes visible but disabled, with a clear reason and a briefcase when policy is known. Hide Assisted's "New" badge while disabled. Permission enforcement and the Harness picker are unchanged.

Test: Apply managed permissions.disableAssistedPermissionsMode: true and permissions.disableBypassPermissionsMode: "disable", then restart. Confirm both modes remain visible but cannot be selected, and Assisted has no "New" badge. Remove the restrictions and restart to confirm selection and the badge return.

🤖 Authored with GitHub Copilot on behalf of @rwoll.

Comments Threads Pending Resolution

ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

4 open issues

- Medium severity [Hard-coding `PERMISSION_LEVEL_ORDER` risks silently hiding any future `ChatPermissionLevel`…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341516956) · New
- Medium severity [The policy-disabled description is built by concatenating a non-localized `'$(briefcase) '` prefix…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517035) · New
- Low severity [Setting `tooltip: ''` unconditionally can lead to confusing empty tooltips / unintended suppression…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517096) · New
- Low severity [The `level` parameter is typed as `string`, but this helper only meaningfully supports…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517177) · New

4 resolved since last review

- Medium severity [`isApprovalLevelDisabledByKnownPolicy` doesn’t handle `ChatPermissionLevel.Autopilot`, even though…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700953)
-

ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

4 open issues

- Medium severity [`getOfferedPermissionLevels` filters strictly to `PERMISSION_LEVEL_ORDER`. If a new permission…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700685) · New
- Medium severity [`equalsUnavailablePermissionLevels` is order-sensitive. If the host/report produces the same…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700788) · New
- Medium severity [`getOfferedPermissionLevels` filters strictly to `PERMISSION_LEVEL_ORDER`. If a new permission…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700860) · New
- Medium severity [`isApprovalLevelDisabledByKnownPolicy` doesn’t handle `ChatPermissionLevel.Autopilot`, even though…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700953) · New

What changed in this PR

This PR updates the chat permissions picker UX so that supported-but-restricted permission modes remain visible as disabled rows with clear explanations (including managed-policy attribution when known), instead of disappearing and looking like missing features.

**Changes:**
- Introduces a shared “unavailable permission level” presentation (description + hover + aria text) and uses it across pickers.
- Updates permission/mode pickers to keep withheld levels visible-but-disabled, suppressing “New” while disabled and preventing stale activation.
- Adds/updates tests and docs to validate policy attribution, stale menus, refresh behavior, and accessibility help text.

| File | Description |
| ---- | ----------- |
| src/​vs/​workbench/​contrib/​chat/​test/​browser/​widget/​input/​permissionPickerActionItem.test.ts | Adds coverage for disabled-but-visible permission row...

Resolved Comment Threads

ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

4 open issues

- Medium severity [Hard-coding `PERMISSION_LEVEL_ORDER` risks silently hiding any future `ChatPermissionLevel`…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341516956) · New
- Medium severity [The policy-disabled description is built by concatenating a non-localized `'$(briefcase) '` prefix…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517035) · New
- Low severity [Setting `tooltip: ''` unconditionally can lead to confusing empty tooltips / unintended suppression…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517096) · New
- Low severity [The `level` parameter is typed as `string`, but this helper only meaningfully supports…](https://github.com/microsoft/vscode/pull/343302#discussion_r4341517177) · New

4 resolved since last review

- Medium severity [`isApprovalLevelDisabledByKnownPolicy` doesn’t handle `ChatPermissionLevel.Autopilot`, even though…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700953)
-

src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostModePickerPresentation.ts
Outdated
45
	ChatPermissionLevel.Assisted,
Unspecified Git Hub Copilot Model
46
	ChatPermissionLevel.AutoApprove,
Unspecified Git Hub Copilot Model
47
	ChatPermissionLevel.Autopilot,
Unspecified Git Hub Copilot Model
48
];
Unspecified Git Hub Copilot Model
49
Unspecified Git Hub Copilot Model
src/vs/workbench/contrib/chat/browser/widget/input/permissionPickerActionItem.ts
Outdated
260
						checked: currentLevel === level,
252 261
						enabled: !
unavailable
,
262
						inlineToggle,
263
						...(unavailable ? { description: unavailable.description, ariaDescription: unavailable.ariaDescription } : { detail: meta.detail }),
264
						tooltip: '',
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostModePickerPresentation.ts
Outdated
69
Unspecified Git Hub Copilot Model
70
/** Explains why a supported permission level cannot be selected, attributing it to policy only when policy is known. */
Unspecified Git Hub Copilot Model
71
export function getPermissionLevelUnavailablePresentation(byPolicy: boolean): IPermissionLevelUnavailablePresentation {
Unspecified Git Hub Copilot Model
72
	const description = byPolicy
Unspecified Git Hub Copilot Model
73
		? new MarkdownString('$(briefcase) ', { supportThemeIcons: true }).appendMarkdown(localize('agentHostModePicker.policyDisabled', "Disabled by policy"))
Unspecified Git Hub Copilot Model
src/vs/workbench/contrib/chat/common/agentHostConfigPolicy.ts
Outdated
39
 * Whether enterprise policy known to this client disables an approval level. Hosts report withheld levels without a
40
 * reason, so only levels proven here are attributed to policy. An explicit mode restriction replaces the legacy
41
 * global auto-approval policy, matching the managed-settings precedence the Copilot host applies.
42
 */
43
export function isApprovalLevelDisabledByKnownPolicy(level: string, configurationService: IConfigurationService, managedSettingsService: IManagedSettingsService): boolean {
ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

4 open issues

- Medium severity [`getOfferedPermissionLevels` filters strictly to `PERMISSION_LEVEL_ORDER`. If a new permission…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700685) · New
- Medium severity [`equalsUnavailablePermissionLevels` is order-sensitive. If the host/report produces the same…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700788) · New
- Medium severity [`getOfferedPermissionLevels` filters strictly to `PERMISSION_LEVEL_ORDER`. If a new permission…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700860) · New
- Medium severity [`isApprovalLevelDisabledByKnownPolicy` doesn’t handle `ChatPermissionLevel.Autopilot`, even though…](https://github.com/microsoft/vscode/pull/343302#discussion_r4340700953) · New

What changed in this PR

This PR updates the chat permissions picker UX so that supported-but-restricted permission modes remain visible as disabled rows with clear explanations (including managed-policy attribution when known), instead of disappearing and looking like missing features.

**Changes:**
- Introduces a shared “unavailable permission level” presentation (description + hover + aria text) and uses it across pickers.
- Updates permission/mode pickers to keep withheld levels visible-but-disabled, suppressing “New” while disabled and preventing stale activation.
- Adds/updates tests and docs to validate policy attribution, stale menus, refresh behavior, and accessibility help text.

| File | Description |
| ---- | ----------- |
| src/​vs/​workbench/​contrib/​chat/​test/​browser/​widget/​input/​permissionPickerActionItem.test.ts | Adds coverage for disabled-but-visible permission row...

src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostModePickerPresentation.ts
Outdated
54
	readonly byPolicy: boolean;
Unspecified Git Hub Copilot Model
55
}
Unspecified Git Hub Copilot Model
56
Unspecified Git Hub Copilot Model
57
export function equalsUnavailablePermissionLevels(a: readonly IUnavailablePermissionLevel[] | undefined, b: readonly IUnavailablePermissionLevel[] | undefined): boolean {
Unspecified Git Hub Copilot Model
58
	return equals(a, b, (x, y) => x.level === y.level && x.byPolicy === y.byPolicy);
Unspecified Git Hub Copilot Model
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostModePickerPresentation.ts
Outdated
44
	ChatPermissionLevel.Default,
Unspecified Git Hub Copilot Model
45
	ChatPermissionLevel.Assisted,
Unspecified Git Hub Copilot Model
46
	ChatPermissionLevel.AutoApprove,
Unspecified Git Hub Copilot Model
47
	ChatPermissionLevel.Autopilot,
Unspecified Git Hub Copilot Model
48
];
Unspecified Git Hub Copilot Model
src/vs/workbench/contrib/chat/common/agentHostConfigPolicy.ts
Outdated
52
			return bypass === 'disable' || legacy;
53
		default:
54
			return false;
55
	}
56
}
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostModePickerPresentation.ts
Outdated
87
}
Unspecified Git Hub Copilot Model
88
Unspecified Git Hub Copilot Model
89
/** Orders selectable and unavailable levels together; unsupported levels stay hidden. */
Unspecified Git Hub Copilot Model
90
export function getOfferedPermissionLevels(available: readonly ChatPermissionLevel[], unavailable: readonly IUnavailablePermissionLevel[] = []): readonly ChatPermissionLevel[] {
Unspecified Git Hub Copilot Model
91
	return PERMISSION_LEVEL_ORDER.filter(level => available.includes(level) || unavailable.some(entry => entry.level === level));
Unspecified Git Hub Copilot Model