Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/chat.ts
5251 views
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 { IMouseWheelEvent } from '../../../../base/browser/mouseEvent.js';
7
import { Event } from '../../../../base/common/event.js';
8
import { IDisposable } from '../../../../base/common/lifecycle.js';
9
import { URI } from '../../../../base/common/uri.js';
10
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
11
import { Selection } from '../../../../editor/common/core/selection.js';
12
import { EditDeltaInfo } from '../../../../editor/common/textModelEditSource.js';
13
import { MenuId } from '../../../../platform/actions/common/actions.js';
14
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
15
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
16
import { PreferredGroup } from '../../../services/editor/common/editorService.js';
17
import { IChatAgentAttachmentCapabilities, IChatAgentCommand, IChatAgentData } from '../common/participants/chatAgents.js';
18
import { IChatResponseModel, IChatModelInputState } from '../common/model/chatModel.js';
19
import { IChatMode } from '../common/chatModes.js';
20
import { IParsedChatRequest } from '../common/requestParser/chatParserTypes.js';
21
import { CHAT_PROVIDER_ID } from '../common/participants/chatParticipantContribTypes.js';
22
import { ChatRequestQueueKind, IChatElicitationRequest, IChatLocationData, IChatSendRequestOptions } from '../common/chatService/chatService.js';
23
import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatPendingDividerViewModel } from '../common/model/chatViewModel.js';
24
import { ChatAgentLocation, ChatModeKind } from '../common/constants.js';
25
import { ChatAttachmentModel } from './attachments/chatAttachmentModel.js';
26
import { IChatEditorOptions } from './widgetHosts/editor/chatEditor.js';
27
import { ChatInputPart } from './widget/input/chatInputPart.js';
28
import { ChatWidget, IChatWidgetContrib } from './widget/chatWidget.js';
29
import { ICodeBlockActionContext } from './widget/chatContentParts/codeBlockPart.js';
30
import { AgentSessionProviders } from './agentSessions/agentSessions.js';
31
32
/**
33
* A workspace item that can be selected in the workspace picker.
34
*/
35
export interface IWorkspacePickerItem {
36
readonly uri: URI;
37
readonly label: string;
38
readonly isFolder: boolean;
39
}
40
41
/**
42
* Delegate interface for the workspace picker.
43
* Allows consumers to get and set the target workspace for chat submissions in empty window contexts.
44
*/
45
export interface IWorkspacePickerDelegate {
46
/**
47
* Returns the list of available workspaces to select from.
48
*/
49
getWorkspaces(): IWorkspacePickerItem[];
50
/**
51
* Returns the currently selected workspace, if any.
52
*/
53
getSelectedWorkspace(): IWorkspacePickerItem | undefined;
54
/**
55
* Sets the currently selected workspace.
56
*/
57
setSelectedWorkspace(workspace: IWorkspacePickerItem | undefined): void;
58
/**
59
* Event that fires when the selected workspace changes.
60
*/
61
onDidChangeSelectedWorkspace: Event<IWorkspacePickerItem | undefined>;
62
/**
63
* Event that fires when the available workspaces change.
64
*/
65
onDidChangeWorkspaces: Event<void>;
66
/**
67
* Command ID to execute when user wants to open a new folder.
68
*/
69
openFolderCommand: string;
70
}
71
72
/**
73
* Delegate interface for the session target picker.
74
* Allows consumers to get and optionally set the active session provider.
75
*/
76
export interface ISessionTypePickerDelegate {
77
getActiveSessionProvider(): AgentSessionProviders | undefined;
78
/**
79
* Optional setter for the active session provider.
80
* When provided, the picker will call this instead of executing the openNewChatSessionInPlace command.
81
* This allows the welcome view to maintain independent state from the main chat panel.
82
*/
83
setActiveSessionProvider?(provider: AgentSessionProviders): void;
84
/**
85
* Optional getter for the pending delegation target - the target that will be used when submit is pressed.
86
*/
87
getPendingDelegationTarget?(): AgentSessionProviders | undefined;
88
/**
89
* Optional setter for the pending delegation target.
90
* When a user selects a different session provider in a non-empty chat,
91
* this stores the target for delegation on the next submit instead of immediately creating a new session.
92
*/
93
setPendingDelegationTarget?(provider: AgentSessionProviders): void;
94
/**
95
* Optional event that fires when the active session provider changes.
96
* When provided, listeners (like chatInputPart) can react to session type changes
97
* and update pickers accordingly.
98
*/
99
onDidChangeActiveSessionProvider?: Event<AgentSessionProviders>;
100
}
101
102
export const IChatWidgetService = createDecorator<IChatWidgetService>('chatWidgetService');
103
104
export interface IChatWidgetService {
105
106
readonly _serviceBrand: undefined;
107
108
/**
109
* Returns the most recently focused widget if any.
110
*
111
* ⚠️ Consider carefully if this is appropriate for your use case. If you
112
* can know what session you're interacting with, prefer {@link getWidgetBySessionResource}
113
* or similar methods to work nicely with multiple chat widgets.
114
*/
115
readonly lastFocusedWidget: IChatWidget | undefined;
116
117
readonly onDidAddWidget: Event<IChatWidget>;
118
119
/**
120
* Fires when a chat session is no longer open in any chat widget.
121
*/
122
readonly onDidBackgroundSession: Event<URI>;
123
124
/**
125
* Reveals the widget, focusing its input unless `preserveFocus` is true.
126
*/
127
reveal(widget: IChatWidget, preserveFocus?: boolean): Promise<boolean>;
128
129
/**
130
* Reveals the last active widget, or creates a new chat if necessary.
131
*/
132
revealWidget(preserveFocus?: boolean): Promise<IChatWidget | undefined>;
133
134
getAllWidgets(): ReadonlyArray<IChatWidget>;
135
getWidgetByInputUri(uri: URI): IChatWidget | undefined;
136
openSession(sessionResource: URI, target?: typeof ChatViewPaneTarget): Promise<IChatWidget | undefined>;
137
openSession(sessionResource: URI, target?: PreferredGroup, options?: IChatEditorOptions): Promise<IChatWidget | undefined>;
138
openSession(sessionResource: URI, target?: typeof ChatViewPaneTarget | PreferredGroup, options?: IChatEditorOptions): Promise<IChatWidget | undefined>;
139
140
getWidgetBySessionResource(sessionResource: URI): IChatWidget | undefined;
141
142
getWidgetsByLocations(location: ChatAgentLocation): ReadonlyArray<IChatWidget>;
143
144
/**
145
* An IChatWidget registers itself when created.
146
*/
147
register(newWidget: IChatWidget): IDisposable;
148
}
149
150
export const ChatViewPaneTarget = Symbol('ChatViewPaneTarget');
151
152
export const IQuickChatService = createDecorator<IQuickChatService>('quickChatService');
153
export interface IQuickChatService {
154
readonly _serviceBrand: undefined;
155
readonly onDidClose: Event<void>;
156
readonly enabled: boolean;
157
readonly focused: boolean;
158
/** Defined when quick chat is open */
159
readonly sessionResource?: URI;
160
toggle(options?: IQuickChatOpenOptions): void;
161
focus(): void;
162
open(options?: IQuickChatOpenOptions): void;
163
close(): void;
164
openInChatView(): void;
165
}
166
167
export interface IQuickChatOpenOptions {
168
/**
169
* The query for quick chat.
170
*/
171
query: string;
172
/**
173
* Whether the query is partial and will await more input from the user.
174
*/
175
isPartialQuery?: boolean;
176
/**
177
* An optional selection range to apply to the query text box.
178
*/
179
selection?: Selection;
180
}
181
182
export const IChatAccessibilityService = createDecorator<IChatAccessibilityService>('chatAccessibilityService');
183
export interface IChatAccessibilityService {
184
readonly _serviceBrand: undefined;
185
acceptRequest(uri: URI, skipRequestSignal?: boolean): void;
186
disposeRequest(requestId: URI): void;
187
acceptResponse(widget: ChatWidget, container: HTMLElement, response: IChatResponseViewModel | string | undefined, requestId: URI | undefined, isVoiceInput?: boolean): void;
188
acceptElicitation(message: IChatElicitationRequest): void;
189
}
190
191
export interface IChatCodeBlockInfo {
192
readonly ownerMarkdownPartId: string;
193
readonly codeBlockIndex: number;
194
readonly elementId: string;
195
readonly uri: URI | undefined;
196
readonly uriPromise: Promise<URI | undefined>;
197
codemapperUri: URI | undefined;
198
readonly chatSessionResource: URI | undefined;
199
focus(): void;
200
readonly languageId?: string | undefined;
201
readonly editDeltaInfo?: EditDeltaInfo | undefined;
202
}
203
204
export interface IChatFileTreeInfo {
205
treeDataId: string;
206
treeIndex: number;
207
focus(): void;
208
}
209
210
export type ChatTreeItem = IChatRequestViewModel | IChatResponseViewModel | IChatPendingDividerViewModel;
211
212
export interface IChatListItemRendererOptions {
213
readonly renderStyle?: 'compact' | 'minimal';
214
readonly noHeader?: boolean;
215
readonly noFooter?: boolean;
216
readonly renderDetectedCommandsWithRequest?: boolean;
217
readonly restorable?: boolean;
218
readonly editable?: boolean;
219
readonly renderTextEditsAsSummary?: (uri: URI) => boolean;
220
readonly referencesExpandedWhenEmptyResponse?: boolean | ((mode: ChatModeKind) => boolean);
221
readonly progressMessageAtBottomOfResponse?: boolean | ((mode: ChatModeKind) => boolean);
222
}
223
224
export interface IChatWidgetViewOptions {
225
autoScroll?: boolean | ((mode: ChatModeKind) => boolean);
226
renderInputOnTop?: boolean;
227
renderFollowups?: boolean;
228
renderStyle?: 'compact' | 'minimal';
229
renderInputToolbarBelowInput?: boolean;
230
supportsFileReferences?: boolean;
231
filter?: (item: ChatTreeItem) => boolean;
232
/** Action triggered when 'clear' is called on the widget. */
233
clear?: () => Promise<void>;
234
rendererOptions?: IChatListItemRendererOptions;
235
menus?: {
236
/**
237
* The menu that is inside the input editor, use for send, dictation
238
*/
239
executeToolbar?: MenuId;
240
/**
241
* The menu that next to the input editor, use for close, config etc
242
*/
243
inputSideToolbar?: MenuId;
244
/**
245
* The telemetry source for all commands of this widget
246
*/
247
telemetrySource?: string;
248
};
249
defaultElementHeight?: number;
250
editorOverflowWidgetsDomNode?: HTMLElement;
251
enableImplicitContext?: boolean;
252
enableWorkingSet?: 'explicit' | 'implicit';
253
supportsChangingModes?: boolean;
254
dndContainer?: HTMLElement;
255
defaultMode?: IChatMode;
256
/**
257
* Optional delegate for the session target picker.
258
* When provided, allows the widget to maintain independent state for the selected session type.
259
* This is useful for contexts like the welcome view where target selection should not
260
* immediately open a new session.
261
*/
262
sessionTypePickerDelegate?: ISessionTypePickerDelegate;
263
264
/**
265
* Optional delegate for the workspace picker.
266
* When provided, shows a workspace picker in the chat input allowing users to select
267
* a target workspace for their request. This is useful for empty window contexts where
268
* the user wants to send a request to a specific workspace.
269
*/
270
workspacePickerDelegate?: IWorkspacePickerDelegate;
271
272
/**
273
* Optional handler for chat submission.
274
* When provided, this handler is called before the normal input acceptance flow.
275
* If it returns true (handled), the normal submission is skipped.
276
* This is useful for contexts like the welcome view where submission should
277
* redirect to a different workspace rather than executing locally.
278
*/
279
submitHandler?: (query: string, mode: ChatModeKind) => Promise<boolean>;
280
}
281
282
export interface IChatViewViewContext {
283
viewId: string;
284
}
285
286
export function isIChatViewViewContext(context: IChatWidgetViewContext): context is IChatViewViewContext {
287
return typeof (context as IChatViewViewContext).viewId === 'string';
288
}
289
290
export interface IChatResourceViewContext {
291
isQuickChat?: boolean;
292
isInlineChat?: boolean;
293
}
294
295
export function isIChatResourceViewContext(context: IChatWidgetViewContext): context is IChatResourceViewContext {
296
return !isIChatViewViewContext(context);
297
}
298
299
export type IChatWidgetViewContext = IChatViewViewContext | IChatResourceViewContext | {};
300
301
export interface IChatAcceptInputOptions {
302
noCommandDetection?: boolean;
303
isVoiceInput?: boolean;
304
enableImplicitContext?: boolean; // defaults to true
305
// Whether to store the input to history. This defaults to 'true' if the input
306
// box's current content is being accepted, or 'false' if a specific input
307
// is being submitted to the widget.
308
storeToHistory?: boolean;
309
/**
310
* When set, queues this message to be sent after the current request completes.
311
* If Steering, also sets yieldRequested on any active request to signal it should wrap up.
312
*/
313
queue?: ChatRequestQueueKind;
314
}
315
316
export interface IChatWidgetViewModelChangeEvent {
317
readonly previousSessionResource: URI | undefined;
318
readonly currentSessionResource: URI | undefined;
319
}
320
321
export interface IChatWidget {
322
readonly domNode: HTMLElement;
323
readonly onDidChangeViewModel: Event<IChatWidgetViewModelChangeEvent>;
324
readonly onDidAcceptInput: Event<void>;
325
readonly onDidHide: Event<void>;
326
readonly onDidShow: Event<void>;
327
readonly onDidSubmitAgent: Event<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>;
328
readonly onDidChangeAgent: Event<{ agent: IChatAgentData; slashCommand?: IChatAgentCommand }>;
329
readonly onDidChangeParsedInput: Event<void>;
330
readonly onDidFocus: Event<void>;
331
readonly location: ChatAgentLocation;
332
readonly viewContext: IChatWidgetViewContext;
333
readonly viewModel: IChatViewModel | undefined;
334
readonly inputEditor: ICodeEditor;
335
readonly supportsFileReferences: boolean;
336
readonly attachmentCapabilities: IChatAgentAttachmentCapabilities;
337
readonly parsedInput: IParsedChatRequest;
338
readonly lockedAgentId: string | undefined;
339
lastSelectedAgent: IChatAgentData | undefined;
340
readonly scopedContextKeyService: IContextKeyService;
341
readonly input: ChatInputPart;
342
readonly attachmentModel: ChatAttachmentModel;
343
readonly locationData?: IChatLocationData;
344
readonly contribs: readonly IChatWidgetContrib[];
345
346
readonly supportsChangingModes: boolean;
347
348
getContrib<T extends IChatWidgetContrib>(id: string): T | undefined;
349
reveal(item: ChatTreeItem): void;
350
focus(item: ChatTreeItem): void;
351
getSibling(item: ChatTreeItem, type: 'next' | 'previous'): ChatTreeItem | undefined;
352
getFocus(): ChatTreeItem | undefined;
353
setInput(query?: string): void;
354
getInput(): string;
355
refreshParsedInput(): void;
356
logInputHistory(): void;
357
acceptInput(query?: string, options?: IChatAcceptInputOptions): Promise<IChatResponseModel | undefined>;
358
startEditing(requestId: string): void;
359
finishedEditing(completedEdit?: boolean): void;
360
rerunLastRequest(): Promise<void>;
361
setInputPlaceholder(placeholder: string): void;
362
resetInputPlaceholder(): void;
363
/**
364
* Focuses the response item in the list.
365
* @param lastFocused Focuses the most recently focused response. Otherwise, focuses the last response.
366
*/
367
focusResponseItem(lastFocused?: boolean): void;
368
focusInput(): void;
369
/**
370
* Focuses the Todos view in the chat widget.
371
* @returns Whether the operation succeeded (i.e., the Todos view was focused).
372
*/
373
focusTodosView(): boolean;
374
/**
375
* Toggles focus between the Todos view and the previous focus target in the chat widget.
376
* @returns Whether the operation succeeded (i.e., the focus was toggled).
377
*/
378
toggleTodosViewFocus(): boolean;
379
/**
380
* Focuses the question carousel in the chat widget.
381
* @returns Whether the operation succeeded (i.e., the question carousel was focused).
382
*/
383
focusQuestionCarousel(): boolean;
384
/**
385
* Toggles focus between the question carousel and the chat input.
386
* @returns Whether the operation succeeded (i.e., the focus was toggled).
387
*/
388
toggleQuestionCarouselFocus(): boolean;
389
hasInputFocus(): boolean;
390
getModeRequestOptions(): Partial<IChatSendRequestOptions>;
391
getCodeBlockInfoForEditor(uri: URI): IChatCodeBlockInfo | undefined;
392
getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[];
393
getFileTreeInfosForResponse(response: IChatResponseViewModel): IChatFileTreeInfo[];
394
getLastFocusedFileTreeForResponse(response: IChatResponseViewModel): IChatFileTreeInfo | undefined;
395
clear(): Promise<void>;
396
getViewState(): IChatModelInputState | undefined;
397
lockToCodingAgent(name: string, displayName: string, agentId?: string): void;
398
unlockFromCodingAgent(): void;
399
handleDelegationExitIfNeeded(sourceAgent: Pick<IChatAgentData, 'id' | 'name'> | undefined, targetAgent: IChatAgentData | undefined): Promise<void>;
400
401
delegateScrollFromMouseWheelEvent(event: IMouseWheelEvent): void;
402
}
403
404
405
export interface ICodeBlockActionContextProvider {
406
getCodeBlockContext(editor?: ICodeEditor): ICodeBlockActionContext | undefined;
407
}
408
409
export const IChatCodeBlockContextProviderService = createDecorator<IChatCodeBlockContextProviderService>('chatCodeBlockContextProviderService');
410
export interface IChatCodeBlockContextProviderService {
411
readonly _serviceBrand: undefined;
412
readonly providers: ICodeBlockActionContextProvider[];
413
registerProvider(provider: ICodeBlockActionContextProvider, id: string): IDisposable;
414
}
415
416
export const ChatViewId = `workbench.panel.chat.view.${CHAT_PROVIDER_ID}`;
417
export const ChatViewContainerId = 'workbench.panel.chat';
418
419