Skip to content

Commit abfecf5

Browse files
authored
Merge pull request microsoft#256378 from microsoft/osortega/session-provider
Chat session provider
2 parents 95f1d43 + 7b8532b commit abfecf5

File tree

9 files changed

+257
-1
lines changed

9 files changed

+257
-1
lines changed

src/vs/platform/extensions/common/extensionsApiProposals.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ const _allApiProposals = {
5353
chatReferenceDiagnostic: {
5454
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatReferenceDiagnostic.d.ts',
5555
},
56+
chatSessionsProvider: {
57+
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts',
58+
},
5659
chatStatusItem: {
5760
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatStatusItem.d.ts',
5861
},

src/vs/workbench/api/browser/extensionHost.contribution.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import './mainThreadAiEmbeddingVector.js';
9191
import './mainThreadAiSettingsSearch.js';
9292
import './mainThreadMcp.js';
9393
import './mainThreadChatStatus.js';
94+
import './mainThreadChatSessions.js';
9495
import './mainThreadDataChannels.js';
9596

9697
export class ExtensionPoints implements IWorkbenchContribution {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 { CancellationToken } from '../../../base/common/cancellation.js';
7+
import { Disposable, DisposableMap } from '../../../base/common/lifecycle.js';
8+
import { ILogService } from '../../../platform/log/common/log.js';
9+
import { IChatSessionContent, IChatSessionsProvider, IChatSessionsService } from '../../contrib/chat/common/chatSessionsService.js';
10+
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
11+
import { ExtHostContext, MainContext, MainThreadChatSessionsShape } from '../common/extHost.protocol.js';
12+
13+
@extHostNamedCustomer(MainContext.MainThreadChatSessions)
14+
export class MainThreadChatSessions extends Disposable implements MainThreadChatSessionsShape {
15+
private readonly _registrations = this._register(new DisposableMap<number>());
16+
17+
constructor(
18+
private readonly _extHostContext: IExtHostContext,
19+
@IChatSessionsService private readonly _chatSessionsService: IChatSessionsService,
20+
@ILogService private readonly _logService: ILogService,
21+
) {
22+
super();
23+
}
24+
25+
$registerChatSessionsProvider(handle: number): void {
26+
// Register the provider handle - this tracks that a provider exists
27+
const provider: IChatSessionsProvider = {
28+
provideChatSessions: (token) => this._provideChatSessionsInformation(handle, token)
29+
};
30+
this._registrations.set(handle, this._chatSessionsService.registerChatSessionsProvider(handle, provider));
31+
}
32+
33+
private async _provideChatSessionsInformation(handle: number, token: CancellationToken): Promise<IChatSessionContent[]> {
34+
const proxy = this._extHostContext.getProxy(ExtHostContext.ExtHostChatSessions);
35+
36+
try {
37+
// Get all results as an array from the RPC call
38+
return await proxy.$provideChatSessions(handle, token);
39+
} catch (error) {
40+
this._logService.error('Error providing chat sessions:', error);
41+
}
42+
return [];
43+
}
44+
45+
$unregisterChatSessionsProvider(handle: number): void {
46+
this._registrations.deleteAndDispose(handle);
47+
}
48+
}

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ import { ExtHostWebviewViews } from './extHostWebviewView.js';
112112
import { IExtHostWindow } from './extHostWindow.js';
113113
import { IExtHostWorkspace } from './extHostWorkspace.js';
114114
import { ExtHostAiSettingsSearch } from './extHostAiSettingsSearch.js';
115+
import { IExtHostChatSessions } from './extHostChatSessions.js';
115116

116117
export interface IExtensionRegistries {
117118
mine: ExtensionDescriptionRegistry;
@@ -153,6 +154,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
153154
const extHostLanguageModels = accessor.get(IExtHostLanguageModels);
154155
const extHostMcp = accessor.get(IExtHostMpcService);
155156
const extHostDataChannels = accessor.get(IExtHostDataChannels);
157+
const extHostChatSessions = accessor.get(IExtHostChatSessions);
156158

157159
// register addressable instances
158160
rpcProtocol.set(ExtHostContext.ExtHostFileSystemInfo, extHostFileSystemInfo);
@@ -172,6 +174,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
172174
rpcProtocol.set(ExtHostContext.ExtHostAuthentication, extHostAuthentication);
173175
rpcProtocol.set(ExtHostContext.ExtHostChatProvider, extHostLanguageModels);
174176
rpcProtocol.set(ExtHostContext.ExtHostDataChannels, extHostDataChannels);
177+
rpcProtocol.set(ExtHostContext.ExtHostChatSessions, extHostChatSessions);
175178

176179
// automatically create and register addressable instances
177180
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
@@ -1501,7 +1504,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
15011504
onDidDisposeChatSession: (listeners, thisArgs?, disposables?) => {
15021505
checkProposedApiEnabled(extension, 'chatParticipantPrivate');
15031506
return _asExtensionEvent(extHostChatAgents2.onDidDisposeChatSession)(listeners, thisArgs, disposables);
1504-
}
1507+
},
1508+
registerChatSessionsProvider(provider: vscode.ChatSessionsProvider) {
1509+
checkProposedApiEnabled(extension, 'chatSessionsProvider');
1510+
return extHostChatSessions.registerChatSessionsProvider(provider);
1511+
},
15051512
};
15061513

15071514
// namespace: lm

src/vs/workbench/api/common/extHost.common.services.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { ExtHostMcpService, IExtHostMpcService } from './extHostMcp.js';
3434
import { ExtHostUrls, IExtHostUrlsService } from './extHostUrls.js';
3535
import { ExtHostProgress, IExtHostProgress } from './extHostProgress.js';
3636
import { ExtHostDataChannels, IExtHostDataChannels } from './extHostDataChannels.js';
37+
import { ExtHostChatSessions, IExtHostChatSessions } from './extHostChatSessions.js';
3738

3839
registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, InstantiationType.Delayed);
3940
registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed);
@@ -64,3 +65,4 @@ registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs, InstantiationType.Eager
6465
registerSingleton(IExtHostVariableResolverProvider, ExtHostVariableResolverProviderService, InstantiationType.Eager);
6566
registerSingleton(IExtHostMpcService, ExtHostMcpService, InstantiationType.Eager);
6667
registerSingleton(IExtHostDataChannels, ExtHostDataChannels, InstantiationType.Eager);
68+
registerSingleton(IExtHostChatSessions, ExtHostChatSessions, InstantiationType.Eager);

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { ChatSessionContent } from 'vscode';
67
import { VSBuffer } from '../../../base/common/buffer.js';
78
import { CancellationToken } from '../../../base/common/cancellation.js';
89
import { IRemoteConsoleLog } from '../../../base/common/console.js';
@@ -3100,6 +3101,15 @@ export interface MainThreadChatStatusShape {
31003101
$disposeEntry(id: string): void;
31013102
}
31023103

