Path: blob/main/src/vs/sessions/contrib/agentHost/browser/agentSessionSettingsFileSystemProvider.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IJSONSchema } from '../../../../base/common/jsonSchema.js';6import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js';7import { URI } from '../../../../base/common/uri.js';8import { localize } from '../../../../nls.js';9import { ILogService } from '../../../../platform/log/common/log.js';10import { ResolveSessionConfigResult } from '../../../../platform/agentHost/common/state/protocol/commands.js';11import { SessionConfigPropertySchema } from '../../../../platform/agentHost/common/state/protocol/state.js';12import { IAgentHostSessionsProvider } from '../../../common/agentHostSessionsProvider.js';13import { ISessionsProvidersService } from '../../../services/sessions/browser/sessionsProvidersService.js';14import { ISession, toSessionId } from '../../../services/sessions/common/session.js';15import {16AbstractAgentHostConfigFileSystemProvider,17AbstractAgentHostConfigSchemaRegistrar,18AgentHostConfigPropertyFilter,19buildAgentHostConfigJsonSchema,20IAgentHostConfigLike,21IAgentHostSettingsContext,22IAgentHostSettingsLocale,23serializeAgentHostConfigDocument,24} from './agentHostSettingsShared.js';2526/** Scheme for the synthetic agent-host session settings files. */27export const AGENT_SESSION_SETTINGS_SCHEME = 'agent-session-settings';2829/**30* Build the URI used to open the settings file for an agent-host session.31*32* URI shape: `agent-session-settings://{providerId}/{resourceScheme}{resourcePath}.jsonc`33*34* - `authority` = {@link ISession.providerId} (e.g. `local-agent-host`, `agenthost-<auth>`)35* - path encodes the session's resource scheme and path so the URI can be36* parsed back into an {@link ISession.sessionId} via {@link toSessionId}37* without having to look the session up on the provider.38*/39export function agentSessionSettingsUri(session: ISession): URI {40// `resource.path` already starts with `/`, so splice it between the scheme and the `.jsonc` suffix.41return URI.from({42scheme: AGENT_SESSION_SETTINGS_SCHEME,43authority: session.providerId,44path: `/${session.resource.scheme}${session.resource.path}.jsonc`,45});46}4748interface ISessionSettingsContext extends IAgentHostSettingsContext {49/** Reconstructed {@link ISession.sessionId}. */50readonly sessionId: string;51}5253function parseSessionSettingsUri(uri: URI): ISessionSettingsContext | undefined {54if (uri.scheme !== AGENT_SESSION_SETTINGS_SCHEME) {55return undefined;56}57const providerId = uri.authority;58if (!providerId) {59return undefined;60}61// Path: /{resourceScheme}/{rawId}.jsonc62const path = uri.path.startsWith('/') ? uri.path.substring(1) : uri.path;63const firstSlash = path.indexOf('/');64if (firstSlash <= 0) {65return undefined;66}67const resourceScheme = path.substring(0, firstSlash);68let rest = path.substring(firstSlash); // includes leading '/'69const lastDot = rest.lastIndexOf('.');70if (lastDot > 0) {71rest = rest.substring(0, lastDot);72}73if (!resourceScheme || rest === '/') {74return undefined;75}76const resource = URI.from({ scheme: resourceScheme, path: rest });77return { providerId, sessionId: toSessionId(providerId, resource) };78}7980/**81* Property filter: only session-mutable, non-read-only properties are82* editable. Read-only / non-mutable properties (e.g. `isolation`, `branch`)83* are preserved in the underlying config and round-tripped on write — they84* just aren't surfaced for editing.85*/86const sessionSettingsPropertyFilter: AgentHostConfigPropertyFilter = (_key, schema) => {87const s = schema as SessionConfigPropertySchema;88return s.sessionMutable === true && s.readOnly !== true;89};9091const sessionSettingsLocale: IAgentHostSettingsLocale = {92get header() { return localize('agentSessionSettings.header', "Session settings for this agent host session."); },93get saveHint() { return localize('agentSessionSettings.saveHint', "Edit values below and save to apply. Unknown or non-mutable properties are ignored."); },94get parseError() { return localize('agentSessionSettings.parseError', "Failed to parse agent session settings as JSON."); },95get notObject() { return localize('agentSessionSettings.notObject', "Agent session settings must be a JSON object."); },96};9798/**99* Serialize the session-mutable config values for a session into a100* commented, pretty-printed JSON document.101*/102export function serializeSessionSettings(provider: IAgentHostSessionsProvider, sessionId: string): string {103return serializeAgentHostConfigDocument(provider.getSessionConfig(sessionId), sessionSettingsPropertyFilter, sessionSettingsLocale);104}105106/**107* Build a JSON schema describing the editable session-mutable, non-readOnly108* properties of an agent-host session config. The filter mirrors the one109* used by {@link serializeSessionSettings} so validation matches the file110* contents produced by this provider.111*/112export function buildSessionSettingsJsonSchema(config: ResolveSessionConfigResult): IJSONSchema {113return buildAgentHostConfigJsonSchema(config, sessionSettingsPropertyFilter);114}115116/**117* Filesystem provider serving synthetic JSONC documents that represent the118* session-mutable config values of agent-host sessions.119*/120export class AgentSessionSettingsFileSystemProvider extends AbstractAgentHostConfigFileSystemProvider<ISessionSettingsContext> {121122protected readonly _schemeLabel = AGENT_SESSION_SETTINGS_SCHEME;123protected readonly _traceTag = 'AgentSessionSettings';124protected readonly _locale = sessionSettingsLocale;125126constructor(127private readonly _schemaRegistrar: AgentSessionSettingsSchemaRegistrar,128@ISessionsProvidersService sessionsProvidersService: ISessionsProvidersService,129@ILogService logService: ILogService,130) {131super(sessionsProvidersService, logService);132}133134protected _parseUri(resource: URI): ISessionSettingsContext | undefined {135return parseSessionSettingsUri(resource);136}137138protected _serialize(provider: IAgentHostSessionsProvider, ctx: ISessionSettingsContext): string {139return serializeSessionSettings(provider, ctx.sessionId);140}141142protected _watchChanges(provider: IAgentHostSessionsProvider, ctx: ISessionSettingsContext, fire: () => void): IDisposable {143return provider.onDidChangeSessionConfig(changedSessionId => {144if (changedSessionId === ctx.sessionId) {145fire();146}147});148}149150protected _ensureSchemaRegistered(provider: IAgentHostSessionsProvider, ctx: ISessionSettingsContext): void {151const session = provider.getSessions().find(s => s.sessionId === ctx.sessionId);152if (session) {153this._schemaRegistrar.ensureRegistered(provider, session);154}155}156157protected _hasConfig(provider: IAgentHostSessionsProvider, ctx: ISessionSettingsContext): boolean {158return provider.getSessionConfig(ctx.sessionId) !== undefined;159}160161// The input is the user's full view of editable values. Dispatch as a162// replace — `replaceSessionConfig` guarantees non-editable properties163// (non-mutable or readOnly) are preserved regardless of what we send,164// and unknown keys are ignored.165protected _replaceConfig(provider: IAgentHostSessionsProvider, ctx: ISessionSettingsContext, values: Record<string, unknown>): Promise<void> {166return provider.replaceSessionConfig(ctx.sessionId, values);167}168169protected _describeForTrace(ctx: ISessionSettingsContext): string {170return `session ${ctx.sessionId}`;171}172}173174/**175* Keeps per-session JSON schemas registered so editors of the synthetic176* `agent-session-settings://…` files get completions, hover, and validation.177*/178export class AgentSessionSettingsSchemaRegistrar extends AbstractAgentHostConfigSchemaRegistrar<ISession> {179180protected _propertyFilter(): AgentHostConfigPropertyFilter {181return sessionSettingsPropertyFilter;182}183184protected _settingsUri(session: ISession): string {185return agentSessionSettingsUri(session).toString();186}187188// Schema content is served via the `vscode://schemas/...` filesystem189// provider (see `SettingsFileSystemProvider`); the JSON language client190// only knows how to fetch schema content for that scheme. The191// settings-file URI is used as the fileMatch glob so the schema is192// applied to the actual editor document.193protected _schemaId(session: ISession): string {194return `vscode://schemas/agent-session-settings/${session.providerId}/${session.resource.scheme}/${session.resource.path}.jsonc`;195}196197protected _getConfig(provider: IAgentHostSessionsProvider, session: ISession): IAgentHostConfigLike | undefined {198return provider.getSessionConfig(session.sessionId);199}200201protected _targetsForProvider(provider: IAgentHostSessionsProvider): readonly ISession[] {202return provider.getSessions();203}204205protected _observeProvider(206provider: IAgentHostSessionsProvider,207onChanged: (session: ISession) => void,208onRemoved: (session: ISession) => void,209): IDisposable {210const store = new DisposableStore();211store.add(provider.onDidChangeSessionConfig(sessionId => {212const session = provider.getSessions().find(s => s.sessionId === sessionId);213if (session) {214onChanged(session);215}216}));217store.add(provider.onDidChangeSessions(e => {218for (const removed of e.removed) {219onRemoved(removed);220}221}));222return store;223}224}225226227