From d4d37b83e07b67086eddfbde9da8900163ecfb67 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 26 Jan 2026 17:05:36 +0100 Subject: [PATCH] Add proposed API support for agent sessions workspace (#290385) * Add proposed API support for agent sessions workspace * add access to agentSessionsWorkspace proposed api --- extensions/vscode-api-tests/package.json | 3 ++- .../common/extensionsApiProposals.ts | 3 +++ src/vs/platform/workspace/common/workspace.ts | 13 ++++++++++++- .../api/browser/mainThreadWorkspace.ts | 3 ++- .../workbench/api/common/extHost.api.impl.ts | 4 ++++ .../workbench/api/common/extHostWorkspace.ts | 14 +++++++++----- src/vs/workbench/browser/contextkeys.ts | 2 +- .../userDataProfile/browser/userDataProfile.ts | 6 +++--- .../browser/configurationService.ts | 5 +++-- .../environment/common/environmentService.ts | 1 - .../electron-browser/environmentService.ts | 8 +------- .../browser/webWorkerExtensionHost.ts | 3 ++- .../extensions/common/extensionHostProtocol.ts | 1 + .../localProcessExtensionHost.ts | 3 ++- ...vscode.proposed.agentSessionsWorkspace.d.ts | 18 ++++++++++++++++++ 15 files changed, 63 insertions(+), 24 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.agentSessionsWorkspace.d.ts diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index ecdc5446dd3..c3c08b17c5b 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -54,7 +54,8 @@ "workspaceTrust", "inlineCompletionsAdditions", "devDeviceId", - "languageModelProxy" + "languageModelProxy", + "agentSessionsWorkspace" ], "private": true, "activationEvents": [], diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 32b28f51a86..bfd1f278c35 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -9,6 +9,9 @@ const _allApiProposals = { activeComment: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.activeComment.d.ts', }, + agentSessionsWorkspace: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.agentSessionsWorkspace.d.ts', + }, aiRelatedInformation: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts', }, diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index d46996a9210..c45fcaa00c0 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -281,6 +281,11 @@ export interface IWorkspace { * the location of the workspace configuration */ readonly configuration?: URI | null; + + /** + * Whether this workspace is an agent sessions workspace. + */ + readonly isAgentSessionsWorkspace?: boolean; } export function isWorkspace(thing: unknown): thing is IWorkspace { @@ -344,6 +349,7 @@ export class Workspace implements IWorkspace { private _transient: boolean, private _configuration: URI | null, private ignorePathCasing: (key: URI) => boolean, + private _isAgentSessionsWorkspace?: boolean, ) { this.foldersMap = TernarySearchTree.forUris(this.ignorePathCasing, () => true); this.folders = folders; @@ -354,6 +360,7 @@ export class Workspace implements IWorkspace { this._configuration = workspace.configuration; this._transient = workspace.transient; this.ignorePathCasing = workspace.ignorePathCasing; + this._isAgentSessionsWorkspace = workspace.isAgentSessionsWorkspace; this.folders = workspace.folders; } @@ -373,6 +380,10 @@ export class Workspace implements IWorkspace { this._configuration = configuration; } + get isAgentSessionsWorkspace(): boolean | undefined { + return this._isAgentSessionsWorkspace; + } + getFolder(resource: URI): IWorkspaceFolder | null { if (!resource) { return null; @@ -389,7 +400,7 @@ export class Workspace implements IWorkspace { } toJSON(): IWorkspace { - return { id: this.id, folders: this.folders, transient: this.transient, configuration: this.configuration }; + return { id: this.id, folders: this.folders, transient: this.transient, configuration: this.configuration, isAgentSessionsWorkspace: this.isAgentSessionsWorkspace }; } } diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 2befe296032..062c43408a0 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -138,7 +138,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { folders: workspace.folders, id: workspace.id, name: this._labelService.getWorkspaceLabel(workspace), - transient: workspace.transient + transient: workspace.transient, + isAgentSessionsWorkspace: workspace.isAgentSessionsWorkspace }; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b9a14bbf744..b1179119f0b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1009,6 +1009,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I set workspaceFile(value) { throw new errors.ReadonlyError('workspaceFile'); }, + get isAgentSessionsWorkspace() { + checkProposedApiEnabled(extension, 'agentSessionsWorkspace'); + return extHostWorkspace.isAgentSessionsWorkspace; + }, updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => { return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd); }, diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 00f258e8df2..89b4ecf179a 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -95,7 +95,7 @@ class ExtHostWorkspaceImpl extends Workspace { return { workspace: null, added: [], removed: [] }; } - const { id, name, folders, configuration, transient, isUntitled } = data; + const { id, name, folders, configuration, transient, isUntitled, isAgentSessionsWorkspace } = data; const newWorkspaceFolders: vscode.WorkspaceFolder[] = []; // If we have an existing workspace, we try to find the folders that match our @@ -123,7 +123,7 @@ class ExtHostWorkspaceImpl extends Workspace { // make sure to restore sort order based on index newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1); - const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, !!transient, configuration ? URI.revive(configuration) : null, !!isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)); + const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, !!transient, configuration ? URI.revive(configuration) : null, !!isUntitled, !!isAgentSessionsWorkspace, uri => ignorePathCasing(uri, extHostFileSystemInfo)); const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri, extHostFileSystemInfo); return { workspace, added, removed }; @@ -143,8 +143,8 @@ class ExtHostWorkspaceImpl extends Workspace { private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; private readonly _structure: TernarySearchTree; - constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], transient: boolean, configuration: URI | null, private _isUntitled: boolean, ignorePathCasing: (key: URI) => boolean) { - super(id, folders.map(f => new WorkspaceFolder(f)), transient, configuration, ignorePathCasing); + constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], transient: boolean, configuration: URI | null, private _isUntitled: boolean, isAgentSessionsWorkspace: boolean, ignorePathCasing: (key: URI) => boolean) { + super(id, folders.map(f => new WorkspaceFolder(f)), transient, configuration, ignorePathCasing, isAgentSessionsWorkspace); this._structure = TernarySearchTree.forUris(ignorePathCasing, () => true); // setup the workspace folder data structure @@ -223,7 +223,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac this._proxy = extHostRpc.getProxy(MainContext.MainThreadWorkspace); this._messageService = extHostRpc.getProxy(MainContext.MainThreadMessageService); const data = initData.workspace; - this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], !!data.transient, data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined; + this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], !!data.transient, data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled, !!data.isAgentSessionsWorkspace, uri => ignorePathCasing(uri, extHostFileSystemInfo)) : undefined; } $initializeWorkspace(data: IWorkspaceData | null, trusted: boolean): void { @@ -246,6 +246,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac return this._actualWorkspace ? this._actualWorkspace.name : undefined; } + get isAgentSessionsWorkspace(): boolean { + return this._actualWorkspace?.isAgentSessionsWorkspace ?? false; + } + get workspaceFile(): vscode.Uri | undefined { if (this._actualWorkspace) { if (this._actualWorkspace.configuration) { diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 6e3d850fc4d..366e5759558 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -95,7 +95,7 @@ export class WorkbenchContextKeysHandler extends Disposable { this.virtualWorkspaceContext = VirtualWorkspaceContext.bindTo(this.contextKeyService); this.temporaryWorkspaceContext = TemporaryWorkspaceContext.bindTo(this.contextKeyService); this.isAgentSessionsWorkspaceContext = IsAgentSessionsWorkspaceContext.bindTo(this.contextKeyService); - this.isAgentSessionsWorkspaceContext.set(!!this.environmentService.agentSessionsWindow); + this.isAgentSessionsWorkspaceContext.set(!!this.contextService.getWorkspace().isAgentSessionsWorkspace); this.updateWorkspaceContextKeys(); // Capabilities diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index 07e1b2e70a8..5bb262b0f3e 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -78,7 +78,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements this.hasProfilesContext.set(this.userDataProfilesService.profiles.length > 1); this._register(this.userDataProfilesService.onDidChangeProfiles(e => this.hasProfilesContext.set(this.userDataProfilesService.profiles.length > 1))); - if (!this.environmentService.agentSessionsWindow) { + if (!this.workspaceContextService.getWorkspace().isAgentSessionsWorkspace) { this.registerEditor(); this.registerActions(); @@ -91,8 +91,8 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements this.reportWorkspaceProfileInfo(); - if (environmentService.options?.profileToPreview) { - lifecycleService.when(LifecyclePhase.Restored).then(() => this.handleURL(URI.revive(environmentService.options!.profileToPreview!))); + if (this.environmentService.options?.profileToPreview) { + lifecycleService.when(LifecyclePhase.Restored).then(() => this.handleURL(URI.revive(this.environmentService.options!.profileToPreview!))); } this.registerDropHandler(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 23e5563064e..be4654c4166 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -109,7 +109,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat constructor( { remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache }, - environmentService: IBrowserWorkbenchEnvironmentService, + private readonly environmentService: IBrowserWorkbenchEnvironmentService, private readonly userDataProfileService: IUserDataProfileService, private readonly userDataProfilesService: IUserDataProfilesService, private readonly fileService: IFileService, @@ -519,7 +519,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath, this.uriIdentityService.extUri); const workspaceId = workspaceIdentifier.id; - const workspace = new Workspace(workspaceId, workspaceFolders, this.workspaceConfiguration.isTransient(), workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri)); + const isAgentSessionsWorkspace = this.uriIdentityService.extUri.isEqual(workspaceConfigPath, this.environmentService.agentSessionsWorkspace); + const workspace = new Workspace(workspaceId, workspaceFolders, this.workspaceConfiguration.isTransient(), workspaceConfigPath, uri => this.uriIdentityService.extUri.ignorePathCasing(uri), isAgentSessionsWorkspace); workspace.initialized = this.workspaceConfiguration.initialized; return workspace; } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index f3c7b9fc4d0..5312892fe6f 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -36,7 +36,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly skipWelcome: boolean; readonly disableWorkspaceTrust: boolean; readonly webviewExternalEndpoint: string; - readonly agentSessionsWindow?: boolean; // --- Development readonly debugRenderer: boolean; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index b271289d59c..563753dfedc 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -13,9 +13,8 @@ import { memoize } from '../../../../base/common/decorators.js'; import { URI } from '../../../../base/common/uri.js'; import { Schemas } from '../../../../base/common/network.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; -import { isEqual, joinPath } from '../../../../base/common/resources.js'; +import { joinPath } from '../../../../base/common/resources.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { isWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js'; export const INativeWorkbenchEnvironmentService = refineServiceDecorator(IEnvironmentService); @@ -152,11 +151,6 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment @memoize get filesToWait(): IPathsToWaitFor | undefined { return this.configuration.filesToWait; } - @memoize - get agentSessionsWindow(): boolean | undefined { - return isWorkspaceIdentifier(this.configuration.workspace) && isEqual(this.configuration.workspace.configPath, this.agentSessionsWorkspace); - } - constructor( private readonly configuration: INativeWindowConfiguration, productService: IProductService diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index f8f208b8d61..a95e4ae1b50 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -324,7 +324,8 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost configuration: workspace.configuration || undefined, id: workspace.id, name: this._labelService.getWorkspaceLabel(workspace), - transient: workspace.transient + transient: workspace.transient, + isAgentSessionsWorkspace: workspace.isAgentSessionsWorkspace }, consoleForward: { includeStack: false, diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 2ccaf82a8f8..54b8c4f9e78 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -83,6 +83,7 @@ export interface IStaticWorkspaceData { transient?: boolean; configuration?: UriComponents | null; isUntitled?: boolean | null; + isAgentSessionsWorkspace?: boolean; } export interface MessagePortLike { diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 1a8a31ac4f3..6676c97550b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -495,7 +495,8 @@ export class NativeLocalProcessExtensionHost extends Disposable implements IExte id: workspace.id, name: this._labelService.getWorkspaceLabel(workspace), isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false, - transient: workspace.transient + transient: workspace.transient, + isAgentSessionsWorkspace: workspace.isAgentSessionsWorkspace }, remote: { authority: this._environmentService.remoteAuthority, diff --git a/src/vscode-dts/vscode.proposed.agentSessionsWorkspace.d.ts b/src/vscode-dts/vscode.proposed.agentSessionsWorkspace.d.ts new file mode 100644 index 00000000000..278e4ccb7ac --- /dev/null +++ b/src/vscode-dts/vscode.proposed.agentSessionsWorkspace.d.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export namespace workspace { + + /** + * Indicates whether the current workspace is an agent sessions workspace. + * + * Agent sessions workspace is a special workspace used for AI agent interactions + * where the window is dedicated to agent session management. + */ + export const isAgentSessionsWorkspace: boolean; + } +}