3104+
export interface MainThreadChatSessionsShape extends IDisposable {
3105+
$registerChatSessionsProvider(handle: number): void;
3106+
$unregisterChatSessionsProvider(handle: number): void;
3107+
}
3108+
3109+
export interface ExtHostChatSessionsShape {
3110+
$provideChatSessions(handle: number, token: CancellationToken): Promise<ChatSessionContent[]>;
3111+
}
3112+
31033113
// --- proxy identifiers
31043114

31053115
export const MainContext = {
@@ -3177,6 +3187,7 @@ export const MainContext = {
31773187
MainThreadChatStatus: createProxyIdentifier<MainThreadChatStatusShape>('MainThreadChatStatus'),
31783188
MainThreadAiSettingsSearch: createProxyIdentifier<MainThreadAiSettingsSearchShape>('MainThreadAiSettingsSearch'),
31793189
MainThreadDataChannels: createProxyIdentifier<MainThreadDataChannelsShape>('MainThreadDataChannels'),
3190+
MainThreadChatSessions: createProxyIdentifier<MainThreadChatSessionsShape>('MainThreadChatSessions'),
31803191
};
31813192

31823193
export const ExtHostContext = {
@@ -3250,4 +3261,5 @@ export const ExtHostContext = {
32503261
ExtHostLocalization: createProxyIdentifier<ExtHostLocalizationShape>('ExtHostLocalization'),
32513262
ExtHostMcp: createProxyIdentifier<ExtHostMcpShape>('ExtHostMcp'),
32523263
ExtHostDataChannels: createProxyIdentifier<ExtHostDataChannelsShape>('ExtHostDataChannels'),
3264+
ExtHostChatSessions: createProxyIdentifier<ExtHostChatSessionsShape>('ExtHostChatSessions'),
32533265
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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, DisposableStore } from '../../../base/common/lifecycle.js';
7+
import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
8+
import { IExtHostRpcService } from './extHostRpcService.js';
9+
import { ExtHostChatSessionsShape, MainContext, MainThreadChatSessionsShape } from './extHost.protocol.js';
10+
import type * as vscode from 'vscode';
11+
import { ILogService } from '../../../platform/log/common/log.js';
12+
import { Proxied } from '../../services/extensions/common/proxyIdentifier.js';
13+
14+
export interface IExtHostChatSessions extends ExtHostChatSessionsShape {
15+
registerChatSessionsProvider(provider: vscode.ChatSessionsProvider): vscode.Disposable;
16+
$provideChatSessions(handle: number, token: vscode.CancellationToken): Promise<vscode.ChatSessionContent[]>;
17+
}
18+
export const IExtHostChatSessions = createDecorator<IExtHostChatSessions>('IExtHostChatSessions');
19+
20+
export class ExtHostChatSessions extends Disposable implements IExtHostChatSessions {
21+
declare _serviceBrand: undefined;
22+
23+
private readonly _proxy: Proxied<MainThreadChatSessionsShape>;
24+
private readonly _statusProviders = new Map<number, { provider: vscode.ChatSessionsProvider; disposable: DisposableStore }>();
25+
private _nextHandle = 0;
26+
27+
constructor(
28+
@IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService,
29+
@ILogService private readonly _logService: ILogService,
30+
) {
31+
super();
32+
this._proxy = this._extHostRpc.getProxy(MainContext.MainThreadChatSessions);
33+
}
34+
35+
registerChatSessionsProvider(provider: vscode.ChatSessionsProvider): vscode.Disposable {
36+
const handle = this._nextHandle++;
37+
const disposables = new DisposableStore();
38+
39+
this._statusProviders.set(handle, { provider, disposable: disposables });
40+
this._proxy.$registerChatSessionsProvider(handle);
41+
42+
return {
43+
dispose: () => {
44+
this._statusProviders.delete(handle);
45+
disposables.dispose();
46+
provider.dispose();
47+
this._proxy.$unregisterChatSessionsProvider(handle);
48+
}
49+
};
50+
}
51+
52+
async $provideChatSessions(handle: number, token: vscode.CancellationToken): Promise<vscode.ChatSessionContent[]> {
53+
const entry = this._statusProviders.get(handle);
54+
if (!entry) {
55+
this._logService.error(`No provider registered for handle ${handle}`);
56+
return [];
57+
}
58+
59+
return await entry.provider.provideChatSessions(token);
60+
}
61+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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, IDisposable } from '../../../../base/common/lifecycle.js';
7+
import { CancellationToken } from '../../../../base/common/cancellation.js';
8+
import { ILogService } from '../../../../platform/log/common/log.js';
9+
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10+
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
11+
import { IconPath } from '../../../../editor/common/languages.js';
12+
import { UriComponents } from '../../../../base/common/uri.js';
13+
14+
export interface IChatSessionContent {
15+
uri: UriComponents;
16+
label: string;
17+
iconPath?: IconPath;
18+
}
19+
20+
export interface IChatSessionsProvider {
21+
provideChatSessions(token: CancellationToken): Promise<IChatSessionContent[]>;
22+
}
23+
24+
export interface IChatSessionsService {
25+
readonly _serviceBrand: undefined;
26+
registerChatSessionsProvider(handle: number, provider: IChatSessionsProvider): IDisposable;
27+
hasChatSessionsProviders: boolean;
28+
provideChatSessions(token: CancellationToken): Promise<IChatSessionContent[]>;
29+
}
30+
31+
export const IChatSessionsService = createDecorator<IChatSessionsService>('chatSessionsService');
32+
33+
export class ChatSessionsService extends Disposable implements IChatSessionsService {
34+
readonly _serviceBrand: undefined;
35+
private _providers: Map<number, IChatSessionsProvider> = new Map();
36+
37+
constructor(
38+
@ILogService private readonly _logService: ILogService,
39+
) {
40+
super();
41+
}
42+
43+
public async provideChatSessions(token: CancellationToken): Promise<IChatSessionContent[]> {
44+
const results: IChatSessionContent[] = [];
45+
46+
// Iterate through all registered providers and collect their results
47+
for (const [handle, provider] of this._providers) {
48+
try {
49+
if (provider.provideChatSessions) {
50+
results.push(...await provider.provideChatSessions(token));
51+
}
52+
} catch (error) {
53+
this._logService.error(`Error getting chat sessions from provider ${handle}:`, error);
54+
}
55+
56+
if (token.isCancellationRequested) {
57+
break;
58+
}
59+
}
60+
61+
return results;
62+
}
63+
64+
public registerChatSessionsProvider(handle: number, provider: IChatSessionsProvider): IDisposable {
65+
this._providers.set(handle, provider);
66+
return {
67+
dispose: () => {
68+
this._providers.delete(handle);
69+
}
70+
};
71+
}
72+
73+
public get hasChatSessionsProviders(): boolean {
74+
return this._providers.size > 0;
75+
}
76+
}
77+
78+
registerSingleton(IChatSessionsService, ChatSessionsService, InstantiationType.Delayed);
79+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
declare module 'vscode' {
7+
/**
8+
* Provides a list of chat sessions
9+
*/
10+
export interface ChatSessionsProvider extends Disposable {
11+
12+
/**
13+
* Fired when chat sessions change.
14+
*/
15+
readonly onDidChangeChatSessionContent: Event<void>;
16+
17+
/**
18+
* Provide a list of chat sessions.
19+
* */
20+
provideChatSessions(token: CancellationToken): Thenable<ChatSessionContent[]>;
21+
}
22+
23+
export interface ChatSessionContent {
24+
/**
25+
* Identifies the session
26+
* */
27+
uri: Uri;
28+
29+
/**
30+
* Human readable name of the session shown in the UI
31+
*/
32+
label: string;
33+
34+
/**
35+
* An icon for the participant shown in UI.
36+
*/
37+
iconPath?: IconPath;
38+
}
39+
40+
export namespace chat {
41+
export function registerChatSessionsProvider(provider: ChatSessionsProvider): Disposable;
42+
}
43+
}

0 commit comments

Comments
 (0)