Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/chat/browser/agentHost/agentHostPermissionPickerDelegate.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Disposable, DisposableMap } from '../../../../../base/common/lifecycle.js';
7
import { derived, IObservable, IReader, observableSignal } from '../../../../../base/common/observable.js';
8
import { KNOWN_AUTO_APPROVE_VALUES, SessionConfigKey } from '../../../../../platform/agentHost/common/sessionConfigKeys.js';
9
import { SessionConfigPropertySchema } from '../../../../../platform/agentHost/common/state/protocol/commands.js';
10
import { ChatPermissionLevel, isChatPermissionLevel } from '../../../../../workbench/contrib/chat/common/constants.js';
11
import { IPermissionPickerDelegate } from '../../../../contrib/copilotChatSessions/browser/permissionPicker.js';
12
import { IAgentHostSessionsProvider, isAgentHostProvider } from '../../../../common/agentHostSessionsProvider.js';
13
import { ISessionsProvider } from '../../../../services/sessions/common/sessionsProvider.js';
14
import { ISessionsProvidersService } from '../../../../services/sessions/browser/sessionsProvidersService.js';
15
import { ISessionsManagementService } from '../../../../services/sessions/common/sessionsManagement.js';
16
17
const REQUIRED_AUTO_APPROVE_VALUE = 'default';
18
const REQUIRED_MODE_VALUE = 'interactive';
19
20
/**
21
* Returns `true` when an `autoApprove` session-config property uses the
22
* shape the unified permission picker expects: a string enum that is a
23
* subset of `default | autoApprove | autopilot` and contains at least
24
* `default`.
25
*
26
* Callers use this to decide whether to render the unified
27
* {@link PermissionPicker} (with its built-in warning dialogs, autopilot
28
* gating, and policy enforcement) or fall back to the generic per-property
29
* picker.
30
*/
31
export function isWellKnownAutoApproveSchema(schema: SessionConfigPropertySchema): boolean {
32
if (schema.type !== 'string' || !Array.isArray(schema.enum) || schema.enum.length === 0) {
33
return false;
34
}
35
if (!schema.enum.includes(REQUIRED_AUTO_APPROVE_VALUE)) {
36
return false;
37
}
38
return schema.enum.every(value => KNOWN_AUTO_APPROVE_VALUES.has(value));
39
}
40
41
/**
42
* {@link IPermissionPickerDelegate} backed by the active session's AHP
43
* `autoApprove` config property.
44
*
45
* - `currentPermissionLevel` derives from the active session's
46
* `provider.getSessionConfig(...).values.autoApprove`, recomputed when the
47
* active session changes or when any agent-host provider fires
48
* `onDidChangeSessionConfig`.
49
* - `setPermissionLevel(level)` calls `provider.setSessionConfigValue(sessionId,
50
* 'autoApprove', level)` for the active session's provider.
51
* - `isApplicable` is `true` only when the active session's `autoApprove`
52
* schema matches the well-known shape, so the picker hides itself for
53
* non-conforming agents (which fall back to the generic per-property
54
* picker) and when no agent-host session is active.
55
*/
56
export class AgentHostPermissionPickerDelegate extends Disposable implements IPermissionPickerDelegate {
57
58
/** Fires every time any agent-host provider's session config changes. */
59
private readonly _configChangedSignal = observableSignal('agentHostPermissionPicker.configChanged');
60
private readonly _providerSubscriptions = this._register(new DisposableMap<string>());
61
62
readonly currentPermissionLevel: IObservable<ChatPermissionLevel>;
63
readonly isApplicable: IObservable<boolean>;
64
65
constructor(
66
@ISessionsManagementService private readonly _sessionsManagementService: ISessionsManagementService,
67
@ISessionsProvidersService private readonly _sessionsProvidersService: ISessionsProvidersService,
68
) {
69
super();
70
71
this._watchProviders(this._sessionsProvidersService.getProviders());
72
this._register(this._sessionsProvidersService.onDidChangeProviders(e => {
73
for (const provider of e.removed) {
74
this._providerSubscriptions.deleteAndDispose(provider.id);
75
}
76
this._watchProviders(e.added);
77
this._configChangedSignal.trigger(undefined);
78
}));
79
80
this.currentPermissionLevel = derived(this, reader => this._readLevel(reader));
81
this.isApplicable = derived(this, reader => this._readIsWellKnown(reader));
82
}
83
84
setPermissionLevel(level: ChatPermissionLevel): void {
85
const session = this._sessionsManagementService.activeSession.get();
86
if (!session) {
87
return;
88
}
89
const provider = this._getProvider(session.providerId);
90
if (!provider) {
91
return;
92
}
93
provider.setSessionConfigValue(session.sessionId, SessionConfigKey.AutoApprove, level)
94
.catch(() => { /* best-effort */ });
95
}
96
97
private _readLevel(reader: IReader): ChatPermissionLevel {
98
this._configChangedSignal.read(reader);
99
const session = this._sessionsManagementService.activeSession.read(reader);
100
if (!session) {
101
return ChatPermissionLevel.Default;
102
}
103
const provider = this._getProvider(session.providerId);
104
if (!provider) {
105
return ChatPermissionLevel.Default;
106
}
107
const value = provider.getSessionConfig(session.sessionId)?.values[SessionConfigKey.AutoApprove];
108
return isChatPermissionLevel(value) ? value : ChatPermissionLevel.Default;
109
}
110
111
private _readIsWellKnown(reader: IReader): boolean {
112
this._configChangedSignal.read(reader);
113
const session = this._sessionsManagementService.activeSession.read(reader);
114
if (!session) {
115
return false;
116
}
117
const provider = this._getProvider(session.providerId);
118
if (!provider) {
119
return false;
120
}
121
const schema = provider.getSessionConfig(session.sessionId)?.schema.properties[SessionConfigKey.AutoApprove];
122
return !!schema && isWellKnownAutoApproveSchema(schema);
123
}
124
125
private _getProvider(providerId: string): IAgentHostSessionsProvider | undefined {
126
const provider = this._sessionsProvidersService.getProvider(providerId);
127
return provider && isAgentHostProvider(provider) ? provider : undefined;
128
}
129
130
private _watchProviders(providers: readonly ISessionsProvider[]): void {
131
for (const provider of providers) {
132
if (!isAgentHostProvider(provider) || this._providerSubscriptions.has(provider.id)) {
133
continue;
134
}
135
this._providerSubscriptions.set(provider.id, provider.onDidChangeSessionConfig(() => {
136
this._configChangedSignal.trigger(undefined);
137
}));
138
}
139
}
140
}
141
142
/**
143
* Returns `true` when a `mode` session-config property uses the shape the
144
* dedicated agent-host mode picker expects: a string enum that contains
145
* at least `interactive`.
146
*
147
* Callers use this to decide whether to render the dedicated mode picker
148
* (with mode-specific icons and behavior) or fall back to the generic
149
* per-property picker.
150
*/
151
export function isWellKnownModeSchema(schema: SessionConfigPropertySchema): boolean {
152
if (schema.type !== 'string' || !Array.isArray(schema.enum) || schema.enum.length === 0) {
153
return false;
154
}
155
if (!schema.enum.includes(REQUIRED_MODE_VALUE)) {
156
return false;
157
}
158
return true;
159
}
160
161
162