src/vs/sessions/common/agentHostSessionsProvider.ts

313 LOC · 287 covered · 26 uncovered · 7 ranges · 3 concepts · 2 introducers · 2 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- agentHostSessionsProvider.ts ×5
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 { Event } from '../../base/common/event.js';
7 > import { IObservable } from '../../base/common/observable.js';
8 > import { equals } from '../../base/common/objects.js';
9 > import { URI } from '../../base/common/uri.js';
10 > import { AuthenticateParams, AuthenticateResult, IAgentConnection } from '../../platform/agentHost/common/agentService.js';
11 > import { RemoteAgentHostConnectionStatus } from '../../platform/agentHost/common/remoteAgentHostService.js';
12 > import { ResolveSessionConfigResult, SessionConfigValueItem } from '../../platform/agentHost/common/state/protocol/commands.js';
13 > import { AgentCustomization, Customization, McpServerStatus, RootConfigState, type McpServerState, type RootState } from '../../platform/agentHost/common/state/protocol/state.js';
14 > import { ISessionsProvider } from '../services/sessions/common/sessionsProvider.js';
15 > import { ISessionAgentRef } from '../services/sessions/common/session.js';
16 >
17 > /**
18 > * Progress emitted while an agent-host provider is establishing a connection.
19 > */
20 > export interface IAgentHostConnectProgress {
21 > readonly connectionKey: string;
22 > readonly message: string;
23 > }
24 >
25 > /**
26 > * A rich view of a single MCP server exposed by an agent host session.
27 > * Encapsulates the dispatch plumbing so consumers can present and toggle
28 > * servers without depending on the low-level protocol action surface.
29 > */
30 > export interface IAgentHostMcpServer {
31 > readonly id: string;
32 > readonly name: string;
33 > readonly enabled: boolean;
34 > readonly status: McpServerStatus;
35 > readonly state: McpServerState;
36 > readonly logOutputChannelId?: string;
37 > /** Starts or restarts the server. Providers that cannot control lifecycle may no-op. */
38 > start(): Promise<void>;
39 > /** Stops the server. Providers that cannot control lifecycle may no-op. */
40 > stop(): Promise<void>;
41 > setEnabled(enabled: boolean): void;
42 > }
43 >
44 > /**
45 > * Extended sessions provider for agent host providers (local and remote).
46 > * Adds remote connection properties and dynamic session configuration.
47 > */
48 > export interface IAgentHostSessionsProvider extends ISessionsProvider {
49 > // -- Remote Connection (optional, used by remote agent host providers) --
50 > /** Connection status observable, present on remote providers. */
51 > readonly connectionStatus?: IObservable<RemoteAgentHostConnectionStatus>;
52 > /** Progress messages during on-demand connect. */
53 > readonly onDidReportConnectProgress?: Event<IAgentHostConnectProgress>;
54 > /** Remote address string, present on remote providers. */
55 > readonly remoteAddress?: string;
56 > /**
57 > * Establish (or re-establish) the connection for this host on demand.
58 > * Tears down any existing connection first. Present on remote providers
59 > * that manage their own transport (e.g. tunnel relay); providers that
60 > * use the generic {@link IRemoteAgentHostService} reconnect flow may
61 > * leave this undefined.
62 > */
63 > connect?(): Promise<void>;
64 > /**
65 > * Tear down the active connection for this host without forgetting the
66 > * entry. A subsequent {@link connect} call should be able to re-establish
67 > * it. Present on remote providers that manage their own transport.
68 > */
69 > disconnect?(): Promise<void>;
70 >
71 > /**
72 > * When `true`, the workspace picker keeps this provider's browse
73 > * action(s) enabled even while {@link connectionStatus} reports
74 > * `disconnected` — the assumption being that clicking the action
75 > * itself triggers a connect attempt (e.g. booting a stopped WSL
76 > * distro). The `incompatible` state is still treated as unavailable
77 > * because the user can't recover from it via a click.
78 > */
79 > readonly canConnectOnDemand?: boolean;
80 >
81 > // -- Dynamic Session Config --
82 >
83 > /** Fires when dynamic configuration for a session changes. */
84 > readonly onDidChangeSessionConfig: Event<string>;
85 > /** Returns the last resolved dynamic configuration for a session. */
86 > getSessionConfig(sessionId: string): ResolveSessionConfigResult | undefined;
87 > /**
88 > * Observable: `true` while a `resolveSessionConfig` round-trip is in
89 > * flight. Pickers gate on this rather than `session.loading` so they
90 > * stay interactive in the required-values-missing state.
91 > */
92 > isSessionConfigResolving(sessionId: string): IObservable<boolean>;
93 > /** Sets one dynamic configuration property and re-resolves the schema. */
94 > setSessionConfigValue(sessionId: string, property: string, value: unknown): Promise<void>;
95 > /**
96 > * Replaces the full set of running-session config values atomically.
97 > *
98 > * Dispatches a single `session/configChanged` action with replace
99 > * semantics. Only user-editable properties (`sessionMutable: true` and
100 > * not `readOnly`) are actually replaced from the caller-supplied values —
101 > * for every other property the current value is carried through, so
102 > * non-mutable / read-only properties (e.g. `isolation`, `branch`) can
103 > * never be altered through this API even if included in the input.
104 > * Unknown keys (no schema entry) are ignored.
105 > *
106 > * No-op for pre-creation (new) sessions — use {@link setSessionConfigValue}
107 > * there since the schema is still being resolved.
108 > */
109 > replaceSessionConfig(sessionId: string, values: Record<string, unknown>): Promise<void>;
110 > /** Returns dynamic completions for a configuration property. */
111 > getSessionConfigCompletions(sessionId: string, property: string, query?: string): Promise<readonly SessionConfigValueItem[]>;
112 > /** Returns the resolved config that should be sent to createSession. */
113 > getCreateSessionConfig(sessionId: string): Record<string, unknown> | undefined;
114 > /** Clears dynamic configuration state for an abandoned new session. */
115 > clearSessionConfig(sessionId: string): void;
116 >
117 > // -- Root (agent host) Config --
118 >
119 > /** Fires when the root (agent host) configuration schema or values change. */
120 > readonly onDidChangeRootConfig: Event<void>;
121 > /** Returns the last-known root (agent host) configuration, or `undefined` if the host has not published any. */
122 > getRootConfig(): RootConfigState | undefined;
123 > getRootState(): RootState | undefined;
124 > mapAgentHostResource(uri: URI): URI;
125 > /**
126 > * Sets one root configuration property.
127 > *
128 > * Optimistically updates local state and dispatches a
129 > * `root/configChanged` action (non-replace) to the agent host.
130 > */
131 > setRootConfigValue(property: string, value: unknown): Promise<void>;
132 > /**
133 > * Replaces the full set of root configuration values atomically.
134 > *
135 > * Dispatches a single `root/configChanged` action with replace semantics.
136 > * Unknown keys (no schema entry) are ignored.
137 > */
138 > replaceRootConfig(values: Record<string, unknown>): Promise<void>;
139 >
140 > /** Authenticate against the backing agent-host connection. */
141 > authenticate(params: AuthenticateParams): Promise<AuthenticateResult>;
142 >
143 > // -- Custom Agents --
144 >
145 > /**
146 > * Fires when the effective custom-agent set for any session may have
147 > * changed (root state customizations or per-session customizations
148 > * updated). The event has no payload — consumers re-read via
149 > * {@link getCustomAgents}.
150 > */
151 > readonly onDidChangeCustomAgents: Event<void>;
152 > /**
153 > * Returns the merged, de-duped custom-agent list a session should see,
154 > * computed from root, active-client, and session customizations. Returns
155 > * an empty array when the session is unknown or no agents have been
156 > * advertised.
157 > */
158 > getCustomAgents(sessionId: string): readonly AgentCustomization[];
159 >
160 > readonly onDidChangeCustomizations: Event<void>;
161 >
162 > /**
163 > * Returns the full set of customizations.
164 > */
165 > getCustomizations(sessionId: string): readonly Customization[];
166 >
167 > /**
168 > * Returns the working directory for the session, if provided by the host.
169 > */
170 > getWorkingDirectory(sessionId: string): string | undefined;
171 >
172 > /**
173 > * Returns the MCP servers exposed by the session as rich objects whose
174 > * methods dispatch protocol-level toggle and lifecycle actions.
175 > * Returns an empty array when the session is unknown or exposes no MCP
176 > * servers.
177 > */
178 > getMcpServers(sessionId: string): readonly IAgentHostMcpServer[];
179 >
180 > /**
181 > * Set (or clear) the selected custom agent for a session. Optional so
182 > * providers that don't expose custom agents can omit it.
183 > * @param sessionId The ID of the session.
184 > * @param agent The agent to select, or `undefined` to clear the selection
185 > * and use the provider's default behavior.
186 > */
187 > setAgent?(sessionId: string, agent: ISessionAgentRef | undefined): void;
188 >
189 > /**
190 > * Returns the agent-host annotations channel for a session so that
191 > * sessions-layer features (e.g. agent feedback) can subscribe to and
192 > * dispatch annotation actions against the session's
193 > * `<sessionUri>/annotations` channel. Returns `undefined` when the
194 > * session is unknown or the host connection is unavailable.
195 > */
196 > getFeedbackAnnotationsChannel(sessionId: string): { readonly connection: IAgentConnection; readonly annotationsUri: URI } | undefined;
197 >
198 > }
199 >
200 > export const LOCAL_AGENT_HOST_PROVIDER_ID = 'local-agent-host';
201 >
202 > /**
203 > * Experimental setting id controlling whether the local agent host acts as the
204 > * default sessions provider. When enabled (and `chat.agentHost.enabled` is
205 > * true), the local agent host's session types are surfaced before those of
206 > * other providers. Defaults to `false`.
207 > */
208 > export const LocalAgentHostDefaultProviderSettingId = 'chat.agentHost.defaultSessionsProvider';
209 >
210 > export const REMOTE_AGENT_HOST_PROVIDER_PREFIX = 'agenthost-';
211 > export const REMOTE_AGENT_HOST_PROVIDER_RE = /^agenthost-/;
212 > export const ANY_AGENT_HOST_PROVIDER_RE = /^(local-agent-host|agenthost-)/;
213 >
214 > /**
215 > * Checks whether a provider is an agent host provider based on its
216 > * reserved provider ID (`local-agent-host` or `agenthost-*` prefix).
217 > */
218 > export function isAgentHostProvider(provider: ISessionsProvider): provider is IAgentHostSessionsProvider {
219 return isAgentHostProviderId(provider.id);
220 }
222 > /**
223 > * Checks whether a provider ID is for an agent host provider
224 > * (`local-agent-host` or any `agenthost-*` provider).
225 > */
226 > export function isAgentHostProviderId(providerId: string): boolean {
227 return providerId === LOCAL_AGENT_HOST_PROVIDER_ID || providerId.startsWith(REMOTE_AGENT_HOST_PROVIDER_PREFIX);
228 }
230 > /**
231 > * Structural equality for resolved session configs. Returns true when both
232 > * inputs have the same value-key set with deep-equal values and the same set
233 > * of schema property keys with identical (by-identity) property objects.
234 > * Schema property objects are compared by identity since they originate from
235 > * the same protocol snapshot in the providers that use this helper. Values
236 > * are deep-compared via {@link equals} so non-string entries (e.g. permission
237 > * objects) compare correctly.
238 > */
239 > export function resolvedConfigsEqual(a: ResolveSessionConfigResult, b: ResolveSessionConfigResult): boolean {
240 const aValueKeys = Object.keys(a.values);
241 const bValueKeys = Object.keys(b.values);
242 if (aValueKeys.length !== bValueKeys.length) {
243 return false;
244 }
245 for (const key of aValueKeys) {
246 if (!equals(a.values[key], b.values[key])) {
247 return false;
248 }
249 }
250 const aPropKeys = Object.keys(a.schema.properties);
251 const bPropKeys = Object.keys(b.schema.properties);
252 if (aPropKeys.length !== bPropKeys.length) {
253 return false;
254 }
255 for (const key of aPropKeys) {
256 if (a.schema.properties[key] !== b.schema.properties[key]) {
257 return false;
258 }
259 }
260 return true;
261 }
263 > /** Known auto-approve config values. */
264 > const AUTO_APPROVE_ENUM = ['default', 'autoApprove', 'autopilot'];
265 >
266 > type MutableConfigSchemaItem =
267 > | { type: 'string'; title: string; sessionMutable: true; enum: string[] }
268 > | { type: 'number'; title: string; sessionMutable: true }
269 > | { type: 'boolean'; title: string; sessionMutable: true }
270 > | { type: 'array'; title: string; sessionMutable: true }
271 > | { type: 'object'; title: string; sessionMutable: true };
272 >
273 > function buildMutableConfigSchemaItem(key: string, value: unknown): MutableConfigSchemaItem | undefined { agentHostSessionsProvider.ts ×2
274 > if (typeof value === 'string') {
275 > return {
276 > type: 'string',
277 > title: key,
278 > sessionMutable: true,
279 > enum: key === 'autoApprove' ? AUTO_APPROVE_ENUM : [value],
280 > };
281 > }
282 > if (typeof value === 'number') {
283 > return { type: 'number', title: key, sessionMutable: true };
284 > }
285 > if (typeof value === 'boolean') {
286 > return { type: 'boolean', title: key, sessionMutable: true };
287 > }
288 > if (Array.isArray(value)) {
289 > return { type: 'array', title: key, sessionMutable: true };
290 > }
291 > if (value && typeof value === 'object') {
292 > return { type: 'object', title: key, sessionMutable: true };
293 > }
294 > return undefined;
295 > }
297 > /**
298 > * Builds a minimal session-mutable config schema from changed values.
299 > * Used when a restored session receives a ConfigChanged action before
300 > * the full schema has been hydrated. Properties whose value type isn't
301 > * representable in the config schema (e.g. `null`, `undefined`) are
302 > * omitted.
303 > */
304 > export function buildMutableConfigSchema(config: Record<string, unknown>): Record<string, MutableConfigSchemaItem> {
305 > const properties: Record<string, MutableConfigSchemaItem> = {}; agentHostSessionsProvider.ts ×2
306 > for (const key of Object.keys(config)) {
307 > const property = buildMutableConfigSchemaItem(key, config[key]);
308 > if (property) {
309 > properties[key] = property;
310 > }
311 > }
312 > return properties;
313 > }