Skip to content

Commit 0452e0c

Browse files
committed
Merge remote-tracking branch 'origin/main' into osortega/chat-sessions-view
2 parents a5465b5 + aa876a2 commit 0452e0c

File tree

13 files changed

+407
-72
lines changed

13 files changed

+407
-72
lines changed

src/vs/workbench/api/browser/mainThreadChatSessions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat
3030
chatSessionType,
3131
provideChatSessionItems: (token) => this._provideChatSessionItems(handle, token)
3232
};
33-
this._registrations.set(handle, this._chatSessionsService.registerChatSessionItemProvider(handle, provider));
33+
this._registrations.set(handle, this._chatSessionsService.registerChatSessionItemProvider(provider));
3434
}
3535

3636
private async _provideChatSessionItems(handle: number, token: CancellationToken): Promise<IChatSessionItem[]> {

src/vs/workbench/contrib/chat/browser/actions/chatActions.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import { ChatEntitlement, IChatEntitlementService } from '../../common/chatEntit
5757
import { ChatMode, IChatMode, IChatModeService } from '../../common/chatModes.js';
5858
import { extractAgentAndCommand } from '../../common/chatParserTypes.js';
5959
import { IChatDetail, IChatService } from '../../common/chatService.js';
60-
import { IChatSessionsService } from '../../common/chatSessionsService.js';
60+
import { IChatSessionItem, IChatSessionsService } from '../../common/chatSessionsService.js';
6161
import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM } from '../../common/chatViewModel.js';
6262
import { IChatWidgetHistoryService } from '../../common/chatWidgetHistoryService.js';
6363
import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../../common/constants.js';
@@ -565,13 +565,18 @@ export function registerChatActions() {
565565
const cancellationToken = new CancellationTokenSource();
566566

567567
try {
568-
const sessions = await chatSessionsService.provideChatSessionItems(cancellationToken.token);
568+
const providers = chatSessionsService.getChatSessionProviders();
569+
const providerNSessions: { providerType: string; session: IChatSessionItem }[] = [];
569570

570-
for (const session of sessions) {
571+
for (const provider of providers) {
572+
const sessions = await chatSessionsService.provideChatSessionItems(provider.id, cancellationToken.token);
573+
providerNSessions.push(...sessions.map(session => ({ providerType: provider.id, session })));
574+
}
575+
576+
for (const session of providerNSessions) {
571577
const sessionContent = session.session;
572-
const provider = session.provider;
573578

574-
const ckey = contextKeyService.createKey('chatSessionType', provider.chatSessionType);
579+
const ckey = contextKeyService.createKey('chatSessionType', session.providerType);
575580
const actions = menuService.getMenuActions(MenuId.ChatSessionsMenu, contextKeyService);
576581
const menuActions = getContextMenuActions(actions, 'navigation');
577582
ckey.reset();
@@ -622,7 +627,7 @@ export function registerChatActions() {
622627
currentPicks.push(...agentPicks);
623628

624629
// Add "Show more..." if needed and not showing all agents
625-
if (!showAllAgents && sessions.length > 5) {
630+
if (!showAllAgents && providerNSessions.length > 5) {
626631
currentPicks.push({
627632
label: localize('chat.history.showMoreAgents', 'Show more...'),
628633
description: '',

src/vs/workbench/contrib/chat/browser/chatContentParts/chatElicitationContentPart.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export class ChatElicitationContentPart extends Disposable implements IChatConte
3232
const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, elicitation.title, elicitation.originMessage, this.getMessageToRender(elicitation), buttons, context.container));
3333
confirmationWidget.setShowButtons(elicitation.state === 'pending');
3434

35+
this._register(elicitation.onDidRequestHide(() => this.domNode.remove()));
36+
3537
this._register(confirmationWidget.onDidChangeHeight(() => this._onDidChangeHeight.fire()));
3638

3739
this._register(confirmationWidget.onDidClick(async e => {

src/vs/workbench/contrib/chat/browser/chatElicitationRequestPart.ts

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

6+
import { Emitter } from '../../../../base/common/event.js';
67
import { IMarkdownString } from '../../../../base/common/htmlContent.js';
8+
import { Disposable } from '../../../../base/common/lifecycle.js';
79
import { IChatElicitationRequest } from '../common/chatService.js';
810

9-
export class ChatElicitationRequestPart implements IChatElicitationRequest {
11+
export class ChatElicitationRequestPart extends Disposable implements IChatElicitationRequest {
1012
public readonly kind = 'elicitation';
1113
public state: 'pending' | 'accepted' | 'rejected' = 'pending';
1214
public acceptedResult?: Record<string, unknown>;
1315

16+
private _onDidRequestHide = this._register(new Emitter<void>());
17+
public readonly onDidRequestHide = this._onDidRequestHide.event;
18+
1419
constructor(
1520
public readonly title: string | IMarkdownString,
1621
public readonly message: string | IMarkdownString,
@@ -19,7 +24,13 @@ export class ChatElicitationRequestPart implements IChatElicitationRequest {
1924
public readonly rejectButtonLabel: string,
2025
public readonly accept: () => Promise<void>,
2126
public readonly reject: () => Promise<void>,
22-
) { }
27+
) {
28+
super();
29+
}
30+
31+
hide(): void {
32+
this._onDidRequestHide.fire();
33+
}
2334

2435
public toJSON() {
2536
return {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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 } from '../../../../base/common/lifecycle.js';
7+
import { localize } from '../../../../nls.js';
8+
import { ILogService } from '../../../../platform/log/common/log.js';
9+
import { Registry } from '../../../../platform/registry/common/platform.js';
10+
import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from '../../../common/contributions.js';
11+
import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js';
12+
import { ExtensionsRegistry } from '../../../services/extensions/common/extensionsRegistry.js';
13+
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
14+
import { IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js';
15+
16+
const extensionPoint = ExtensionsRegistry.registerExtensionPoint<IChatSessionsExtensionPoint[]>({
17+
extensionPoint: 'chatSessions',
18+
jsonSchema: {
19+
description: localize('chatSessionsExtPoint', 'Contributes chat session integrations to the chat widget.'),
20+
type: 'array',
21+
items: {
22+
type: 'object',
23+
properties: {
24+
id: {
25+
description: localize('chatSessionsExtPoint.id', 'A unique identifier for this item.'),
26+
type: 'string',
27+
},
28+
name: {
29+
description: localize('chatSessionsExtPoint.name', 'Name shown in the chat widget. (eg: @agent)'),
30+
type: 'string',
31+
},
32+
displayName: {
33+
description: localize('chatSessionsExtPoint.displayName', 'A longer name for this item which is used for display in menus.'),
34+
type: 'string',
35+
},
36+
description: {
37+
description: localize('chatSessionsExtPoint.description', 'Description of the chat session for use in menus and tooltips.'),
38+
type: 'string'
39+
},
40+
when: {
41+
description: localize('chatSessionsExtPoint.when', 'Condition which must be true to show this item.'),
42+
type: 'string'
43+
}
44+
},
45+
required: ['id', 'name', 'displayName', 'description'],
46+
}
47+
},
48+
activationEventsGenerator: (contribs, results) => {
49+
for (const contrib of contribs) {
50+
results.push(`onChatSession:${contrib.id}`);
51+
}
52+
}
53+
});
54+
55+
export class ChatSessionsContribution extends Disposable implements IWorkbenchContribution {
56+
constructor(
57+
@ILogService private readonly logService: ILogService,
58+
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
59+
) {
60+
super();
61+
62+
extensionPoint.setHandler(extensions => {
63+
for (const ext of extensions) {
64+
if (!isProposedApiEnabled(ext.description, 'chatSessionsProvider')) {
65+
continue;
66+
}
67+
if (!Array.isArray(ext.value)) {
68+
continue;
69+
}
70+
for (const contribution of ext.value) {
71+
const c: IChatSessionsExtensionPoint = {
72+
id: contribution.id,
73+
name: contribution.name,
74+
displayName: contribution.displayName,
75+
description: contribution.description,
76+
when: contribution.when,
77+
};
78+
this.logService.info(`Registering chat session from extension contribution: ${c.displayName} (id='${c.id}' name='${c.name}')`);
79+
this._register(this.chatSessionsService.registerContribution(c)); // TODO: Is it for contribution to own this? I think not
80+
}
81+
}
82+
});
83+
}
84+
}
85+
86+
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
87+
workbenchRegistry.registerWorkbenchContribution(ChatSessionsContribution, LifecyclePhase.Restored);

src/vs/workbench/contrib/chat/browser/chatSessions.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,10 @@ class LocalChatSessionsProvider extends Disposable implements IChatSessionItemPr
124124
private editorOrder: string[] = [];
125125

126126
constructor(
127-
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
127+
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
128128
) {
129129
super();
130+
130131
this.initializeCurrentEditorSet();
131132
this.registerEditorListeners();
132133
}
@@ -218,7 +219,6 @@ class LocalChatSessionsProvider extends Disposable implements IChatSessionItemPr
218219

219220
async provideChatSessionItems(token: CancellationToken): Promise<ILocalChatSessionItem[]> {
220221
const sessions: ILocalChatSessionItem[] = [];
221-
222222
// Create a map to quickly find editors by their key
223223
const editorMap = new Map<string, { editor: EditorInput; group: IEditorGroup }>();
224224

@@ -291,12 +291,12 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer {
291291

292292
// Create and register the local chat sessions provider
293293
this.localProvider = this._register(this.instantiationService.createInstance(LocalChatSessionsProvider));
294-
this._register(this.chatSessionsService.registerChatSessionItemProvider(0, this.localProvider));
294+
this._register(this.chatSessionsService.registerChatSessionItemProvider(this.localProvider));
295295

296296
this.updateViewRegistration();
297297

298298
// Listen for provider changes and register/unregister views accordingly
299-
this._register(this.chatSessionsService.onDidChangeProviders(() => {
299+
this._register(this.chatSessionsService.onDidChangeItemsProviders(() => {
300300
this.updateViewRegistration();
301301
}));
302302

@@ -307,8 +307,16 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer {
307307
return title;
308308
}
309309

310+
private getAllChatSessionProviders(): IChatSessionItemProvider[] {
311+
if (this.localProvider) {
312+
return [this.localProvider, ...this.chatSessionsService.getChatSessionItemProviders()];
313+
} else {
314+
return this.chatSessionsService.getChatSessionItemProviders();
315+
}
316+
}
317+
310318
private updateViewRegistration(): void {
311-
const currentProviders = this.chatSessionsService.providers;
319+
const currentProviders = this.getAllChatSessionProviders();
312320
const currentProviderIds = new Set(currentProviders.map(p => p.chatSessionType));
313321

314322
// Find views that need to be unregistered (providers that are no longer available)
@@ -334,7 +342,7 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer {
334342

335343
private async registerViews() {
336344
const container = Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).get(VIEWLET_ID);
337-
const providers = this.chatSessionsService.providers;
345+
const providers = this.getAllChatSessionProviders();
338346

339347
if (container && providers.length > 0) {
340348
const viewDescriptorsToRegister: IViewDescriptor[] = [];
@@ -345,7 +353,6 @@ class ChatSessionsViewPaneContainer extends ViewPaneContainer {
345353
if (!this.registeredViewDescriptors.has(provider.chatSessionType)) {
346354
const viewDescriptor: IViewDescriptor = {
347355
id: `${VIEWLET_ID}.${provider.chatSessionType}`,
348-
// TODO: localization?
349356
name: {
350357
value: provider.label,
351358
original: provider.label,

src/vs/workbench/contrib/chat/common/chatService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ export interface IChatElicitationRequest {
238238
acceptedResult?: Record<string, unknown>;
239239
accept(): Promise<void>;
240240
reject(): Promise<void>;
241+
onDidRequestHide: Event<void>;
241242
}
242243

243244
export interface IChatTerminalToolInvocationData {

0 commit comments

Comments
 (0)