Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/common/chatService/chatService.ts
4780 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 { IAction } from '../../../../../base/common/actions.js';
7
import { DeferredPromise } from '../../../../../base/common/async.js';
8
import { CancellationToken } from '../../../../../base/common/cancellation.js';
9
import { Event } from '../../../../../base/common/event.js';
10
import { IMarkdownString } from '../../../../../base/common/htmlContent.js';
11
import { DisposableStore, IReference } from '../../../../../base/common/lifecycle.js';
12
import { autorun, autorunSelfDisposable, IObservable, IReader } from '../../../../../base/common/observable.js';
13
import { ThemeIcon } from '../../../../../base/common/themables.js';
14
import { hasKey } from '../../../../../base/common/types.js';
15
import { URI, UriComponents } from '../../../../../base/common/uri.js';
16
import { IRange, Range } from '../../../../../editor/common/core/range.js';
17
import { ISelection } from '../../../../../editor/common/core/selection.js';
18
import { Command, Location, TextEdit } from '../../../../../editor/common/languages.js';
19
import { FileType } from '../../../../../platform/files/common/files.js';
20
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
21
import { IAutostartResult } from '../../../mcp/common/mcpTypes.js';
22
import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
23
import { IWorkspaceSymbol } from '../../../search/common/search.js';
24
import { IChatAgentCommand, IChatAgentData, IChatAgentResult, UserSelectedTools } from '../participants/chatAgents.js';
25
import { IChatEditingSession } from '../editing/chatEditingService.js';
26
import { IChatModel, IChatRequestModeInfo, IChatRequestModel, IChatRequestVariableData, IChatResponseModel, IExportableChatData, ISerializableChatData } from '../model/chatModel.js';
27
import { IParsedChatRequest } from '../requestParser/chatParserTypes.js';
28
import { IChatParserContext } from '../requestParser/chatRequestParser.js';
29
import { IChatRequestVariableEntry } from '../attachments/chatVariableEntries.js';
30
import { IChatRequestVariableValue } from '../attachments/chatVariables.js';
31
import { ChatAgentLocation } from '../constants.js';
32
import { IPreparedToolInvocation, IToolConfirmationMessages, IToolResult, IToolResultInputOutputDetails, ToolDataSource } from '../tools/languageModelToolsService.js';
33
34
export interface IChatRequest {
35
message: string;
36
variables: Record<string, IChatRequestVariableValue[]>;
37
}
38
39
export enum ChatErrorLevel {
40
Info = 0,
41
Warning = 1,
42
Error = 2
43
}
44
45
export interface IChatResponseErrorDetailsConfirmationButton {
46
// eslint-disable-next-line @typescript-eslint/no-explicit-any
47
data: any;
48
label: string;
49
isSecondary?: boolean;
50
}
51
52
export interface IChatResponseErrorDetails {
53
message: string;
54
responseIsIncomplete?: boolean;
55
responseIsFiltered?: boolean;
56
responseIsRedacted?: boolean;
57
isQuotaExceeded?: boolean;
58
isRateLimited?: boolean;
59
level?: ChatErrorLevel;
60
confirmationButtons?: IChatResponseErrorDetailsConfirmationButton[];
61
code?: string;
62
}
63
64
export interface IChatResponseProgressFileTreeData {
65
label: string;
66
uri: URI;
67
type?: FileType;
68
children?: IChatResponseProgressFileTreeData[];
69
}
70
71
export type IDocumentContext = {
72
uri: URI;
73
version: number;
74
ranges: IRange[];
75
};
76
77
export function isIDocumentContext(obj: unknown): obj is IDocumentContext {
78
return (
79
!!obj &&
80
typeof obj === 'object' &&
81
'uri' in obj && obj.uri instanceof URI &&
82
'version' in obj && typeof obj.version === 'number' &&
83
'ranges' in obj && Array.isArray(obj.ranges) && obj.ranges.every(Range.isIRange)
84
);
85
}
86
87
export interface IChatUsedContext {
88
documents: IDocumentContext[];
89
kind: 'usedContext';
90
}
91
92
export function isIUsedContext(obj: unknown): obj is IChatUsedContext {
93
return (
94
!!obj &&
95
typeof obj === 'object' &&
96
'documents' in obj &&
97
Array.isArray(obj.documents) &&
98
obj.documents.every(isIDocumentContext)
99
);
100
}
101
102
export interface IChatContentVariableReference {
103
variableName: string;
104
value?: URI | Location;
105
}
106
107
export function isChatContentVariableReference(obj: unknown): obj is IChatContentVariableReference {
108
return !!obj &&
109
typeof obj === 'object' &&
110
typeof (obj as IChatContentVariableReference).variableName === 'string';
111
}
112
113
export enum ChatResponseReferencePartStatusKind {
114
Complete = 1,
115
Partial = 2,
116
Omitted = 3
117
}
118
119
export enum ChatResponseClearToPreviousToolInvocationReason {
120
NoReason = 0,
121
FilteredContentRetry = 1,
122
CopyrightContentRetry = 2,
123
}
124
125
export interface IChatContentReference {
126
reference: URI | Location | IChatContentVariableReference | string;
127
iconPath?: ThemeIcon | { light: URI; dark?: URI };
128
options?: {
129
status?: { description: string; kind: ChatResponseReferencePartStatusKind };
130
diffMeta?: { added: number; removed: number };
131
originalUri?: URI;
132
};
133
kind: 'reference';
134
}
135
136
export interface IChatCodeCitation {
137
value: URI;
138
license: string;
139
snippet: string;
140
kind: 'codeCitation';
141
}
142
143
export interface IChatContentInlineReference {
144
resolveId?: string;
145
inlineReference: URI | Location | IWorkspaceSymbol;
146
name?: string;
147
kind: 'inlineReference';
148
}
149
150
export interface IChatMarkdownContent {
151
kind: 'markdownContent';
152
content: IMarkdownString;
153
inlineReferences?: Record<string, IChatContentInlineReference>;
154
fromSubagent?: boolean;
155
}
156
157
export interface IChatTreeData {
158
treeData: IChatResponseProgressFileTreeData;
159
kind: 'treeData';
160
}
161
export interface IMultiDiffResource {
162
originalUri?: URI;
163
modifiedUri?: URI;
164
goToFileUri?: URI;
165
added?: number;
166
removed?: number;
167
}
168
169
export interface IChatMultiDiffInnerData {
170
title: string;
171
resources: IMultiDiffResource[];
172
}
173
174
export interface IChatMultiDiffData {
175
multiDiffData: IChatMultiDiffInnerData | IObservable<IChatMultiDiffInnerData>;
176
kind: 'multiDiffData';
177
collapsed?: boolean;
178
readOnly?: boolean;
179
}
180
181
export interface IChatMultiDiffDataSerialized {
182
multiDiffData: IChatMultiDiffInnerData;
183
kind: 'multiDiffData';
184
collapsed?: boolean;
185
readOnly?: boolean;
186
}
187
188
export class ChatMultiDiffData implements IChatMultiDiffData {
189
public readonly kind = 'multiDiffData';
190
public readonly collapsed?: boolean | undefined;
191
public readonly readOnly?: boolean | undefined;
192
public readonly multiDiffData: IChatMultiDiffData['multiDiffData'];
193
194
constructor(opts: {
195
multiDiffData: IChatMultiDiffInnerData | IObservable<IChatMultiDiffInnerData>;
196
collapsed?: boolean;
197
readOnly?: boolean;
198
}) {
199
this.readOnly = opts.readOnly;
200
this.collapsed = opts.collapsed;
201
this.multiDiffData = opts.multiDiffData;
202
}
203
204
toJSON(): IChatMultiDiffDataSerialized {
205
return {
206
kind: this.kind,
207
multiDiffData: hasKey(this.multiDiffData, { title: true }) ? this.multiDiffData : this.multiDiffData.get(),
208
collapsed: this.collapsed,
209
readOnly: this.readOnly,
210
};
211
}
212
}
213
214
export interface IChatProgressMessage {
215
content: IMarkdownString;
216
kind: 'progressMessage';
217
}
218
219
export interface IChatTask extends IChatTaskDto {
220
deferred: DeferredPromise<string | void>;
221
progress: (IChatWarningMessage | IChatContentReference)[];
222
readonly onDidAddProgress: Event<IChatWarningMessage | IChatContentReference>;
223
add(progress: IChatWarningMessage | IChatContentReference): void;
224
225
complete: (result: string | void) => void;
226
task: () => Promise<string | void>;
227
isSettled: () => boolean;
228
}
229
230
export interface IChatUndoStop {
231
kind: 'undoStop';
232
id: string;
233
}
234
235
export interface IChatExternalEditsDto {
236
kind: 'externalEdits';
237
undoStopId: string;
238
start: boolean; /** true=start, false=stop */
239
resources: UriComponents[];
240
}
241
242
export interface IChatTaskDto {
243
content: IMarkdownString;
244
kind: 'progressTask';
245
}
246
247
export interface IChatTaskSerialized {
248
content: IMarkdownString;
249
progress: (IChatWarningMessage | IChatContentReference)[];
250
kind: 'progressTaskSerialized';
251
}
252
253
export interface IChatTaskResult {
254
content: IMarkdownString | void;
255
kind: 'progressTaskResult';
256
}
257
258
export interface IChatWarningMessage {
259
content: IMarkdownString;
260
kind: 'warning';
261
}
262
263
export interface IChatAgentVulnerabilityDetails {
264
title: string;
265
description: string;
266
}
267
268
export interface IChatResponseCodeblockUriPart {
269
kind: 'codeblockUri';
270
uri: URI;
271
isEdit?: boolean;
272
undoStopId?: string;
273
}
274
275
export interface IChatAgentMarkdownContentWithVulnerability {
276
content: IMarkdownString;
277
vulnerabilities: IChatAgentVulnerabilityDetails[];
278
kind: 'markdownVuln';
279
}
280
281
export interface IChatCommandButton {
282
command: Command;
283
kind: 'command';
284
}
285
286
export interface IChatMoveMessage {
287
uri: URI;
288
range: IRange;
289
kind: 'move';
290
}
291
292
export interface IChatTextEdit {
293
uri: URI;
294
edits: TextEdit[];
295
kind: 'textEdit';
296
done?: boolean;
297
isExternalEdit?: boolean;
298
}
299
300
export interface IChatClearToPreviousToolInvocation {
301
kind: 'clearToPreviousToolInvocation';
302
reason: ChatResponseClearToPreviousToolInvocationReason;
303
}
304
305
export interface IChatNotebookEdit {
306
uri: URI;
307
edits: ICellEditOperation[];
308
kind: 'notebookEdit';
309
done?: boolean;
310
isExternalEdit?: boolean;
311
}
312
313
export interface IChatConfirmation {
314
title: string;
315
message: string | IMarkdownString;
316
// eslint-disable-next-line @typescript-eslint/no-explicit-any
317
data: any;
318
buttons?: string[];
319
isUsed?: boolean;
320
kind: 'confirmation';
321
}
322
323
export const enum ElicitationState {
324
Pending = 'pending',
325
Accepted = 'accepted',
326
Rejected = 'rejected',
327
}
328
329
export interface IChatElicitationRequest {
330
kind: 'elicitation2'; // '2' because initially serialized data used the same kind
331
title: string | IMarkdownString;
332
message: string | IMarkdownString;
333
acceptButtonLabel: string;
334
rejectButtonLabel: string | undefined;
335
subtitle?: string | IMarkdownString;
336
source?: ToolDataSource;
337
state: IObservable<ElicitationState>;
338
acceptedResult?: Record<string, unknown>;
339
moreActions?: IAction[];
340
accept(value: IAction | true): Promise<void>;
341
reject?: () => Promise<void>;
342
isHidden?: IObservable<boolean>;
343
hide?(): void;
344
}
345
346
export interface IChatElicitationRequestSerialized {
347
kind: 'elicitationSerialized';
348
title: string | IMarkdownString;
349
message: string | IMarkdownString;
350
subtitle: string | IMarkdownString | undefined;
351
source: ToolDataSource | undefined;
352
state: ElicitationState.Accepted | ElicitationState.Rejected;
353
isHidden: boolean;
354
acceptedResult?: Record<string, unknown>;
355
}
356
357
export interface IChatThinkingPart {
358
kind: 'thinking';
359
value?: string | string[];
360
id?: string;
361
// eslint-disable-next-line @typescript-eslint/no-explicit-any
362
metadata?: { readonly [key: string]: any };
363
generatedTitle?: string;
364
}
365
366
export interface IChatTerminalToolInvocationData {
367
kind: 'terminal';
368
commandLine: {
369
original: string;
370
userEdited?: string;
371
toolEdited?: string;
372
};
373
/** Message for model recommending the use of an alternative tool */
374
alternativeRecommendation?: string;
375
language: string;
376
terminalToolSessionId?: string;
377
/** The predefined command ID that will be used for this terminal command */
378
terminalCommandId?: string;
379
/** Serialized URI for the command that was executed in the terminal */
380
terminalCommandUri?: UriComponents;
381
/** Serialized output of the executed command */
382
terminalCommandOutput?: {
383
text: string;
384
truncated?: boolean;
385
lineCount?: number;
386
};
387
/** Stored theme colors at execution time to style detached output */
388
terminalTheme?: {
389
background?: string;
390
foreground?: string;
391
};
392
/** Stored command state to restore decorations after reload */
393
terminalCommandState?: {
394
exitCode?: number;
395
timestamp?: number;
396
duration?: number;
397
};
398
autoApproveInfo?: IMarkdownString;
399
}
400
401
/**
402
* @deprecated This is the old API shape, we should support this for a while before removing it so
403
* we don't break existing chats
404
*/
405
export interface ILegacyChatTerminalToolInvocationData {
406
kind: 'terminal';
407
command: string;
408
language: string;
409
}
410
411
export function isLegacyChatTerminalToolInvocationData(data: unknown): data is ILegacyChatTerminalToolInvocationData {
412
return !!data && typeof data === 'object' && 'command' in data;
413
}
414
415
export interface IChatToolInputInvocationData {
416
kind: 'input';
417
// eslint-disable-next-line @typescript-eslint/no-explicit-any
418
rawInput: any;
419
/** Optional MCP App UI metadata for rendering during and after tool execution */
420
mcpAppData?: {
421
/** URI of the UI resource for rendering (e.g., "ui://weather-server/dashboard") */
422
resourceUri: string;
423
/** Reference to the server definition for reconnection */
424
serverDefinitionId: string;
425
/** Reference to the collection containing the server */
426
collectionId: string;
427
};
428
}
429
430
export const enum ToolConfirmKind {
431
Denied,
432
ConfirmationNotNeeded,
433
Setting,
434
LmServicePerTool,
435
UserAction,
436
Skipped
437
}
438
439
export type ConfirmedReason =
440
| { type: ToolConfirmKind.Denied }
441
| { type: ToolConfirmKind.ConfirmationNotNeeded; reason?: string | IMarkdownString }
442
| { type: ToolConfirmKind.Setting; id: string }
443
| { type: ToolConfirmKind.LmServicePerTool; scope: 'session' | 'workspace' | 'profile' }
444
| { type: ToolConfirmKind.UserAction }
445
| { type: ToolConfirmKind.Skipped };
446
447
export interface IChatToolInvocation {
448
readonly presentation: IPreparedToolInvocation['presentation'];
449
readonly toolSpecificData?: IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent;
450
readonly confirmationMessages?: IToolConfirmationMessages;
451
readonly originMessage: string | IMarkdownString | undefined;
452
readonly invocationMessage: string | IMarkdownString;
453
readonly pastTenseMessage: string | IMarkdownString | undefined;
454
readonly source: ToolDataSource;
455
readonly toolId: string;
456
readonly toolCallId: string;
457
readonly parameters: unknown;
458
readonly fromSubAgent?: boolean;
459
readonly state: IObservable<IChatToolInvocation.State>;
460
generatedTitle?: string;
461
462
kind: 'toolInvocation';
463
}
464
465
export namespace IChatToolInvocation {
466
export const enum StateKind {
467
WaitingForConfirmation,
468
Executing,
469
WaitingForPostApproval,
470
Completed,
471
Cancelled,
472
}
473
474
interface IChatToolInvocationStateBase {
475
type: StateKind;
476
}
477
478
interface IChatToolInvocationWaitingForConfirmationState extends IChatToolInvocationStateBase {
479
type: StateKind.WaitingForConfirmation;
480
confirm(reason: ConfirmedReason): void;
481
}
482
483
interface IChatToolInvocationPostConfirmState {
484
confirmed: ConfirmedReason;
485
}
486
487
interface IChatToolInvocationExecutingState extends IChatToolInvocationStateBase, IChatToolInvocationPostConfirmState {
488
type: StateKind.Executing;
489
progress: IObservable<{ message?: string | IMarkdownString; progress: number | undefined }>;
490
}
491
492
interface IChatToolInvocationPostExecuteState extends IChatToolInvocationPostConfirmState {
493
resultDetails: IToolResult['toolResultDetails'];
494
}
495
496
interface IChatToolWaitingForPostApprovalState extends IChatToolInvocationStateBase, IChatToolInvocationPostExecuteState {
497
type: StateKind.WaitingForPostApproval;
498
confirm(reason: ConfirmedReason): void;
499
contentForModel: IToolResult['content'];
500
}
501
502
interface IChatToolInvocationCompleteState extends IChatToolInvocationStateBase, IChatToolInvocationPostExecuteState {
503
type: StateKind.Completed;
504
postConfirmed: ConfirmedReason | undefined;
505
contentForModel: IToolResult['content'];
506
}
507
508
interface IChatToolInvocationCancelledState extends IChatToolInvocationStateBase {
509
type: StateKind.Cancelled;
510
reason: ToolConfirmKind.Denied | ToolConfirmKind.Skipped;
511
}
512
513
export type State =
514
| IChatToolInvocationWaitingForConfirmationState
515
| IChatToolInvocationExecutingState
516
| IChatToolWaitingForPostApprovalState
517
| IChatToolInvocationCompleteState
518
| IChatToolInvocationCancelledState;
519
520
export function executionConfirmedOrDenied(invocation: IChatToolInvocation | IChatToolInvocationSerialized, reader?: IReader): ConfirmedReason | undefined {
521
if (invocation.kind === 'toolInvocationSerialized') {
522
if (invocation.isConfirmed === undefined || typeof invocation.isConfirmed === 'boolean') {
523
return { type: invocation.isConfirmed ? ToolConfirmKind.UserAction : ToolConfirmKind.Denied };
524
}
525
return invocation.isConfirmed;
526
}
527
528
const state = invocation.state.read(reader);
529
if (state.type === StateKind.WaitingForConfirmation) {
530
return undefined; // don't know yet
531
}
532
if (state.type === StateKind.Cancelled) {
533
return { type: state.reason };
534
}
535
536
return state.confirmed;
537
}
538
539
export function awaitConfirmation(invocation: IChatToolInvocation, token?: CancellationToken): Promise<ConfirmedReason> {
540
const reason = executionConfirmedOrDenied(invocation);
541
if (reason) {
542
return Promise.resolve(reason);
543
}
544
545
const store = new DisposableStore();
546
return new Promise<ConfirmedReason>(resolve => {
547
if (token) {
548
store.add(token.onCancellationRequested(() => {
549
resolve({ type: ToolConfirmKind.Denied });
550
}));
551
}
552
553
store.add(autorun(reader => {
554
const reason = executionConfirmedOrDenied(invocation, reader);
555
if (reason) {
556
store.dispose();
557
resolve(reason);
558
}
559
}));
560
}).finally(() => {
561
store.dispose();
562
});
563
}
564
565
function postApprovalConfirmedOrDenied(invocation: IChatToolInvocation, reader?: IReader): ConfirmedReason | undefined {
566
const state = invocation.state.read(reader);
567
if (state.type === StateKind.Completed) {
568
return state.postConfirmed || { type: ToolConfirmKind.ConfirmationNotNeeded };
569
}
570
if (state.type === StateKind.Cancelled) {
571
return { type: state.reason };
572
}
573
574
return undefined;
575
}
576
577
export function confirmWith(invocation: IChatToolInvocation | undefined, reason: ConfirmedReason) {
578
const state = invocation?.state.get();
579
if (state?.type === StateKind.WaitingForConfirmation || state?.type === StateKind.WaitingForPostApproval) {
580
state.confirm(reason);
581
return true;
582
}
583
return false;
584
}
585
586
export function awaitPostConfirmation(invocation: IChatToolInvocation, token?: CancellationToken): Promise<ConfirmedReason> {
587
const reason = postApprovalConfirmedOrDenied(invocation);
588
if (reason) {
589
return Promise.resolve(reason);
590
}
591
592
const store = new DisposableStore();
593
return new Promise<ConfirmedReason>(resolve => {
594
if (token) {
595
store.add(token.onCancellationRequested(() => {
596
resolve({ type: ToolConfirmKind.Denied });
597
}));
598
}
599
600
store.add(autorun(reader => {
601
const reason = postApprovalConfirmedOrDenied(invocation, reader);
602
if (reason) {
603
store.dispose();
604
resolve(reason);
605
}
606
}));
607
}).finally(() => {
608
store.dispose();
609
});
610
}
611
612
export function resultDetails(invocation: IChatToolInvocation | IChatToolInvocationSerialized, reader?: IReader) {
613
if (invocation.kind === 'toolInvocationSerialized') {
614
return invocation.resultDetails;
615
}
616
617
const state = invocation.state.read(reader);
618
if (state.type === StateKind.Completed || state.type === StateKind.WaitingForPostApproval) {
619
return state.resultDetails;
620
}
621
622
return undefined;
623
}
624
625
export function isComplete(invocation: IChatToolInvocation | IChatToolInvocationSerialized, reader?: IReader): boolean {
626
if (invocation.kind === 'toolInvocationSerialized') {
627
return true; // always cancelled or complete
628
}
629
630
const state = invocation.state.read(reader);
631
return state.type === StateKind.Completed || state.type === StateKind.Cancelled;
632
}
633
}
634
635
636
export interface IToolResultOutputDetailsSerialized {
637
output: {
638
type: 'data';
639
mimeType: string;
640
base64Data: string;
641
};
642
}
643
644
/**
645
* This is a IChatToolInvocation that has been serialized, like after window reload, so it is no longer an active tool invocation.
646
*/
647
export interface IChatToolInvocationSerialized {
648
presentation: IPreparedToolInvocation['presentation'];
649
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent;
650
invocationMessage: string | IMarkdownString;
651
originMessage: string | IMarkdownString | undefined;
652
pastTenseMessage: string | IMarkdownString | undefined;
653
resultDetails?: Array<URI | Location> | IToolResultInputOutputDetails | IToolResultOutputDetailsSerialized;
654
/** boolean used by pre-1.104 versions */
655
isConfirmed: ConfirmedReason | boolean | undefined;
656
isComplete: boolean;
657
toolCallId: string;
658
toolId: string;
659
source: ToolDataSource;
660
readonly fromSubAgent?: boolean;
661
generatedTitle?: string;
662
kind: 'toolInvocationSerialized';
663
}
664
665
export interface IChatExtensionsContent {
666
extensions: string[];
667
kind: 'extensions';
668
}
669
670
export interface IChatPullRequestContent {
671
uri: URI;
672
title: string;
673
description: string;
674
author: string;
675
linkTag: string;
676
kind: 'pullRequest';
677
}
678
679
export interface IChatTodoListContent {
680
kind: 'todoList';
681
sessionId: string;
682
todoList: Array<{
683
id: string;
684
title: string;
685
description: string;
686
status: 'not-started' | 'in-progress' | 'completed';
687
}>;
688
}
689
690
export interface IChatMcpServersStarting {
691
readonly kind: 'mcpServersStarting';
692
readonly state?: IObservable<IAutostartResult>; // not hydrated when serialized
693
didStartServerIds?: string[];
694
}
695
696
export class ChatMcpServersStarting implements IChatMcpServersStarting {
697
public readonly kind = 'mcpServersStarting';
698
699
public didStartServerIds?: string[] = [];
700
701
public get isEmpty() {
702
const s = this.state.get();
703
return !s.working && s.serversRequiringInteraction.length === 0;
704
}
705
706
constructor(public readonly state: IObservable<IAutostartResult>) { }
707
708
wait() {
709
return new Promise<IAutostartResult>(resolve => {
710
autorunSelfDisposable(reader => {
711
const s = this.state.read(reader);
712
if (!s.working) {
713
reader.dispose();
714
resolve(s);
715
}
716
});
717
});
718
}
719
720
toJSON(): IChatMcpServersStarting {
721
return { kind: 'mcpServersStarting', didStartServerIds: this.didStartServerIds };
722
}
723
}
724
725
export interface IChatPrepareToolInvocationPart {
726
readonly kind: 'prepareToolInvocation';
727
readonly toolName: string;
728
}
729
730
export type IChatProgress =
731
| IChatMarkdownContent
732
| IChatAgentMarkdownContentWithVulnerability
733
| IChatTreeData
734
| IChatMultiDiffData
735
| IChatUsedContext
736
| IChatContentReference
737
| IChatContentInlineReference
738
| IChatCodeCitation
739
| IChatProgressMessage
740
| IChatTask
741
| IChatTaskResult
742
| IChatCommandButton
743
| IChatWarningMessage
744
| IChatTextEdit
745
| IChatNotebookEdit
746
| IChatMoveMessage
747
| IChatResponseCodeblockUriPart
748
| IChatConfirmation
749
| IChatClearToPreviousToolInvocation
750
| IChatToolInvocation
751
| IChatToolInvocationSerialized
752
| IChatExtensionsContent
753
| IChatPullRequestContent
754
| IChatUndoStop
755
| IChatPrepareToolInvocationPart
756
| IChatThinkingPart
757
| IChatTaskSerialized
758
| IChatElicitationRequest
759
| IChatElicitationRequestSerialized
760
| IChatMcpServersStarting;
761
762
export interface IChatFollowup {
763
kind: 'reply';
764
message: string;
765
agentId: string;
766
subCommand?: string;
767
title?: string;
768
tooltip?: string;
769
}
770
771
export function isChatFollowup(obj: unknown): obj is IChatFollowup {
772
return (
773
!!obj &&
774
(obj as IChatFollowup).kind === 'reply' &&
775
typeof (obj as IChatFollowup).message === 'string' &&
776
typeof (obj as IChatFollowup).agentId === 'string'
777
);
778
}
779
780
export enum ChatAgentVoteDirection {
781
Down = 0,
782
Up = 1
783
}
784
785
export enum ChatAgentVoteDownReason {
786
IncorrectCode = 'incorrectCode',
787
DidNotFollowInstructions = 'didNotFollowInstructions',
788
IncompleteCode = 'incompleteCode',
789
MissingContext = 'missingContext',
790
PoorlyWrittenOrFormatted = 'poorlyWrittenOrFormatted',
791
RefusedAValidRequest = 'refusedAValidRequest',
792
OffensiveOrUnsafe = 'offensiveOrUnsafe',
793
Other = 'other',
794
WillReportIssue = 'willReportIssue'
795
}
796
797
export interface IChatVoteAction {
798
kind: 'vote';
799
direction: ChatAgentVoteDirection;
800
reason: ChatAgentVoteDownReason | undefined;
801
}
802
803
export enum ChatCopyKind {
804
// Keyboard shortcut or context menu
805
Action = 1,
806
Toolbar = 2
807
}
808
809
export interface IChatCopyAction {
810
kind: 'copy';
811
codeBlockIndex: number;
812
copyKind: ChatCopyKind;
813
copiedCharacters: number;
814
totalCharacters: number;
815
copiedText: string;
816
totalLines: number;
817
copiedLines: number;
818
modelId: string;
819
languageId?: string;
820
}
821
822
export interface IChatInsertAction {
823
kind: 'insert';
824
codeBlockIndex: number;
825
totalCharacters: number;
826
totalLines: number;
827
languageId?: string;
828
modelId: string;
829
newFile?: boolean;
830
}
831
832
export interface IChatApplyAction {
833
kind: 'apply';
834
codeBlockIndex: number;
835
totalCharacters: number;
836
totalLines: number;
837
languageId?: string;
838
modelId: string;
839
newFile?: boolean;
840
codeMapper?: string;
841
editsProposed: boolean;
842
}
843
844
845
export interface IChatTerminalAction {
846
kind: 'runInTerminal';
847
codeBlockIndex: number;
848
languageId?: string;
849
}
850
851
export interface IChatCommandAction {
852
kind: 'command';
853
commandButton: IChatCommandButton;
854
}
855
856
export interface IChatFollowupAction {
857
kind: 'followUp';
858
followup: IChatFollowup;
859
}
860
861
export interface IChatBugReportAction {
862
kind: 'bug';
863
}
864
865
export interface IChatInlineChatCodeAction {
866
kind: 'inlineChat';
867
action: 'accepted' | 'discarded';
868
}
869
870
871
export interface IChatEditingSessionAction {
872
kind: 'chatEditingSessionAction';
873
uri: URI;
874
hasRemainingEdits: boolean;
875
outcome: 'accepted' | 'rejected' | 'userModified';
876
}
877
878
export interface IChatEditingHunkAction {
879
kind: 'chatEditingHunkAction';
880
uri: URI;
881
lineCount: number;
882
linesAdded: number;
883
linesRemoved: number;
884
outcome: 'accepted' | 'rejected';
885
hasRemainingEdits: boolean;
886
modeId?: string;
887
modelId?: string;
888
languageId?: string;
889
}
890
891
export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatApplyAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction | IChatBugReportAction | IChatInlineChatCodeAction | IChatEditingSessionAction | IChatEditingHunkAction;
892
893
export interface IChatUserActionEvent {
894
action: ChatUserAction;
895
agentId: string | undefined;
896
command: string | undefined;
897
sessionResource: URI;
898
requestId: string;
899
result: IChatAgentResult | undefined;
900
modelId?: string | undefined;
901
modeId?: string | undefined;
902
}
903
904
export interface IChatDynamicRequest {
905
/**
906
* The message that will be displayed in the UI
907
*/
908
message: string;
909
910
/**
911
* Any extra metadata/context that will go to the provider.
912
*/
913
// eslint-disable-next-line @typescript-eslint/no-explicit-any
914
metadata?: any;
915
}
916
917
export interface IChatCompleteResponse {
918
message: string | ReadonlyArray<IChatProgress>;
919
result?: IChatAgentResult;
920
followups?: IChatFollowup[];
921
}
922
923
export interface IChatSessionStats {
924
fileCount: number;
925
added: number;
926
removed: number;
927
}
928
929
export interface IChatSessionTiming {
930
startTime: number;
931
endTime?: number;
932
}
933
934
export const enum ResponseModelState {
935
Pending,
936
Complete,
937
Cancelled,
938
Failed,
939
NeedsInput,
940
}
941
942
export interface IChatDetail {
943
sessionResource: URI;
944
title: string;
945
lastMessageDate: number;
946
timing: IChatSessionTiming;
947
isActive: boolean;
948
stats?: IChatSessionStats;
949
lastResponseState: ResponseModelState;
950
}
951
952
export interface IChatProviderInfo {
953
id: string;
954
}
955
956
export interface IChatSendRequestResponseState {
957
responseCreatedPromise: Promise<IChatResponseModel>;
958
responseCompletePromise: Promise<void>;
959
}
960
961
export interface IChatSendRequestData extends IChatSendRequestResponseState {
962
agent: IChatAgentData;
963
slashCommand?: IChatAgentCommand;
964
}
965
966
export interface IChatEditorLocationData {
967
type: ChatAgentLocation.EditorInline;
968
id: string;
969
document: URI;
970
selection: ISelection;
971
wholeRange: IRange;
972
}
973
974
export interface IChatNotebookLocationData {
975
type: ChatAgentLocation.Notebook;
976
sessionInputUri: URI;
977
}
978
979
export interface IChatTerminalLocationData {
980
type: ChatAgentLocation.Terminal;
981
// TBD
982
}
983
984
export type IChatLocationData = IChatEditorLocationData | IChatNotebookLocationData | IChatTerminalLocationData;
985
986
export interface IChatSendRequestOptions {
987
modeInfo?: IChatRequestModeInfo;
988
userSelectedModelId?: string;
989
userSelectedTools?: IObservable<UserSelectedTools>;
990
location?: ChatAgentLocation;
991
locationData?: IChatLocationData;
992
parserContext?: IChatParserContext;
993
attempt?: number;
994
noCommandDetection?: boolean;
995
// eslint-disable-next-line @typescript-eslint/no-explicit-any
996
acceptedConfirmationData?: any[];
997
// eslint-disable-next-line @typescript-eslint/no-explicit-any
998
rejectedConfirmationData?: any[];
999
attachedContext?: IChatRequestVariableEntry[];
1000
1001
/** The target agent ID can be specified with this property instead of using @ in 'message' */
1002
agentId?: string;
1003
/** agentId, but will not add a @ name to the request */
1004
agentIdSilent?: string;
1005
slashCommand?: string;
1006
1007
/**
1008
* The label of the confirmation action that was selected.
1009
*/
1010
confirmation?: string;
1011
1012
}
1013
1014
export type IChatModelReference = IReference<IChatModel>;
1015
1016
export const IChatService = createDecorator<IChatService>('IChatService');
1017
1018
export interface IChatService {
1019
_serviceBrand: undefined;
1020
transferredSessionResource: URI | undefined;
1021
1022
readonly onDidSubmitRequest: Event<{ readonly chatSessionResource: URI }>;
1023
1024
/**
1025
* An observable containing all live chat models.
1026
*/
1027
readonly chatModels: IObservable<Iterable<IChatModel>>;
1028
1029
isEnabled(location: ChatAgentLocation): boolean;
1030
hasSessions(): boolean;
1031
startSession(location: ChatAgentLocation, options?: IChatSessionStartOptions): IChatModelReference;
1032
1033
/**
1034
* Get an active session without holding a reference to it.
1035
*/
1036
getSession(sessionResource: URI): IChatModel | undefined;
1037
1038
/**
1039
* Acquire a reference to an active session.
1040
*/
1041
getActiveSessionReference(sessionResource: URI): IChatModelReference | undefined;
1042
1043
getOrRestoreSession(sessionResource: URI): Promise<IChatModelReference | undefined>;
1044
getSessionTitle(sessionResource: URI): string | undefined;
1045
loadSessionFromContent(data: IExportableChatData | ISerializableChatData | URI): IChatModelReference | undefined;
1046
loadSessionForResource(resource: URI, location: ChatAgentLocation, token: CancellationToken): Promise<IChatModelReference | undefined>;
1047
readonly editingSessions: IChatEditingSession[];
1048
getChatSessionFromInternalUri(sessionResource: URI): IChatSessionContext | undefined;
1049
1050
/**
1051
* Returns whether the request was accepted.`
1052
*/
1053
sendRequest(sessionResource: URI, message: string, options?: IChatSendRequestOptions): Promise<IChatSendRequestData | undefined>;
1054
1055
/**
1056
* Sets a custom title for a chat model.
1057
*/
1058
setTitle(sessionResource: URI, title: string): void;
1059
appendProgress(request: IChatRequestModel, progress: IChatProgress): void;
1060
resendRequest(request: IChatRequestModel, options?: IChatSendRequestOptions): Promise<void>;
1061
adoptRequest(sessionResource: URI, request: IChatRequestModel): Promise<void>;
1062
removeRequest(sessionResource: URI, requestId: string): Promise<void>;
1063
cancelCurrentRequestForSession(sessionResource: URI): void;
1064
addCompleteRequest(sessionResource: URI, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, attempt: number | undefined, response: IChatCompleteResponse): void;
1065
setChatSessionTitle(sessionResource: URI, title: string): void;
1066
getLocalSessionHistory(): Promise<IChatDetail[]>;
1067
clearAllHistoryEntries(): Promise<void>;
1068
removeHistoryEntry(sessionResource: URI): Promise<void>;
1069
getChatStorageFolder(): URI;
1070
logChatIndex(): void;
1071
getLiveSessionItems(): Promise<IChatDetail[]>;
1072
getHistorySessionItems(): Promise<IChatDetail[]>;
1073
getMetadataForSession(sessionResource: URI): Promise<IChatDetail | undefined>;
1074
1075
readonly onDidPerformUserAction: Event<IChatUserActionEvent>;
1076
notifyUserAction(event: IChatUserActionEvent): void;
1077
readonly onDidDisposeSession: Event<{ readonly sessionResource: URI[]; readonly reason: 'cleared' }>;
1078
1079
transferChatSession(transferredSessionResource: URI, toWorkspace: URI): Promise<void>;
1080
1081
activateDefaultAgent(location: ChatAgentLocation): Promise<void>;
1082
1083
readonly edits2Enabled: boolean;
1084
1085
readonly requestInProgressObs: IObservable<boolean>;
1086
1087
/**
1088
* For tests only!
1089
*/
1090
setSaveModelsEnabled(enabled: boolean): void;
1091
1092
/**
1093
* For tests only!
1094
*/
1095
waitForModelDisposals(): Promise<void>;
1096
}
1097
1098
export interface IChatSessionContext {
1099
readonly chatSessionType: string;
1100
readonly chatSessionResource: URI;
1101
readonly isUntitled: boolean;
1102
}
1103
1104
export const KEYWORD_ACTIVIATION_SETTING_ID = 'accessibility.voice.keywordActivation';
1105
1106
export interface IChatSessionStartOptions {
1107
canUseTools?: boolean;
1108
disableBackgroundKeepAlive?: boolean;
1109
}
1110
1111