chatModel.ts ×179

Frontier kind: Code frontier

unlabeled · c_bb428abca3ea

252 tests · 41154 LOC · 175 files · introduces 0 tests · 1630 LOC · 4 files

Introduces — evidence that enters the hierarchy at this concept

Code
206 ranges1630 lines · 4 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
3549 ranges41154 lines · 175 files · Browse complete extent
All tests (intent)
252 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

4 files ranked by introduced lines: 1630 introduced LOC across 206 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/model/chatModel.ts 1455 introduced LOC · 179 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatModel.ts
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 { asArray } from '../../../../../base/common/arrays.js';
7 > import { softAssertNever } from '../../../../../base/common/assert.js';
8 > import { VSBuffer, decodeHex, encodeHex } from '../../../../../base/common/buffer.js';
9 > import { IStringDictionary } from '../../../../../base/common/collections.js';
10 > import { BugIndicatingError } from '../../../../../base/common/errors.js';
11 > import { Emitter, Event } from '../../../../../base/common/event.js';
12 > import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../../base/common/htmlContent.js';
13 > import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
14 > import { ResourceMap } from '../../../../../base/common/map.js';
15 > import { revive } from '../../../../../base/common/marshalling.js';
16 > import { Schemas } from '../../../../../base/common/network.js';
17 > import { equals } from '../../../../../base/common/objects.js';
18 > import { IObservable, autorun, constObservable, derived, observableFromEvent, observableSignalFromEvent, observableValue, observableValueOpts, registerAutorunSelfDisposable } from '../../../../../base/common/observable.js';
19 > import { basename, isEqual } from '../../../../../base/common/resources.js';
20 > import { hasKey, WithDefinedProps } from '../../../../../base/common/types.js';
21 > import { URI, UriDto } from '../../../../../base/common/uri.js';
22 > import { generateUuid } from '../../../../../base/common/uuid.js';
23 > import { IRange } from '../../../../../editor/common/core/range.js';
24 > import { OffsetRange } from '../../../../../editor/common/core/ranges/offsetRange.js';
25 > import { ISelection } from '../../../../../editor/common/core/selection.js';
26 > import { TextEdit } from '../../../../../editor/common/languages.js';
27 > import { EditSuggestionId } from '../../../../../editor/common/textModelEditSource.js';
28 > import { localize } from '../../../../../nls.js';
29 > import { canLog, ILogService, LogLevel } from '../../../../../platform/log/common/log.js';
30 > import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
31 > import { ChatRequestToolReferenceEntry, IChatRequestVariableEntry, isImplicitVariableEntry, isStringImplicitContextValue, isStringVariableEntry } from '../attachments/chatVariableEntries.js';
32 > import { migrateLegacyTerminalToolSpecificData } from '../chat.js';
33 > import { ChatPerfMark, markChat } from '../chatPerf.js';
34 > import { ChatAgentVoteDirection, ChatRequestQueueKind, ChatResponseClearToPreviousToolInvocationReason, ElicitationState, IChatAgentMarkdownContentWithVulnerability, IChatAutoModeResolutionPart, IChatClearToPreviousToolInvocation, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatDisabledClaudeHooksPart, IChatEditingSessionAction, IChatElicitationRequest, IChatElicitationRequestSerialized, IChatExternalEdit, IChatExternalToolInvocationUpdate, IChatExtensionsContent, IChatFollowup, IChatHookPart, IChatInfoMessage, IChatLocationData, IChatMarkdownContent, IChatMcpAuthenticationRequired, IChatMcpServersStarting, IChatMcpServersStartingSerialized, IChatMcpServersStartingSlow, IChatModelReference, IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatNotebookEdit, IChatPlanReview, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatQuestionCarousel, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatSendRequestOptions, IChatService, IChatSessionTiming, IChatSystemNotificationPart, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsage, IChatUsagePromptTokenDetail, IChatUsedContext, IChatWarningMessage, IChatWorkspaceEdit, ResponseModelState, ToolConfirmKind, isIUsedContext } from '../chatService/chatService.js';
35 > import { ChatAgentLocation, ChatModeKind, ChatPermissionLevel } from '../constants.js';
36 > import { ChatToolInvocation } from './chatProgressTypes/chatToolInvocation.js';
37 > import { ChatPlanReviewData } from './chatProgressTypes/chatPlanReviewData.js';
38 > import { ChatQuestionCarouselData } from './chatProgressTypes/chatQuestionCarouselData.js';
39 > import { ToolDataSource, IToolData } from '../tools/languageModelToolsService.js';
40 > import { IChatEditingService, IChatEditingSession, ModifiedFileEntryState } from '../editing/chatEditingService.js';
41 > import { ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier } from '../languageModels.js';
42 > import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, UserSelectedTools, reviveSerializedAgent } from '../participants/chatAgents.js';
43 > import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from '../requestParser/chatParserTypes.js';
44 > import { chatSessionResourceToId, LocalChatSessionUri } from './chatUri.js';
45 > import { ObjectMutationLog } from './objectMutationLog.js';
46 >
47 >
48 > /**
49 > * Represents a queued chat request waiting to be processed.
50 > */
51 > export interface IChatPendingRequest {
52 > readonly request: IChatRequestModel;
53 > readonly kind: ChatRequestQueueKind;
54 > /**
55 > * The options that were passed to sendRequest when this request was queued.
56 > * userSelectedTools is snapshotted to a static observable at queue time.
57 > */
58 > readonly sendOptions: IChatSendRequestOptions;
59 > }
60 >
61 > /**
62 > * Serializable version of IChatSendRequestOptions for pending requests.
63 > * Excludes observables and non-serializable fields.
64 > */
65 > export interface ISerializableSendOptions {
66 > modeInfo?: IChatRequestModeInfo;
67 > userSelectedModelId?: string;
68 > userSelectedModelConfiguration?: IStringDictionary<unknown>;
69 > /** Static snapshot of user-selected tools (not an observable) */
70 > userSelectedTools?: UserSelectedTools;
71 > location?: ChatAgentLocation;
72 > locationData?: IChatLocationData;
73 > attempt?: number;
74 > noCommandDetection?: boolean;
75 > agentId?: string;
76 > agentIdSilent?: string;
77 > slashCommand?: string;
78 > confirmation?: string;
79 > isSystemInitiated?: boolean;
80 > systemInitiatedLabel?: string;
81 > terminalExecutionId?: string;
82 > }
83 >
84 > /**
85 > * Serializable representation of a pending chat request.
86 > */
87 > export interface ISerializablePendingRequestData {
88 > id: string;
89 > request: ISerializableChatRequestData;
90 > kind: ChatRequestQueueKind;
91 > sendOptions: ISerializableSendOptions;
92 > }
93 >
94 > export const CHAT_ATTACHABLE_IMAGE_MIME_TYPES: Record<string, string> = {
95 > png: 'image/png',
96 > jpg: 'image/jpeg',
97 > jpeg: 'image/jpeg',
98 > gif: 'image/gif',
99 > webp: 'image/webp',
100 > };
101 >
102 > export function getAttachableImageExtension(mimeType: string): string | undefined {
103 return Object.entries(CHAT_ATTACHABLE_IMAGE_MIME_TYPES).find(([_, value]) => value === mimeType)?.[0];
104 }
105 > chatModel.ts
106 > export interface IChatRequestVariableData {
107 > variables: readonly IChatRequestVariableEntry[];
108 > }
109 >
110 > export namespace IChatRequestVariableData {
111 > export function toExport(data: IChatRequestVariableData): IChatRequestVariableData {
112 return { variables: data.variables.map(IChatRequestVariableEntry.toExport) };
113 }
114 > } chatModel.ts
115 >
116 > export interface IChatRequestModel {
117 > readonly id: string;
118 > readonly timestamp: number;
119 > readonly requestTimestamp: number | undefined;
120 > readonly version: number;
121 > readonly modeInfo?: IChatRequestModeInfo;
122 > readonly session: IChatModel;
123 > readonly message: IParsedChatRequest;
124 > readonly attempt: number;
125 > readonly variableData: IChatRequestVariableData;
126 > readonly confirmation?: string;
127 > readonly locationData?: IChatLocationData;
128 > readonly attachedContext?: IChatRequestVariableEntry[];
129 > readonly isCompleteAddedRequest: boolean;
130 > readonly isTerminalCommand: boolean;
131 > readonly response?: IChatResponseModel;
132 > readonly editedFileEvents?: IChatAgentEditedFileEvent[];
133 > shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
134 > readonly shouldBeBlocked: IObservable<boolean>;
135 > setShouldBeBlocked(value: boolean): void;
136 > readonly modelId?: string;
137 > readonly userSelectedTools?: UserSelectedTools;
138 > readonly isSystemInitiated?: boolean;
139 > readonly systemInitiatedLabel?: string;
140 > readonly terminalExecutionId?: string;
141 > }
142 >
143 > export interface ICodeBlockInfo {
144 > readonly suggestionId: EditSuggestionId;
145 > }
146 >
147 > export interface IChatTextEditGroupState {
148 > sha1: string;
149 > applied: number;
150 > }
151 >
152 > export interface IChatTextEditGroup {
153 > uri: URI;
154 > edits: TextEdit[][];
155 > state?: IChatTextEditGroupState;
156 > kind: 'textEditGroup';
157 > done: boolean | undefined;
158 > isExternalEdit?: boolean;
159 > }
160 >
161 > export function isCellTextEditOperation(value: unknown): value is ICellTextEditOperation {
162 const candidate = value as ICellTextEditOperation;
163 return !!candidate && !!candidate.edit && !!candidate.uri && URI.isUri(candidate.uri);
164 }
165 > chatModel.ts
166 > export function isCellTextEditOperationArray(value: ICellTextEditOperation[] | ICellEditOperation[]): value is ICellTextEditOperation[] {
167 return value.some(isCellTextEditOperation);
168 }
169 > chatModel.ts
170 > export interface ICellTextEditOperation {
171 > edit: TextEdit;
172 > uri: URI;
173 > }
174 >
175 > export interface IChatNotebookEditGroup {
176 > uri: URI;
177 > edits: (ICellTextEditOperation[] | ICellEditOperation[])[];
178 > state?: IChatTextEditGroupState;
179 > kind: 'notebookEditGroup';
180 > done: boolean | undefined;
181 > isExternalEdit?: boolean;
182 > }
183 >
184 > /**
185 > * Progress kinds that are included in the history of a response.
186 > * Excludes "internal" types that are included in history.
187 > */
188 > export type IChatProgressHistoryResponseContent =
189 > | IChatMarkdownContent
190 > | IChatAgentMarkdownContentWithVulnerability
191 > | IChatResponseCodeblockUriPart
192 > | IChatTreeData
193 > | IChatMultiDiffDataSerialized
194 > | IChatContentInlineReference
195 > | IChatProgressMessage
196 > | IChatSystemNotificationPart
197 > | IChatCommandButton
198 > | IChatWarningMessage
199 > | IChatInfoMessage
200 > | IChatTask
201 > | IChatTaskSerialized
202 > | IChatTextEditGroup
203 > | IChatNotebookEditGroup
204 > | IChatConfirmation
205 > | IChatQuestionCarousel
206 > | IChatPlanReview
207 > | IChatExtensionsContent
208 > | IChatThinkingPart
209 > | IChatHookPart
210 > | IChatPullRequestContent
211 > | IChatWorkspaceEdit
212 > | IChatExternalEdit
213 > | IChatAutoModeResolutionPart;
214 >
215 > /**
216 > * "Normal" progress kinds that are rendered as parts of the stream of content.
217 > */
218 > export type IChatProgressResponseContent =
219 > | IChatProgressHistoryResponseContent
220 > | IChatToolInvocation
221 > | IChatToolInvocationSerialized
222 > | IChatMultiDiffData
223 > | IChatUndoStop
224 > | IChatElicitationRequest
225 > | IChatElicitationRequestSerialized
226 > | IChatClearToPreviousToolInvocation
227 > | IChatMcpServersStarting
228 > | IChatMcpServersStartingSerialized
229 > | IChatMcpAuthenticationRequired
230 > | IChatMcpServersStartingSlow
231 > | IChatDisabledClaudeHooksPart;
232 >
233 > export type IChatProgressResponseContentSerialized = Exclude<IChatProgressResponseContent,
234 > | IChatToolInvocation
235 > | IChatElicitationRequest
236 > | IChatTask
237 > | IChatMultiDiffData
238 > | IChatMcpServersStarting
239 > | IChatMcpAuthenticationRequired
240 > | IChatMcpServersStartingSlow
241 > | IChatDisabledClaudeHooksPart
242 > >;
243 >
244 > const nonHistoryKinds = new Set(['toolInvocation', 'toolInvocationSerialized', 'undoStop']);
245 function isChatProgressHistoryResponseContent(content: IChatProgressResponseContent): content is IChatProgressHistoryResponseContent {
246 return !nonHistoryKinds.has(content.kind);
247 }
248 > chatModel.ts
249 > export function toChatHistoryContent(content: ReadonlyArray<IChatProgressResponseContent>): IChatProgressHistoryResponseContent[] {
250 return content.filter(isChatProgressHistoryResponseContent);
251 }
252 > chatModel.ts
253 > export type IChatProgressRenderableResponseContent = Exclude<IChatProgressResponseContent, IChatContentInlineReference | IChatAgentMarkdownContentWithVulnerability | IChatResponseCodeblockUriPart>;
254 >
255 > export interface IResponse {
256 > readonly value: ReadonlyArray<IChatProgressResponseContent>;
257 > getMarkdown(): string;
258 > getFinalResponse(): string;
259 > toString(): string;
260 > }
261 >
262 > export interface IChatResponseModel {
263 > readonly onDidChange: Event<ChatResponseModelChangeReason>;
264 > readonly id: string;
265 > readonly requestId: string;
266 > readonly request: IChatRequestModel | undefined;
267 > readonly username: string;
268 > readonly session: IChatModel;
269 > readonly agent?: IChatAgentData;
270 > readonly usedContext: IChatUsedContext | undefined;
271 > readonly contentReferences: ReadonlyArray<IChatContentReference>;
272 > readonly codeCitations: ReadonlyArray<IChatCodeCitation>;
273 > readonly progressMessages: ReadonlyArray<IChatProgressMessage>;
274 > readonly slashCommand?: IChatAgentCommand;
275 > readonly agentOrSlashCommandDetected: boolean;
276 > /** View of the response shown to the user, may have parts omitted from undo stops. */
277 > readonly response: IResponse;
278 > /** Entire response from the model. */
279 > readonly entireResponse: IResponse;
280 > /** Milliseconds timestamp when this chat response was created. */
281 > readonly timestamp: number;
282 > /** Milliseconds timestamp when this chat response was completed or cancelled. */
283 > readonly completedAt?: number;
284 > /** Known completion timestamp for display. Undefined for legacy responses whose completion time was synthesized during restore. */
285 > readonly completionTimestamp?: number;
286 > /** The state of this response */
287 > readonly state: ResponseModelState;
288 > /** @internal */
289 > readonly stateT: ResponseModelStateT;
290 > /**
291 > * Adjusted millisecond timestamp that excludes the duration during which
292 > * the model was pending user confirmation. `Date.now() - confirmationAdjustedTimestamp`
293 > * will return the amount of time the response was busy generating content.
294 > * This is updated only when `isPendingConfirmation` changes state.
295 > */
296 > readonly confirmationAdjustedTimestamp: IObservable<number>;
297 > readonly isComplete: boolean;
298 > readonly isCanceled: boolean;
299 > readonly isPendingConfirmation: IObservable<{ startedWaitingAt: number; detail?: string } | undefined>;
300 > readonly isInProgress: IObservable<boolean>;
301 > /**
302 > * True whenever this response has not reached a terminal state yet.
303 > * Unlike {@link isInProgress}, this remains true during tool confirmations,
304 > * elicitations, and any other intermediate state.
305 > */
306 > readonly isIncomplete: IObservable<boolean>;
307 > readonly shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
308 > readonly shouldBeBlocked: IObservable<boolean>;
309 > readonly isCompleteAddedRequest: boolean;
310 > /** A stale response is one that has been persisted and rehydrated, so e.g. Commands that have their arguments stored in the EH are gone. */
311 > readonly isStale: boolean;
312 > readonly vote: ChatAgentVoteDirection | undefined;
313 > readonly followups?: IChatFollowup[] | undefined;
314 > readonly result?: IChatAgentResult;
315 > readonly usage?: IChatUsage;
316 > readonly usageObs: IObservable<IChatUsage | undefined>;
317 > readonly completionTokenCount: number | undefined;
318 > readonly completionTokenCountObs: IObservable<number | undefined>;
319 > /** Elapsed generation time in ms (excluding confirmation waits). Set on completion and serialized. */
320 > readonly elapsedMs: number | undefined;
321 > readonly codeBlockInfos: ICodeBlockInfo[] | undefined;
322 >
323 > initializeCodeBlockInfos(codeBlockInfo: ICodeBlockInfo[]): void;
324 > addUndoStop(undoStop: IChatUndoStop): void;
325 > setVote(vote: ChatAgentVoteDirection): void;
326 > setUsage(usage: IChatUsage): void;
327 > setElapsedMs(elapsedMs: number): void;
328 > setEditApplied(edit: IChatTextEditGroup, editCount: number): boolean;
329 > resolveInlineReference(resolveId: string, resolvedReference: IChatContentInlineReference): boolean;
330 > updateContent(progress: IChatProgressResponseContent | IChatTextEdit | IChatNotebookEdit | IChatTask | IChatExternalToolInvocationUpdate, quiet?: boolean): void;
331 > /**
332 > * Adopts any partially-undo {@link response} as the {@link entireResponse}.
333 > * Only valid when {@link isComplete}. This is needed because otherwise an
334 > * undone and then diverged state would start showing old data because the
335 > * undo stops would no longer exist in the model.
336 > */
337 > finalizeUndoState(): void;
338 > }
339 >
340 > export type ChatResponseModelChangeReason =
341 > | { reason: 'other' }
342 > | { reason: 'completedRequest' }
343 > | { reason: 'undoStop'; id: string };
344 >
345 > export const defaultChatResponseModelChangeReason: ChatResponseModelChangeReason = { reason: 'other' };
346 >
347 > export interface IChatRequestModeInfo {
348 > kind: ChatModeKind | undefined; // is undefined in case of telemetryModeId === 'applyCodeBlock'
349 > isBuiltin: boolean;
350 > modeInstructions: IChatRequestModeInstructions | undefined;
351 > telemetryModeId: 'ask' | 'agent' | 'edit' | 'custom' | 'applyCodeBlock' | undefined;
352 > telemetryModeName?: string;
353 > applyCodeBlockSuggestionId: EditSuggestionId | undefined;
354 > permissionLevel?: ChatPermissionLevel;
355 > }
356 >
357 > export interface IChatRequestModeInstructions {
358 > readonly uri?: URI;
359 > readonly name: string;
360 > readonly content: string;
361 > readonly toolReferences: readonly ChatRequestToolReferenceEntry[];
362 > readonly allowedSubagents?: readonly string[];
363 > readonly metadata?: Record<string, boolean | string | number>;
364 > readonly isBuiltin?: boolean;
365 > }
366 >
367 > export interface IChatRequestModelParameters {
368 > session: ChatModel;
369 > message: IParsedChatRequest;
370 > variableData: IChatRequestVariableData;
371 > timestamp?: number;
372 > fallbackTimestamp?: number;
373 > attempt?: number;
374 > modeInfo?: IChatRequestModeInfo;
375 > confirmation?: string;
376 > locationData?: IChatLocationData;
377 > attachedContext?: IChatRequestVariableEntry[];
378 > isCompleteAddedRequest?: boolean;
379 > modelId?: string;
380 > restoredId?: string;
381 > editedFileEvents?: IChatAgentEditedFileEvent[];
382 > userSelectedTools?: UserSelectedTools;
383 > isSystemInitiated?: boolean;
384 > systemInitiatedLabel?: string;
385 > terminalExecutionId?: string;
386 > /** Whether this request runs as a terminal command (agent host `!` prefix). */
387 > isTerminalCommand?: boolean;
388 > }
389 >
390 > export class ChatRequestModel implements IChatRequestModel {
391 > public readonly id: string;
392 > public response: ChatResponseModel | undefined;
393 > public shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
394 > public readonly timestamp: number;
395 > public readonly requestTimestamp: number | undefined;
396 > public readonly message: IParsedChatRequest;
397 > public readonly isCompleteAddedRequest: boolean;
398 > public readonly modelId?: string;
399 > public readonly modeInfo?: IChatRequestModeInfo;
400 > public readonly userSelectedTools?: UserSelectedTools;
401 > public readonly isSystemInitiated?: boolean;
402 > public readonly systemInitiatedLabel?: string;
403 > public readonly terminalExecutionId?: string;
404 > public readonly isTerminalCommand: boolean;
405 >
406 > private readonly _shouldBeBlocked = observableValue<boolean>(this, false);
407 > public get shouldBeBlocked(): IObservable<boolean> {
408 > return this._shouldBeBlocked;
409 > }
410 >
411 > public setShouldBeBlocked(value: boolean): void {
412 this._shouldBeBlocked.set(value, undefined);
413 }
414 > chatModel.ts
415 > private _session: ChatModel;
416 > private readonly _attempt: number;
417 > private _variableData: IChatRequestVariableData;
418 > private readonly _confirmation?: string;
419 > private readonly _locationData?: IChatLocationData;
420 > private readonly _attachedContext?: IChatRequestVariableEntry[];
421 > private readonly _editedFileEvents?: IChatAgentEditedFileEvent[];
422 >
423 > public get session(): ChatModel {
424 return this._session;
425 }
426 > chatModel.ts
427 > public get attempt(): number {
428 return this._attempt;
429 }
430 > chatModel.ts
431 > public get variableData(): IChatRequestVariableData {
432 return this._variableData;
433 }
434 > chatModel.ts
435 > public set variableData(v: IChatRequestVariableData) {
436 this._version++;
437 this._variableData = v;
438 }
439 > chatModel.ts
440 > public get confirmation(): string | undefined {
441 return this._confirmation;
442 }
443 > chatModel.ts
444 > public get locationData(): IChatLocationData | undefined {
445 return this._locationData;
446 }
447 > chatModel.ts
448 > public get attachedContext(): IChatRequestVariableEntry[] | undefined {
449 return this._attachedContext;
450 }
451 > chatModel.ts
452 > public get editedFileEvents(): IChatAgentEditedFileEvent[] | undefined {
453 return this._editedFileEvents;
454 }
455 > chatModel.ts
456 > private _version = 0;
457 > public get version(): number {
458 return this._version;
459 }
460 > chatModel.ts
461 > constructor(params: IChatRequestModelParameters) {
462 this._session = params.session;
463 this.message = params.message;
480 this.isTerminalCommand = params.isTerminalCommand ?? false;
481 }
482 > chatModel.ts
483 > adoptTo(session: ChatModel) {
484 this._session = session;
485 }
486 > } chatModel.ts
487 >
488 > class AbstractResponse implements IResponse {
489 > protected _responseParts: IChatProgressResponseContent[];
490 >
491 > /**
492 > * A stringified representation of response data which might be presented to a screenreader or used when copying a response.
493 > * Computed lazily on demand to avoid expensive string rebuilding during streaming.
494 > */
495 > private _responseRepr: string | undefined;
496 >
497 > /**
498 > * Just the markdown content of the response, used for determining the rendering rate of markdown.
499 > * Computed lazily on demand to avoid expensive string rebuilding during streaming.
500 > */
501 > private _markdownContent: string | undefined;
502 >
503 > get value(): IChatProgressResponseContent[] {
504 return this._responseParts;
505 }
506 > chatModel.ts
507 > constructor(value: IChatProgressResponseContent[]) {
508 this._responseParts = value;
509 }
510 > chatModel.ts
511 > toString(): string {
512 if (this._responseRepr === undefined) {
513 this._responseRepr = this.computeRepr();
515 return this._responseRepr;
516 }
517 > chatModel.ts
518 > /**
519 > * _Just_ the content of markdown parts in the response
520 > */
521 > getMarkdown(): string {
522 if (this._markdownContent === undefined) {
523 this._markdownContent = this.computeMarkdownContent();
525 return this._markdownContent;
526 }
527 > chatModel.ts
528 > /**
529 > * The trailing contiguous markdown/inline-reference content of the response,
530 > * skipping any trailing tool calls or empty markdown parts.
531 > */
532 > getFinalResponse(): string {
533 const parts = this._responseParts;
534 // Walk backwards to find where the last contiguous markdown block starts.
577 return segments.join('');
578 }
579 > chatModel.ts
580 > /**
581 > * Invalidate cached representations so they are recomputed on next access.
582 > */
583 > protected _invalidateRepr() {
584 this._responseRepr = undefined;
585 this._markdownContent = undefined;
586 }
587 > chatModel.ts
588 > private computeMarkdownContent(): string {
589 const segments: string[] = [];
590 for (const part of this._responseParts) {
599 return segments.join('');
600 }
601 > chatModel.ts
602 > protected computeRepr(): string {
603 return this.partsToRepr(this._responseParts);
604 }
605 > chatModel.ts
606 > private partsToRepr(parts: readonly IChatProgressResponseContent[]): string {
607 const blocks: string[] = [];
608 let currentBlockSegments: string[] = [];
702 return blocks.join('\n\n');
703 }
704 > chatModel.ts
705 > private inlineRefToRepr(part: IChatContentInlineReference) {
706 if ('uri' in part.inlineReference) {
707 return this.uriToRepr(part.inlineReference.uri);
712 : this.uriToRepr(part.inlineReference);
713 }
714 > chatModel.ts
715 > private getToolInvocationText(toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized): { text: string; isBlock?: boolean } {
716 const getTerminalDisplayInput = (terminalData: ReturnType<typeof migrateLegacyTerminalToolSpecificData>) => terminalData.presentationOverrides?.commandLine
717 ?? terminalData.commandLine.forDisplay
763 return { text, isBlock: true };
764 }
765 > chatModel.ts
766 > private uriToRepr(uri: URI): string {
767 if (uri.scheme === Schemas.http || uri.scheme === Schemas.https) {
768 return uri.toString(false);
771 return basename(uri);
772 }
773 > } chatModel.ts
774 >
775 > /** A view of a subset of a response */
776 > class ResponseView extends AbstractResponse {
777 > constructor(
778 _response: IResponse,
779 public readonly undoStop: string,
789 super(idx === -1 ? _response.value.slice() : _response.value.slice(0, idx));
790 }
791 > } chatModel.ts
792 >
793 > export class Response extends AbstractResponse implements IDisposable {
794 > private readonly _store = new DisposableStore();
795 > private _onDidChangeValue = this._store.add(new Emitter<void>());
796 > private _activeReasoning: { part: IChatThinkingPart; startedAt: number } | undefined;
797 > public get onDidChangeValue() {
798 > return this._onDidChangeValue.event;
799 > }
800 >
801 > private _citations: IChatCodeCitation[] = [];
802 >
803 >
804 > constructor(value: IMarkdownString | ReadonlyArray<SerializedChatResponsePart>) {
805 super(asArray(value).map((v) => (
806 'kind' in v ? v :
809 )));
810 }
811 > chatModel.ts
812 > dispose(): void {
813 this._store.dispose();
814 }
815 > chatModel.ts
816 >
817 > clear(): void {
818 this.finalizeReasoningDuration();
819 this._responseParts = [];
820 this._contentChanged(true);
821 }
822 > chatModel.ts
823 > clearToPreviousToolInvocation(message?: string): void {
824 this.finalizeReasoningDuration();
825 // look through the response parts and find the last tool invocation, then slice the response parts to that point
842 this._contentChanged(true);
843 }
844 > chatModel.ts
845 > updateContent(progress: IChatProgressResponseContent | IChatTextEdit | IChatNotebookEdit | IChatTask | IChatExternalToolInvocationUpdate, quiet?: boolean): void {
846 if (progress.kind !== 'thinking') {
847 this.finalizeReasoningDuration();
968 }
969 }
970 > chatModel.ts
971 > /**
972 > * Persists the duration of the active reasoning interval.
973 > */
974 > finalizeReasoningDuration(): void {
975 if (!this._activeReasoning) {
976 return;
980 this._activeReasoning = undefined;
981 }
982 > chatModel.ts
983 > public addCitation(citation: IChatCodeCitation) {
984 this._citations.push(citation);
985 this._contentChanged();
986 }
987 > chatModel.ts
988 > public resolveInlineReference(resolveId: string, resolvedReference: IChatContentInlineReference): boolean {
989 for (let i = 0; i < this._responseParts.length; i++) {
990 const current = this._responseParts[i];
1004 return false;
1005 }
1006 > chatModel.ts
1007 > private _mergeOrPushTextEditGroup(uri: URI, edits: TextEdit[], done: boolean | undefined, isExternalEdit: boolean | undefined): void {
1008 for (const candidate of this._responseParts) {
1009 if (candidate.kind === 'textEditGroup' && !candidate.done && isEqual(candidate.uri, uri)) {
1015 this._responseParts.push({ kind: 'textEditGroup', uri, edits: [edits], done, isExternalEdit });
1016 }
1017 > chatModel.ts
1018 > private _mergeOrPushNotebookEditGroup(uri: URI, edits: ICellTextEditOperation[] | ICellEditOperation[], done: boolean | undefined, isExternalEdit: boolean | undefined): void {
1019 for (const candidate of this._responseParts) {
1020 if (candidate.kind === 'notebookEditGroup' && !candidate.done && isEqual(candidate.uri, uri)) {
1026 this._responseParts.push({ kind: 'notebookEditGroup', uri, edits: [edits], done, isExternalEdit });
1027 }
1028 > chatModel.ts
1029 > private _handleExternalToolInvocationUpdate(progress: IChatExternalToolInvocationUpdate): void {
1030 // Look for existing invocation in the response parts
1031 const existingInvocation = this._responseParts.findLast(
1085 this._responseParts.push(invocation);
1086 }
1087 > chatModel.ts
1088 > protected override computeRepr(): string {
1089 let repr = super.computeRepr();
1090 if (this._citations.length) {
1093 return repr;
1094 }
1095 > chatModel.ts
1096 > private _contentChanged(quiet?: boolean) {
1097 this._invalidateRepr();
1098 if (!quiet) {
1100 }
1101 }
1102 > } chatModel.ts
1103 >
1104 > export interface IChatResponseModelParameters {
1105 > responseContent: IMarkdownString | ReadonlyArray<SerializedChatResponsePart>;
1106 > session: ChatModel;
1107 > agent?: IChatAgentData;
1108 > slashCommand?: IChatAgentCommand;
1109 > requestId: string;
1110 > timestamp?: number;
1111 > vote?: ChatAgentVoteDirection;
1112 > result?: IChatAgentResult;
1113 > followups?: ReadonlyArray<IChatFollowup>;
1114 > isCompleteAddedRequest?: boolean;
1115 > shouldBeRemovedOnSend?: IChatRequestDisablement;
1116 > shouldBeBlocked?: boolean;
1117 > restoredId?: string;
1118 > modelState?: ResponseModelStateT;
1119 > completionTimestamp?: number | null;
1120 > timeSpentWaiting?: number;
1121 > elapsedMs?: number;
1122 > /**
1123 > * undefined means it will be set later.
1124 > */
1125 > codeBlockInfos: ICodeBlockInfo[] | undefined;
1126 > }
1127 >
1128 > export type ResponseModelStateT =
1129 > | { value: ResponseModelState.Pending }
1130 > | { value: ResponseModelState.NeedsInput }
1131 > | { value: ResponseModelState.Complete | ResponseModelState.Cancelled | ResponseModelState.Failed; completedAt: number };
1132 >
1133 > export class ChatResponseModel extends Disposable implements IChatResponseModel {
1134 > private readonly _onDidChange = this._register(new Emitter<ChatResponseModelChangeReason>());
1135 > readonly onDidChange = this._onDidChange.event;
1136 >
1137 > public readonly id: string;
1138 > public readonly requestId: string;
1139 > private _session: ChatModel;
1140 > private _agent: IChatAgentData | undefined;
1141 > private _slashCommand: IChatAgentCommand | undefined;
1142 > private _modelState = observableValue<ResponseModelStateT>(this, { value: ResponseModelState.Pending });
1143 > private _vote?: ChatAgentVoteDirection;
1144 > private _result?: IChatAgentResult;
1145 > private readonly _usageObs = observableValue<IChatUsage | undefined>(this, undefined);
1146 > private _parentUsage: IChatUsage | undefined;
1147 > private readonly _subagentCopilotCredits = new Map<string, number>();
1148 > private readonly _completionTokenCountObs = observableValue<number | undefined>(this, undefined);
1149 > private _shouldBeRemovedOnSend: IChatRequestDisablement | undefined;
1150 > public readonly isCompleteAddedRequest: boolean;
1151 > private readonly _shouldBeBlocked = observableValue<boolean>(this, false);
1152 > private readonly _timestamp: number;
1153 > private _completionTimestamp: number | undefined;
1154 > private _timeSpentWaitingAccumulator: number;
1155 > private _elapsedMs: number | undefined;
1156 >
1157 > public confirmationAdjustedTimestamp: IObservable<number>;
1158 >
1159 > public get shouldBeBlocked(): IObservable<boolean> {
1160 > return this._shouldBeBlocked;
1161 > }
1162 >
1163 > public get request(): IChatRequestModel | undefined {
1164 return this.session.getRequests().find(r => r.id === this.requestId);
1165 }
1166 > chatModel.ts
1167 > public get session() {
1168 return this._session;
1169 }
1170 > chatModel.ts
1171 > public get shouldBeRemovedOnSend() {
1172 return this._shouldBeRemovedOnSend;
1173 }
1174 > chatModel.ts
1175 > public get isComplete(): boolean {
1176 return this._modelState.get().value !== ResponseModelState.Pending && this._modelState.get().value !== ResponseModelState.NeedsInput;
1177 }
1178 > chatModel.ts
1179 > public get timestamp(): number {
1180 return this._timestamp;
1181 }
1182 > chatModel.ts
1183 > public set shouldBeRemovedOnSend(disablement: IChatRequestDisablement | undefined) {
1184 if (this._shouldBeRemovedOnSend === disablement) {
1185 return;
1189 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1190 }
1191 > chatModel.ts
1192 > public get isCanceled(): boolean {
1193 return this._modelState.get().value === ResponseModelState.Cancelled;
1194 }
1195 > chatModel.ts
1196 > public get completedAt(): number | undefined {
1197 const state = this._modelState.get();
1198 if (state.value === ResponseModelState.Complete || state.value === ResponseModelState.Cancelled || state.value === ResponseModelState.Failed) {
1201 return undefined;
1202 }
1203 > chatModel.ts
1204 > public get completionTimestamp(): number | undefined {
1205 return this._completionTimestamp;
1206 }
1207 > chatModel.ts
1208 > public get state(): ResponseModelState {
1209 const state = this._modelState.get().value;
1210 if (state === ResponseModelState.Complete && !!this._result?.errorDetails && this.result?.errorDetails?.code !== 'canceled') {
1215 return state;
1216 }
1217 > chatModel.ts
1218 > public get stateT(): ResponseModelStateT {
1219 return this._modelState.get();
1220 }
1221 > chatModel.ts
1222 > public get vote(): ChatAgentVoteDirection | undefined {
1223 return this._vote;
1224 }
1225 > chatModel.ts
1226 > public get followups(): IChatFollowup[] | undefined {
1227 return this._followups;
1228 }
1229 > chatModel.ts
1230 > private _response: Response;
1231 > private _finalizedResponse?: IResponse;
1232 > public get entireResponse(): IResponse {
1233 return this._finalizedResponse || this._response;
1234 }
1235 > chatModel.ts
1236 > public get result(): IChatAgentResult | undefined {
1237 return this._result;
1238 }
1239 > chatModel.ts
1240 > public get usage(): IChatUsage | undefined {
1241 return this._usageObs.get();
1242 }
1243 > chatModel.ts
1244 > public get usageObs(): IObservable<IChatUsage | undefined> {
1245 return this._usageObs;
1246 }
1247 > chatModel.ts
1248 > public get completionTokenCount(): number | undefined {
1249 return this._completionTokenCountObs.get();
1250 }
1251 > chatModel.ts
1252 > public get completionTokenCountObs(): IObservable<number | undefined> {
1253 return this._completionTokenCountObs;
1254 }
1255 > chatModel.ts
1256 > public get elapsedMs(): number | undefined {
1257 return this._elapsedMs;
1258 }
1259 > chatModel.ts
1260 > public get username(): string {
1261 return this.session.responderUsername;
1262 }
1263 > chatModel.ts
1264 > private _followups?: IChatFollowup[];
1265 >
1266 > public get agent(): IChatAgentData | undefined {
1267 return this._agent;
1268 }
1269 > chatModel.ts
1270 > public get slashCommand(): IChatAgentCommand | undefined {
1271 return this._slashCommand;
1272 }
1273 > chatModel.ts
1274 > private _agentOrSlashCommandDetected: boolean | undefined;
1275 > public get agentOrSlashCommandDetected(): boolean {
1276 return this._agentOrSlashCommandDetected ?? false;
1277 }
1278 > chatModel.ts
1279 > private _usedContext: IChatUsedContext | undefined;
1280 > public get usedContext(): IChatUsedContext | undefined {
1281 return this._usedContext;
1282 }
1283 > chatModel.ts
1284 > private readonly _contentReferences: IChatContentReference[] = [];
1285 > public get contentReferences(): ReadonlyArray<IChatContentReference> {
1286 return Array.from(this._contentReferences);
1287 }
1288 > chatModel.ts
1289 > private readonly _codeCitations: IChatCodeCitation[] = [];
1290 > public get codeCitations(): ReadonlyArray<IChatCodeCitation> {
1291 return this._codeCitations;
1292 }
1293 > chatModel.ts
1294 > private readonly _progressMessages: IChatProgressMessage[] = [];
1295 > public get progressMessages(): ReadonlyArray<IChatProgressMessage> {
1296 return this._progressMessages;
1297 }
1298 > chatModel.ts
1299 > private _isStale: boolean = false;
1300 > public get isStale(): boolean {
1301 return this._isStale;
1302 }
1303 > chatModel.ts
1304 >
1305 > readonly isPendingConfirmation: IObservable<{ startedWaitingAt: number; detail?: string } | undefined>;
1306 >
1307 > readonly isInProgress: IObservable<boolean>;
1308 >
1309 > /**
1310 > * True whenever this response has not reached a terminal state yet.
1311 > * Unlike {@link isInProgress}, this remains true during tool confirmations,
1312 > * elicitations, and any other intermediate state. It only becomes false when
1313 > * the response completes, is cancelled, or fails.
1314 > */
1315 > readonly isIncomplete: IObservable<boolean>;
1316 >
1317 > private _responseView?: ResponseView;
1318 > public get response(): IResponse {
1319 const undoStop = this._shouldBeRemovedOnSend?.afterUndoStop;
1320 if (!undoStop) {
1328 return this._responseView;
1329 }
1330 > chatModel.ts
1331 > private _codeBlockInfos: ICodeBlockInfo[] | undefined;
1332 > public get codeBlockInfos(): ICodeBlockInfo[] | undefined {
1333 return this._codeBlockInfos;
1334 }
1335 > chatModel.ts
1336 > constructor(params: IChatResponseModelParameters) {
1337 super();
1338
1439 }).recomputeInitiallyAndOnChange(this._store);
1440 }
1441 > chatModel.ts
1442 > initializeCodeBlockInfos(codeBlockInfo: ICodeBlockInfo[]): void {
1443 if (this._codeBlockInfos) {
1444 throw new BugIndicatingError('Code block infos have already been initialized');
1446 this._codeBlockInfos = [...codeBlockInfo];
1447 }
1448 > chatModel.ts
1449 > setBlockedState(isBlocked: boolean): void {
1450 this._shouldBeBlocked.set(isBlocked, undefined);
1451 }
1452 > chatModel.ts
1453 > /**
1454 > * Apply a progress update to the actual response content.
1455 > */
1456 > updateContent(responsePart: IChatProgressResponseContent | IChatTextEdit | IChatNotebookEdit | IChatExternalToolInvocationUpdate, quiet?: boolean) {
1457 this._response.updateContent(responsePart, quiet);
1458 }
1459 > chatModel.ts
1460 > resolveInlineReference(resolveId: string, resolvedReference: IChatContentInlineReference): boolean {
1461 return this._response.resolveInlineReference(resolveId, resolvedReference);
1462 }
1463 > chatModel.ts
1464 > /**
1465 > * Adds an undo stop at the current position in the stream.
1466 > */
1467 > addUndoStop(undoStop: IChatUndoStop) {
1468 this._onDidChange.fire({ reason: 'undoStop', id: undoStop.id });
1469 this._response.updateContent(undoStop, true);
1470 }
1471 > chatModel.ts
1472 > /**
1473 > * Apply one of the progress updates that are not part of the actual response content.
1474 > */
1475 > applyReference(progress: IChatUsedContext | IChatContentReference) {
1476 if (progress.kind === 'usedContext') {
1477 this._usedContext = progress;
1481 }
1482 }
1483 > chatModel.ts
1484 > applyCodeCitation(progress: IChatCodeCitation) {
1485 this._codeCitations.push(progress);
1486 this._response.addCitation(progress);
1487 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1488 }
1489 > chatModel.ts
1490 > setAgent(agent: IChatAgentData, slashCommand?: IChatAgentCommand) {
1491 this._agent = agent;
1492 this._slashCommand = slashCommand;
1494 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1495 }
1496 > chatModel.ts
1497 > setResult(result: IChatAgentResult): void {
1498 // If already cancelled, discard error details from late-arriving agent responses.
1499 if (this.isCanceled && result.errorDetails) {
1505 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1506 }
1507 > chatModel.ts
1508 > setUsage(usage: IChatUsage): void {
1509 this._parentUsage = usage;
1510 this._setUsage(this._withSubagentCopilotCredits(usage), true);
1511 }
1512 > chatModel.ts
1513 > setSubagentCopilotCredits(subagentCallId: string, copilotCredits: number): void {
1514 const currentCredits = this._subagentCopilotCredits.get(subagentCallId);
1515 if (!Number.isFinite(copilotCredits) || copilotCredits < 0 || (currentCredits !== undefined && copilotCredits <= currentCredits)) {
1520 this._setUsage(this._withSubagentCopilotCredits(usage), false);
1521 }
1522 > chatModel.ts
1523 > private _withSubagentCopilotCredits(usage: IChatUsage): IChatUsage {
1524 let subagentCopilotCredits = 0;
1525 for (const credits of this._subagentCopilotCredits.values()) {
1530 : { ...usage, copilotCredits: (usage.copilotCredits ?? 0) + subagentCopilotCredits };
1531 }
1532 > chatModel.ts
1533 > private _setUsage(usage: IChatUsage, countCompletionTokens: boolean): void {
1534 if (this.isSameUsage(usage)) {
1535 return;
1543 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1544 }
1545 > chatModel.ts
1546 > setElapsedMs(elapsedMs: number): void {
1547 this._elapsedMs = Math.max(0, elapsedMs);
1548 }
1549 > chatModel.ts
1550 > private isSameUsage(usage: IChatUsage): boolean {
1551 const currentUsage = this._usageObs.get();
1552 return !!currentUsage
1557 && equals(currentUsage.promptTokenDetails, usage.promptTokenDetails);
1558 }
1559 > chatModel.ts
1560 > complete(completedAt = Date.now()): void {
1561 this._complete(completedAt, completedAt);
1562 }
1563 > chatModel.ts
1564 > completeWithoutTimestamp(): void {
1565 this._complete(Date.now(), undefined);
1566 }
1567 > chatModel.ts
1568 > private _complete(completedAt: number, completionTimestamp: number | undefined): void {
1569 // No-op if it's already complete
1570 if (this.isComplete) {
1585 this._onDidChange.fire({ reason: 'completedRequest' });
1586 }
1587 > chatModel.ts
1588 > cancel(): void {
1589 this._response.finalizeReasoningDuration();
1590 // Transition any tool invocations that are still streaming partial
1608 this._onDidChange.fire({ reason: 'completedRequest' });
1609 }
1610 > chatModel.ts
1611 > setFollowups(followups: IChatFollowup[] | undefined): void {
1612 this._followups = followups;
1613 this._onDidChange.fire(defaultChatResponseModelChangeReason); // Fire so that command followups get rendered on the row
1614 }
1615 > chatModel.ts
1616 > setVote(vote: ChatAgentVoteDirection): void {
1617 this._vote = vote;
1618 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1619 }
1620 > chatModel.ts
1621 > setEditApplied(edit: IChatTextEditGroup, editCount: number): boolean {
1622 if (!this.response.value.includes(edit)) {
1623 return false;
1630 return true;
1631 }
1632 > chatModel.ts
1633 > adoptTo(session: ChatModel) {
1634 this._session = session;
1635 this._onDidChange.fire(defaultChatResponseModelChangeReason);
1636 }
1637 > chatModel.ts
1638 >
1639 > finalizeUndoState(): void {
1640 this._finalizedResponse = this.response;
1641 this._responseView = undefined;
1642 this._shouldBeRemovedOnSend = undefined;
1643 }
1644 > chatModel.ts
1645 > override dispose(): void {
1646 super.dispose();
1647 this._response.clear();
1650 }
1651 }
1652 > chatModel.ts
1653 > toJSON(): Omit<ISerializableChatResponseData, 'timestamp'> {
1654 const modelState = this._modelState.get();
1655 const pendingConfirmation = this.isPendingConfirmation.get();
1676 } satisfies WithDefinedProps<Omit<ISerializableChatResponseData, 'timestamp'>>;
1677 }
1678 > } chatModel.ts
1679 >
1680 >
1681 > export interface IChatRequestDisablement {
1682 > requestId: string;
1683 > afterUndoStop?: string;
1684 > }
1685 >
1686 > /**
1687 > * Information about a chat request that needs user input to continue.
1688 > */
1689 > export interface IChatRequestNeedsInputInfo {
1690 > /** The chat session title */
1691 > readonly title: string;
1692 > /** Optional detail message, e.g., "<toolname> needs approval to run." */
1693 > readonly detail?: string;
1694 > }
1695 >
1696 > export interface IChatModel extends IDisposable {
1697 > readonly onDidDispose: Event<void>;
1698 > readonly onDidChange: Event<IChatChangeEvent>;
1699 >
1700 > readonly sessionResource: URI;
1701 > /** @deprecated Use {@link sessionResource} instead */
1702 > readonly sessionId: string;
1703 >
1704 > /** Milliseconds timestamp this chat model was created. */
1705 > readonly timestamp: number;
1706 > readonly lastMessageDate: number;
1707 > readonly timing: IChatSessionTiming;
1708 > readonly initialLocation: ChatAgentLocation;
1709 > readonly title: string;
1710 > readonly hasCustomTitle: boolean;
1711 > readonly responderUsername: string;
1712 > /** True whenever a request is currently running */
1713 > readonly requestInProgress: IObservable<boolean>;
1714 > /** True whenever the last request has not reached a terminal state, regardless of intermediate states like tool calls or elicitations */
1715 > readonly hasActiveRequest: IObservable<boolean>;
1716 > /** Provides session information when a request needs user interaction to continue */
1717 > readonly requestNeedsInput: IObservable<IChatRequestNeedsInputInfo | undefined>;
1718 > readonly isReadOnly: IObservable<boolean>;
1719 > readonly inputPlaceholder?: string;
1720 > readonly editingSession?: IChatEditingSession | undefined;
1721 > readonly checkpoint: IChatRequestModel | undefined;
1722 > startEditingSession(isGlobalEditingSession?: boolean, transferFromSession?: IChatEditingSession): void;
1723 > /** Input model for managing input state */
1724 > readonly inputModel: IInputModel;
1725 > readonly hasRequests: boolean;
1726 > readonly lastRequest: IChatRequestModel | undefined;
1727 > /** Whether this model will be kept alive while it is running or has edits */
1728 > readonly willKeepAlive: boolean;
1729 > readonly lastRequestObs: IObservable<IChatRequestModel | undefined>;
1730 > /** Total copilot credits consumed across all turns in this session. */
1731 > readonly sessionCost: number;
1732 > getRequests(): IChatRequestModel[];
1733 > setCheckpoint(requestId: string | undefined): void;
1734 >
1735 > toExport(): IExportableChatData;
1736 > toJSON(): ISerializableChatData;
1737 >
1738 > readonly repoData: IExportableRepoData | undefined;
1739 > setRepoData(data: IExportableRepoData | undefined): void;
1740 >
1741 > /**
1742 > * The working directory URI associated with this session.
1743 > * Only set in the sessions/agents window context.
1744 > */
1745 > readonly workingDirectory: URI | undefined;
1746 > setWorkingDirectory(uri: URI | undefined): void;
1747 >
1748 > readonly onDidChangePendingRequests: Event<void>;
1749 > getPendingRequests(): readonly IChatPendingRequest[];
1750 > }
1751 >
1752 > export interface ISerializableChatsData {
1753 > [sessionId: string]: ISerializableChatData;
1754 > }
1755 >
1756 > export type ISerializableChatAgentData = UriDto<IChatAgentData>;
1757 >
1758 > interface ISerializableChatResponseData {
1759 > responseId?: string;
1760 > result?: IChatAgentResult; // Optional for backcompat
1761 > responseMarkdownInfo?: ISerializableMarkdownInfo[];
1762 > followups?: ReadonlyArray<IChatFollowup>;
1763 > modelState?: ResponseModelStateT;
1764 > vote?: ChatAgentVoteDirection;
1765 > timestamp?: number;
1766 > responseTimestamp?: number;
1767 > slashCommand?: IChatAgentCommand;
1768 > /** For backward compat: should be optional */
1769 > usedContext?: IChatUsedContext;
1770 > contentReferences?: ReadonlyArray<IChatContentReference>;
1771 > codeCitations?: ReadonlyArray<IChatCodeCitation>;
1772 > timeSpentWaiting?: number;
1773 > promptTokens?: number;
1774 > completionTokens?: number;
1775 > outputBuffer?: number;
1776 > promptTokenDetails?: readonly IChatUsagePromptTokenDetail[];
1777 > copilotCredits?: number;
1778 > elapsedMs?: number;
1779 > }
1780 >
1781 > export type SerializedChatResponsePart = IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference | IChatAgentMarkdownContentWithVulnerability | IChatThinkingPart | IChatProgressResponseContentSerialized | IChatQuestionCarousel | IChatPlanReview | IChatDisabledClaudeHooksPart;
1782 >
1783 > export interface ISerializableChatRequestData extends ISerializableChatResponseData {
1784 > requestId: string;
1785 > message: string | IParsedChatRequest; // string => old format
1786 > /** Is really like "prompt data". This is the message in the format in which the agent gets it + variable values. */
1787 > variableData: IChatRequestVariableData;
1788 > response: ReadonlyArray<SerializedChatResponsePart> | undefined;
1789 >
1790 > /**Old, persisted name for shouldBeRemovedOnSend */
1791 > isHidden?: boolean;
1792 > shouldBeRemovedOnSend?: IChatRequestDisablement;
1793 > agent?: ISerializableChatAgentData;
1794 > // responseErrorDetails: IChatResponseErrorDetails | undefined;
1795 > /** @deprecated modelState is used instead now */
1796 > isCanceled?: boolean;
1797 > timestamp?: number;
1798 > confirmation?: string;
1799 > editedFileEvents?: IChatAgentEditedFileEvent[];
1800 > modelId?: string;
1801 > modeInfo?: IChatRequestModeInfo;
1802 > isSystemInitiated?: boolean;
1803 > systemInitiatedLabel?: string;
1804 > terminalExecutionId?: string;
1805 > }
1806 >
1807 > export interface ISerializableMarkdownInfo {
1808 > readonly suggestionId: EditSuggestionId;
1809 > }
1810 >
1811 > /**
1812 > * Repository state captured for chat session export.
1813 > * Enables reproducing the workspace state by cloning, checking out the commit, and applying diffs.
1814 > */
1815 > export interface IExportableRepoData {
1816 > /**
1817 > * Classification of the workspace's version control state.
1818 > * - `remote-git`: Git repo with a configured remote URL
1819 > * - `local-git`: Git repo without any remote (local only)
1820 > * - `plain-folder`: Not a git repository
1821 > */
1822 > workspaceType: 'remote-git' | 'local-git' | 'plain-folder';
1823 >
1824 > /**
1825 > * Sync status between local and remote.
1826 > * - `synced`: Local HEAD matches remote tracking branch (fully pushed)
1827 > * - `unpushed`: Local has commits not pushed to the remote tracking branch
1828 > * - `unpublished`: Local branch has no remote tracking branch configured
1829 > * - `local-only`: No remote configured (local git repo only)
1830 > * - `no-git`: Not a git repository
1831 > */
1832 > syncStatus: 'synced' | 'unpushed' | 'unpublished' | 'local-only' | 'no-git';
1833 >
1834 > /**
1835 > * Remote URL of the repository (e.g., https://github.com/org/repo.git).
1836 > * Undefined if no remote is configured.
1837 > */
1838 > remoteUrl?: string;
1839 >
1840 > /**
1841 > * Vendor/host of the remote repository.
1842 > * Undefined if no remote is configured.
1843 > */
1844 > remoteVendor?: 'github' | 'ado' | 'other';
1845 >
1846 > /**
1847 > * Remote tracking branch for the current branch (e.g., "origin/feature/my-work").
1848 > * Undefined if branch is unpublished or no remote.
1849 > */
1850 > remoteTrackingBranch?: string;
1851 >
1852 > /**
1853 > * Default remote branch used as base for unpublished branches (e.g., "origin/main").
1854 > * Helpful for computing merge-base when branch has no tracking.
1855 > */
1856 > remoteBaseBranch?: string;
1857 >
1858 > /**
1859 > * Commit hash of the remote tracking branch HEAD.
1860 > * Undefined if branch has no remote tracking branch.
1861 > */
1862 > remoteHeadCommit?: string;
1863 >
1864 > /**
1865 > * Name of the current local branch (e.g., "feature/my-work").
1866 > */
1867 > localBranch?: string;
1868 >
1869 > /**
1870 > * Commit hash of the local HEAD when captured.
1871 > */
1872 > localHeadCommit?: string;
1873 >
1874 > /**
1875 > * Working tree diffs (uncommitted changes).
1876 > */
1877 > diffs?: IExportableRepoDiff[];
1878 >
1879 > /**
1880 > * Status of the diffs collection.
1881 > * - `included`: Diffs were successfully captured and included
1882 > * - `tooManyChanges`: Diffs skipped because >100 files changed (degenerate case like mass renames)
1883 > * - `tooLarge`: Diffs skipped because total size exceeded 900KB
1884 > * - `trimmedForStorage`: Diffs were trimmed to save storage (older session)
1885 > * - `noChanges`: No working tree changes detected
1886 > * - `notCaptured`: Diffs not captured (default/undefined case)
1887 > */
1888 > diffsStatus?: 'included' | 'tooManyChanges' | 'tooLarge' | 'trimmedForStorage' | 'noChanges' | 'notCaptured';
1889 >
1890 > /**
1891 > * Number of changed files detected, even if diffs were not included.
1892 > */
1893 > changedFileCount?: number;
1894 > }
1895 >
1896 > /**
1897 > * A file change exported as a unified diff patch compatible with `git apply`.
1898 > */
1899 > export interface IExportableRepoDiff {
1900 > relativePath: string;
1901 > changeType: 'added' | 'modified' | 'deleted' | 'renamed';
1902 > oldRelativePath?: string;
1903 > unifiedDiff?: string;
1904 > status: string;
1905 > }
1906 >
1907 > export interface IExportableChatData {
1908 > initialLocation: ChatAgentLocation | undefined;
1909 > requests: ISerializableChatRequestData[];
1910 > responderUsername: string;
1911 > }
1912 >
1913 > /*
1914 > NOTE: every time the serialized data format is updated, we need to create a new interface, because we may need to handle any old data format when parsing.
1915 > */
1916 >
1917 > export interface ISerializableChatData1 extends IExportableChatData {
1918 > sessionId: string;
1919 > creationDate: number;
1920 > }
1921 >
1922 > export interface ISerializableChatData2 extends ISerializableChatData1 {
1923 > version: 2;
1924 > computedTitle: string | undefined;
1925 > }
1926 >
1927 > export interface ISerializableChatData3 extends Omit<ISerializableChatData2, 'version' | 'computedTitle'> {
1928 > version: 3;
1929 > customTitle: string | undefined;
1930 > /** Whether the session had pending edits when it was stored. */
1931 > hasPendingEdits?: boolean;
1932 > /** Current draft input state (added later, fully backwards compatible) */
1933 > inputState?: ISerializableChatModelInputState;
1934 > repoData?: IExportableRepoData;
1935 > /** Pending requests that were queued but not yet processed */
1936 > pendingRequests?: ISerializablePendingRequestData[];
1937 > /** The working directory URI associated with this session (sessions/agents window). */
1938 > workingDirectory?: string;
1939 > }
1940 >
1941 > /**
1942 > * Input model for managing chat input state independently from the chat model.
1943 > * This keeps display logic separated from the core chat model.
1944 > *
1945 > * The input model:
1946 > * - Manages the current draft state (text, attachments, mode, model selection, cursor/selection)
1947 > * - Provides an observable interface for reactive UI updates
1948 > * - Automatically persists through the chat model's serialization
1949 > * - Enables bidirectional sync between the UI (ChatInputPart) and the model
1950 > * - Uses `undefined` state to indicate no persisted state (new/empty chat)
1951 > *
1952 > * This architecture ensures that:
1953 > * - Input state is preserved when moving chats between editor/sidebar/window
1954 > * - No manual state transfer is needed when switching contexts
1955 > * - The UI stays in sync with the persisted state
1956 > * - New chats use UI defaults (persisted preferences) instead of hardcoded values
1957 > */
1958 > export interface IInputModel {
1959 > /** Observable for current input state (undefined for new/uninitialized chats) */
1960 > readonly state: IObservable<IChatModelInputState | undefined>;
1961 >
1962 > /** Update the input state (partial update) */
1963 > setState(state: Partial<IChatModelInputState>): void;
1964 >
1965 > /** Clear input state (after sending or clearing) */
1966 > clearState(): void;
1967 >
1968 > /** Serializes the state */
1969 > toJSON(): ISerializableChatModelInputState | undefined;
1970 > }
1971 >
1972 > /**
1973 > * Represents the current state of the chat input that hasn't been sent yet.
1974 > * This is the "draft" state that should be preserved across sessions.
1975 > */
1976 > export interface IChatModelInputState {
1977 > /** Current attachments in the input */
1978 > attachments: readonly IChatRequestVariableEntry[];
1979 >
1980 > /** Currently selected chat mode */
1981 > mode: {
1982 > /** Mode ID (e.g., 'ask', 'edit', 'agent', or custom mode ID) */
1983 > id: string;
1984 > /** Mode kind for builtin modes */
1985 > kind: ChatModeKind | undefined;
1986 > };
1987 >
1988 > /** Currently selected language model, if any */
1989 > selectedModel: ILanguageModelChatMetadataAndIdentifier | undefined;
1990 >
1991 > /**
1992 > * Configuration (e.g. context size, thinking effort) for the selected
1993 > * model, captured so it can be restored alongside the model when the
1994 > * session is reopened.
1995 > */
1996 > modelConfiguration?: IStringDictionary<unknown>;
1997 >
1998 > /** Current input text */
1999 > inputText: string;
2000 >
2001 > /** Current selection ranges */
2002 > selections: ISelection[];
2003 >
2004 > /** Current permission level for tool auto-approval */
2005 > permissionLevel?: ChatPermissionLevel;
2006 >
2007 > /** Contributed stored state */
2008 > contrib: Record<string, unknown>;
2009 > }
2010 >
2011 > /**
2012 > * Serializable version of IChatModelInputState
2013 > */
2014 > export interface ISerializableChatModelInputState {
2015 > attachments: readonly IChatRequestVariableEntry[];
2016 > mode: {
2017 > id: string;
2018 > kind: ChatModeKind | undefined;
2019 > };
2020 > selectedModel: {
2021 > identifier: string;
2022 > metadata: ILanguageModelChatMetadata;
2023 > /**
2024 > * Configuration (e.g. context size, thinking effort) for the selected
2025 > * model, captured so it can be restored alongside the model when the
2026 > * session is reopened.
2027 > */
2028 > modelConfiguration?: IStringDictionary<unknown>;
2029 > } | undefined;
2030 > inputText: string;
2031 > selections: ISelection[];
2032 > permissionLevel?: ChatPermissionLevel;
2033 > contrib: Record<string, unknown>;
2034 > }
2035 >
2036 > /**
2037 > * Legacy shape of {@link ISerializableChatModelInputState} as persisted by older
2038 > * versions, where the selected model's configuration was stored as a sibling
2039 > * `modelConfiguration` field instead of nested inside `selectedModel`. Retained
2040 > * so sessions serialized in the old format can still be read.
2041 > */
2042 > interface ILegacySerializableChatModelInputState extends ISerializableChatModelInputState {
2043 > modelConfiguration?: IStringDictionary<unknown>;
2044 > }
2045 >
2046 > /**
2047 > * Chat data that has been parsed and normalized to the current format.
2048 > */
2049 > export type ISerializableChatData = ISerializableChatData3;
2050 >
2051 > export type IChatDataSerializerLog = ObjectMutationLog<IChatModel, ISerializableChatData>;
2052 >
2053 > export interface ISerializedChatDataReference {
2054 > value: ISerializableChatData | IExportableChatData;
2055 > serializer: IChatDataSerializerLog;
2056 > }
2057 >
2058 > /**
2059 > * Chat data that has been loaded but not normalized, and could be any format
2060 > */
2061 > export type ISerializableChatDataIn = ISerializableChatData1 | ISerializableChatData2 | ISerializableChatData3;
2062 >
2063 > /**
2064 > * Normalize chat data from storage to the current format.
2065 > * TODO- ChatModel#_deserialize and reviveSerializedAgent also still do some normalization and maybe that should be done in here too.
2066 > */
2067 > export function normalizeSerializableChatData(raw: ISerializableChatDataIn): ISerializableChatData {
2068 normalizeOldFields(raw);
2069
2086 return raw;
2087 }
2088 > chatModel.ts
2089 function normalizeOldFields(raw: ISerializableChatDataIn): void {
2090 // Fill in fields that very old chat data may be missing
2102 }
2103 }
2104 > chatModel.ts
2105 function getLastYearDate(): number {
2106 const lastYearDate = new Date();
2108 return lastYearDate.getTime();
2109 }
2110 > chatModel.ts
2111 > export function isExportableSessionData(obj: unknown): obj is IExportableChatData {
2112 return !!obj &&
2113 Array.isArray((obj as IExportableChatData).requests) &&
2114 typeof (obj as IExportableChatData).responderUsername === 'string';
2115 }
2116 > chatModel.ts
2117 > export function isSerializableSessionData(obj: unknown): obj is ISerializableChatData {
2118 const data = obj as ISerializableChatData;
2119 return isExportableSessionData(obj) &&
2124 );
2125 }
2126 > chatModel.ts
2127 > export type IChatChangeEvent =
2128 > | IChatInitEvent
2129 > | IChatAddRequestEvent | IChatChangedRequestEvent | IChatRemoveRequestEvent
2130 > | IChatAddResponseEvent
2131 > | IChatSetAgentEvent
2132 > | IChatMoveEvent
2133 > | IChatSetHiddenEvent
2134 > | IChatCompletedRequestEvent
2135 > | IChatSetCustomTitleEvent
2136 > ;
2137 >
2138 > export interface IChatAddRequestEvent {
2139 > kind: 'addRequest';
2140 > request: IChatRequestModel;
2141 > }
2142 >
2143 > export interface IChatChangedRequestEvent {
2144 > kind: 'changedRequest';
2145 > request: IChatRequestModel;
2146 > }
2147 >
2148 > export interface IChatCompletedRequestEvent {
2149 > kind: 'completedRequest';
2150 > request: IChatRequestModel;
2151 > }
2152 >
2153 > export interface IChatAddResponseEvent {
2154 > kind: 'addResponse';
2155 > response: IChatResponseModel;
2156 > }
2157 >
2158 > export const enum ChatRequestRemovalReason {
2159 > /**
2160 > * "Normal" remove
2161 > */
2162 > Removal,
2163 >
2164 > /**
2165 > * Removed because the request will be resent
2166 > */
2167 > Resend,
2168 >
2169 > /**
2170 > * Remove because the request is moving to another model
2171 > */
2172 > Adoption
2173 > }
2174 >
2175 > export interface IChatRemoveRequestEvent {
2176 > kind: 'removeRequest';
2177 > requestId: string;
2178 > responseId?: string;
2179 > reason: ChatRequestRemovalReason;
2180 > }
2181 >
2182 > export interface IChatSetHiddenEvent {
2183 > kind: 'setHidden';
2184 > }
2185 >
2186 > export interface IChatMoveEvent {
2187 > kind: 'move';
2188 > target: URI;
2189 > range: IRange;
2190 > }
2191 >
2192 > export interface IChatSetAgentEvent {
2193 > kind: 'setAgent';
2194 > agent: IChatAgentData;
2195 > command?: IChatAgentCommand;
2196 > }
2197 >
2198 > export interface IChatSetCustomTitleEvent {
2199 > kind: 'setCustomTitle';
2200 > title: string;
2201 > }
2202 >
2203 > export interface IChatInitEvent {
2204 > kind: 'initialize';
2205 > }
2206 >
2207 > /**
2208 > * Internal implementation of IInputModel
2209 > */
2210 > class InputModel implements IInputModel {
2211 > private readonly _state: ReturnType<typeof observableValue<IChatModelInputState | undefined>>;
2212 > readonly state: IObservable<IChatModelInputState | undefined>;
2213 >
2214 > constructor(initialState: IChatModelInputState | undefined, private readonly logger: ILogService, private readonly sessionId: string) {
2215 this._state = observableValueOpts({ debugName: 'inputModelState', equalsFn: equals }, initialState);
2216 this.state = this._state;
2217 }
2218 > chatModel.ts
2219 > setState(state: Partial<IChatModelInputState>): void {
2220 const current = this._state.get();
2221 _logChangesToStateModel(state, current, this.logger, this.sessionId);
2232 }, undefined);
2233 }
2234 > chatModel.ts
2235 > clearState(): void {
2236 this._state.set(undefined, undefined);
2237 }
2238 > chatModel.ts
2239 > toJSON(): ISerializableChatModelInputState | undefined {
2240 const value = this.state.get();
2241 if (!value) {
2269 };
2270 }
2271 > } chatModel.ts
2272 >
2273 > export class ChatModel extends Disposable implements IChatModel {
2274 > static getDefaultTitle(requests: (ISerializableChatRequestData | IChatRequestModel)[]): string {
2275 > const firstRequestMessage = requests.at(0)?.message ?? '';
2276 > const message = typeof firstRequestMessage === 'string' ?
2277 firstRequestMessage :
2278 firstRequestMessage.text;
2279 > return message.split('\n')[0].substring(0, 200); chatModel.ts
2280 > }
2281 >
2282 > private readonly _onDidDispose = this._register(new Emitter<void>());
2283 > readonly onDidDispose = this._onDidDispose.event;
2284 >
2285 > private readonly _onDidChange = this._register(new Emitter<IChatChangeEvent>());
2286 > readonly onDidChange = this._onDidChange.event;
2287 >
2288 > private readonly _pendingRequests: IChatPendingRequest[] = [];
2289 > private readonly _onDidChangePendingRequests = this._register(new Emitter<void>());
2290 > readonly onDidChangePendingRequests = this._onDidChangePendingRequests.event;
2291 >
2292 > private _requests: ChatRequestModel[];
2293 >
2294 > private _repoData: IExportableRepoData | undefined;
2295 > public get repoData(): IExportableRepoData | undefined {
2296 return this._repoData;
2297 }
2298 > public setRepoData(data: IExportableRepoData | undefined): void { chatModel.ts
2299 this._repoData = data;
2300 }
2301 > chatModel.ts
2302 > private _workingDirectory: URI | undefined;
2303 > public get workingDirectory(): URI | undefined {
2304 return this._workingDirectory;
2305 }
2306 > public setWorkingDirectory(uri: URI | undefined): void { chatModel.ts
2307 this._workingDirectory = uri;
2308 }
2309 > chatModel.ts
2310 > getPendingRequests(): readonly IChatPendingRequest[] {
2311 return this._pendingRequests;
2312 }
2313 > chatModel.ts
2314 > setPendingRequests(requests: readonly { requestId: string; kind: ChatRequestQueueKind }[]): void {
2315 const existingMap = new Map(this._pendingRequests.map(p => [p.request.id, p]));
2316 const newPending: IChatPendingRequest[] = [];
2326 this._onDidChangePendingRequests.fire();
2327 }
2328 > chatModel.ts
2329 > /**
2330 > * @internal Used by ChatService to atomically replace the pending request queue.
2331 > */
2332 > replacePendingRequests(requests: readonly IChatPendingRequest[]): void {
2333 if (this._pendingRequests.length === requests.length && requests.every((request, index) => this._pendingRequests[index] === request)) {
2334 return;
2338 this._onDidChangePendingRequests.fire();
2339 }
2340 > chatModel.ts
2341 > /**
2342 > * @internal Used by ChatService to add a request to the queue.
2343 > * Steering messages are placed before queued messages.
2344 > */
2345 > addPendingRequest(request: ChatRequestModel, kind: ChatRequestQueueKind, sendOptions: IChatSendRequestOptions): IChatPendingRequest {
2346 const pendingRequest: IChatPendingRequest = {
2347 request,
2369 return pendingRequest;
2370 }
2371 > chatModel.ts
2372 > /**
2373 > * @internal Used by ChatService to remove a pending request
2374 > */
2375 > removePendingRequest(id: string): void {
2376 const index = this._pendingRequests.findIndex(r => r.request.id === id);
2377 if (index !== -1) {
2380 }
2381 }
2382 > chatModel.ts
2383 > /**
2384 > * @internal Used by ChatService to dequeue the next pending request
2385 > */
2386 > dequeuePendingRequest(): IChatPendingRequest | undefined {
2387 const request = this._pendingRequests.shift();
2388 if (request) {
2391 return request;
2392 }
2393 > chatModel.ts
2394 > /**
2395 > * @internal Used by ChatService to dequeue all consecutive steering requests at the front of the queue.
2396 > * Returns an empty array if the first pending request is not a steering request.
2397 > */
2398 > dequeueAllSteeringRequests(): IChatPendingRequest[] {
2399 const steeringRequests: IChatPendingRequest[] = [];
2400 while (this._pendingRequests.at(0)?.kind === ChatRequestQueueKind.Steering) {
2406 return steeringRequests;
2407 }
2408 > chatModel.ts
2409 > /**
2410 > * @internal Used by ChatService to clear all pending requests
2411 > */
2412 > clearPendingRequests(): void {
2413 if (this._pendingRequests.length > 0) {
2414 this._pendingRequests.length = 0;
2416 }
2417 }
2418 > chatModel.ts
2419 > readonly lastRequestObs: IObservable<IChatRequestModel | undefined>;
2420 >
2421 > // TODO to be clear, this is not the same as the id from the session object, which belongs to the provider.
2422 > // It's easier to be able to identify this model before its async initialization is complete
2423 > private readonly _sessionId: string;
2424 > /** @deprecated Use {@link sessionResource} instead */
2425 > get sessionId(): string {
2426 return this._sessionId;
2427 }
2428 > chatModel.ts
2429 > private readonly _sessionResource: URI;
2430 > get sessionResource(): URI {
2431 return this._sessionResource;
2432 }
2433 > chatModel.ts
2434 > readonly requestInProgress: IObservable<boolean>;
2435 > readonly hasActiveRequest: IObservable<boolean>;
2436 > readonly requestNeedsInput: IObservable<IChatRequestNeedsInputInfo | undefined>;
2437 > readonly isReadOnly: IObservable<boolean>;
2438 >
2439 > /** Input model for managing input state */
2440 > readonly inputModel: InputModel;
2441 >
2442 > get hasRequests(): boolean {
2443 return this._requests.length > 0;
2444 }
2445 > chatModel.ts
2446 > get lastRequest(): ChatRequestModel | undefined {
2447 return this._requests.at(-1);
2448 }
2449 > chatModel.ts
2450 > get sessionCost(): number {
2451 let totalCredits = 0;
2452 for (const request of this._requests) {
2458 return totalCredits;
2459 }
2460 > chatModel.ts
2461 > private _timestamp: number;
2462 > get timestamp(): number {
2463 return this._timestamp;
2464 }
2465 > chatModel.ts
2466 > get timing(): IChatSessionTiming {
2467 const lastRequest = this._requests.at(-1);
2468 const lastResponse = lastRequest?.response;
2475 };
2476 }
2477 > chatModel.ts
2478 > get lastMessageDate(): number {
2479 return this._requests.at(-1)?.timestamp ?? this._timestamp;
2480 }
2481 > chatModel.ts
2482 > private get _defaultAgent() {
2483 return this.chatAgentService.getDefaultAgent(ChatAgentLocation.Chat, ChatModeKind.Ask);
2484 }
2485 > chatModel.ts
2486 > private readonly _initialResponderUsername: string | undefined;
2487 > get responderUsername(): string {
2488 return this._defaultAgent?.fullName ??
2489 this._initialResponderUsername ?? '';
2490 }
2491 > chatModel.ts
2492 > private _isImported = false;
2493 > get isImported(): boolean {
2494 return this._isImported;
2495 }
2496 > chatModel.ts
2497 > private _isDeleted = false;
2498 > get isDeleted(): boolean {
2499 return this._isDeleted;
2500 }
2501 > markDeleted(): void { chatModel.ts
2502 this._isDeleted = true;
2503 }
2504 > chatModel.ts
2505 > private _customTitle: string | undefined;
2506 > get customTitle(): string | undefined {
2507 return this._customTitle;
2508 }
2509 > chatModel.ts
2510 > get title(): string {
2511 return this._customTitle || ChatModel.getDefaultTitle(this._requests);
2512 }
2513 > chatModel.ts
2514 > get hasCustomTitle(): boolean {
2515 return this._customTitle !== undefined;
2516 }
2517 > chatModel.ts
2518 > private _editingSession: IChatEditingSession | undefined;
2519 >
2520 > get editingSession(): IChatEditingSession | undefined {
2521 return this._editingSession;
2522 }
2523 > chatModel.ts
2524 > private readonly _initialLocation: ChatAgentLocation;
2525 > get initialLocation(): ChatAgentLocation {
2526 return this._initialLocation;
2527 }
2528 > chatModel.ts
2529 > private readonly _canUseTools: boolean = true;
2530 > get canUseTools(): boolean {
2531 return this._canUseTools;
2532 }
2533 > chatModel.ts
2534 > private _disableBackgroundKeepAlive: boolean;
2535 > get willKeepAlive(): boolean {
2536 return !this._disableBackgroundKeepAlive;
2537 }
2538 > chatModel.ts
2539 > public dataSerializer?: IChatDataSerializerLog;
2540 >
2541 > constructor(
2542 dataRef: ISerializedChatDataReference | undefined,
2543 initialModelProps: { initialLocation: ChatAgentLocation; canUseTools: boolean; inputState?: ISerializableChatModelInputState; resource?: URI; disableBackgroundKeepAlive?: boolean; isReadOnly?: IObservable<boolean> },
2665 }
2666 }
2667 > chatModel.ts
2668 > startEditingSession(isGlobalEditingSession?: boolean, transferFromSession?: IChatEditingSession): void {
2669 const session = this._editingSession ??= this._register(
2670 transferFromSession
2693 }));
2694 }
2695 > chatModel.ts
2696 > private currentEditedFileEvents = new ResourceMap<IChatAgentEditedFileEvent>();
2697 > notifyEditingAction(action: IChatEditingSessionAction): void {
2698 const state = action.outcome === 'accepted' ? ChatRequestEditedFileEventKind.Keep :
2699 action.outcome === 'rejected' ? ChatRequestEditedFileEventKind.Undo :
2707 }
2708 }
2709 > chatModel.ts
2710 > private _deserialize(obj: IExportableChatData | ISerializedChatDataReference): ChatRequestModel[] {
2711 const requests = hasKey(obj, { serializer: true }) ? obj.value.requests : obj.requests;
2712 if (!Array.isArray(requests)) {
2722 }
2723 }
2724 > chatModel.ts
2725 > private _deserializeRequest(raw: ISerializableChatRequestData): ChatRequestModel {
2726 const parsedRequest =
2727 typeof raw.message === 'string'
2813 return request;
2814 }
2815 > chatModel.ts
2816 > private reviveVariableData(raw: IChatRequestVariableData): IChatRequestVariableData {
2817 const variableData = raw && Array.isArray(raw.variables)
2818 ? raw :
2823 return variableData;
2824 }
2825 > chatModel.ts
2826 > private getParsedRequestFromString(message: string): IParsedChatRequest {
2827 // TODO These offsets won't be used, but chat replies need to go through the parser as well
2828 const parts = [new ChatRequestTextPart(new OffsetRange(0, message.length), { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, message)];
2832 };
2833 }
2834 > chatModel.ts
2835 > /**
2836 > * Hydrates pending requests from serialized data.
2837 > * For each serialized pending request, finds the matching request model and adds it to the pending queue.
2838 > */
2839 > private _deserializePendingRequests(pendingRequests: ISerializablePendingRequestData[]): IChatPendingRequest[] {
2840 try {
2841 return pendingRequests.map(pending => ({
2855 }
2856 }
2857 > chatModel.ts
2858 >
2859 >
2860 > getRequests(): ChatRequestModel[] {
2861 return this._requests;
2862 }
2863 > chatModel.ts
2864 > resetCheckpoint(): void {
2865 for (const request of this._requests) {
2866 request.setShouldBeBlocked(false);
2870 }
2871 }
2872 > chatModel.ts
2873 > setCheckpoint(requestId: string | undefined) {
2874 let checkpoint: ChatRequestModel | undefined;
2875 let checkpointIndex = -1;
2910 this._checkpoint = checkpoint;
2911 }
2912 > chatModel.ts
2913 > private _checkpoint: ChatRequestModel | undefined = undefined;
2914 > public get checkpoint() {
2915 return this._checkpoint;
2916 }
2917 > chatModel.ts
2918 > private _setDisabledRequests(requestIds: IChatRequestDisablement[]) {
2919 this._requests.forEach((request) => {
2920 const shouldBeRemovedOnSend = requestIds.find(r => r.requestId === request.id);
2927 this._onDidChange.fire({ kind: 'setHidden' });
2928 }
2929 > chatModel.ts
2930 > addRequest(
2931 message: IParsedChatRequest,
2932 variableData: IChatRequestVariableData,
2991 return request;
2992 }
2993 > chatModel.ts
2994 > public setCustomTitle(title: string): void {
2995 this._customTitle = title;
2996 this._onDidChange.fire({ kind: 'setCustomTitle', title });
2997 }
2998 > chatModel.ts
2999 > updateRequest(request: ChatRequestModel, variableData: IChatRequestVariableData) {
3000 request.variableData = variableData;
3001 this._onDidChange.fire({ kind: 'changedRequest', request });
3002 }
3003 > chatModel.ts
3004 > adoptRequest(request: ChatRequestModel): void {
3005 // this doesn't use `removeRequest` because it must not dispose the request object
3006 const oldOwner = request.session;
3020 this._onDidChange.fire({ kind: 'addRequest', request });
3021 }
3022 > chatModel.ts
3023 > acceptResponseProgress(request: ChatRequestModel, progress: IChatProgress, quiet?: boolean): void {
3024 if (!request.response) {
3025 request.response = new ChatResponseModel({
3053 }
3054 }
3055 > chatModel.ts
3056 > removeRequest(id: string, reason: ChatRequestRemovalReason = ChatRequestRemovalReason.Removal): void {
3057 const index = this._requests.findIndex(request => request.id === id);
3058 const request = this._requests[index];
3064 }
3065 }
3066 > chatModel.ts
3067 > cancelRequest(request: ChatRequestModel): void {
3068 if (request.response) {
3069 request.response.cancel();
3070 }
3071 }
3072 > chatModel.ts
3073 > setResponse(request: ChatRequestModel, result: IChatAgentResult): void {
3074 if (!request.response) {
3075 request.response = new ChatResponseModel({
3083 request.response.setResult(result);
3084 }
3085 > chatModel.ts
3086 > setFollowups(request: ChatRequestModel, followups: IChatFollowup[] | undefined): void {
3087 if (!request.response) {
3088 // Maybe something went wrong?
3091 request.response.setFollowups(followups);
3092 }
3093 > chatModel.ts
3094 > setResponseModel(request: ChatRequestModel, response: ChatResponseModel): void {
3095 request.response = response;
3096 this._onDidChange.fire({ kind: 'addResponse', response });
3097 }
3098 > chatModel.ts
3099 > toExport(): IExportableChatData {
3100 return {
3101 responderUsername: this.responderUsername,
3142 };
3143 }
3144 > chatModel.ts
3145 > toJSON(): ISerializableChatData {
3146 return {
3147 version: 3,
3154 };
3155 }
3156 > chatModel.ts
3157 > override dispose() {
3158 this._requests.forEach(r => r.response?.dispose());
3159 this._onDidDispose.fire();
3170 this._editingSession = undefined;
3171 }
3172 > } chatModel.ts
3173 >
3174 > export function updateRanges(variableData: IChatRequestVariableData, diff: number): IChatRequestVariableData {
3175 return {
3176 variables: variableData.variables.map(v => ({
3183 };
3184 }
3185 > chatModel.ts
3186 > export function canMergeMarkdownStrings(md1: IMarkdownString, md2: IMarkdownString): boolean {
3187 if (md1.baseUri && md2.baseUri) {
3188 const baseUriEquals = md1.baseUri.scheme === md2.baseUri.scheme
3202 md1.supportThemeIcons === md2.supportThemeIcons;
3203 }
3204 > chatModel.ts
3205 > export function appendMarkdownString(md1: IMarkdownString, md2: IMarkdownString | string): IMarkdownString {
3206 const appendedValue = typeof md2 === 'string' ? md2 : md2.value;
3207 return {
3213 };
3214 }
3215 > chatModel.ts
3216 > export function getCodeCitationsMessage(citations: ReadonlyArray<IChatCodeCitation>): string {
3217 if (citations.length === 0) {
3218 return '';
3225 return label;
3226 }
3227 > chatModel.ts
3228 > /**
3229 > * Converts IChatSendRequestOptions to a serializable format by extracting only
3230 > * serializable fields and converting observables to static values.
3231 > */
3232 > export function serializeSendOptions(options: IChatSendRequestOptions): ISerializableSendOptions {
3233 return {
3234 modeInfo: options.modeInfo,
3249 };
3250 }
3251 > chatModel.ts
3252 > export enum ChatRequestEditedFileEventKind {
3253 > Keep = 1,
3254 > Undo = 2,
3255 > UserModification = 3,
3256 > }
3257 >
3258 > export interface IChatAgentEditedFileEvent {
3259 > readonly uri: URI;
3260 > readonly eventKind: ChatRequestEditedFileEventKind;
3261 > }
3262 >
3263 > /** URI for a resource embedded in a chat request/response */
3264 > export namespace ChatResponseResource {
3265 > export const scheme = 'vscode-chat-response-resource';
3266 >
3267 > export function createUri(sessionResource: URI, toolCallId: string, index: number, basename?: string): URI {
3268 return URI.from({
3269 scheme: ChatResponseResource.scheme,
3272 });
3273 }
3274 > chatModel.ts
3275 > export function parseUri(uri: URI): undefined | { sessionResource: URI; toolCallId: string; index: number } {
3276 if (uri.scheme !== ChatResponseResource.scheme) {
3277 return undefined;
3305 };
3306 }
3307 > } chatModel.ts
3308 >
3309 function _logChangesToStateModel(newState: Partial<IChatModelInputState> | undefined, oldState: Partial<IChatModelInputState> | undefined, logger: ILogService, sessionId: string) {
3310 if (!canLog(logger.getLevel(), LogLevel.Debug) || newState?.selectedModel?.identifier === oldState?.selectedModel?.identifier) {
3315 logger.debug(message);
3316 }
3317 > chatModel.ts
3318 > export function logChangesToStateModel(model: IInputModel | undefined, message: string, newState: Partial<IChatModelInputState> | undefined, oldState: Partial<IChatModelInputState> | undefined, logger: ILogService) {
3319 if (!canLog(logger.getLevel(), LogLevel.Debug)) {
3320 return;
src/vs/workbench/contrib/chat/common/model/chatProgressTypes/chatToolInvocation.ts 127 introduced LOC · 20 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatToolInvocation.ts
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 { encodeBase64 } from '../../../../../../base/common/buffer.js';
7 > import { IMarkdownString } from '../../../../../../base/common/htmlContent.js';
8 > import { IObservable, ISettableObservable, observableValue } from '../../../../../../base/common/observable.js';
9 > import { ThemeIcon } from '../../../../../../base/common/themables.js';
10 > import { localize } from '../../../../../../nls.js';
11 > import { ConfirmedReason, IChatAgentFeedbackReviewConfirmationData, IChatAutomationConfigurationData, IChatAutomationConfiguredData, IChatExtensionsContent, IChatModifiedFilesConfirmationData, IChatSearchToolInvocationData, IChatSessionCreatedData, IChatSimpleToolInvocationData, IChatSubagentToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, IChatToolInvocationOtherClientData, IChatToolInvocationSerialized, ToolConfirmKind, type IChatMcpAuthenticationRequiredServer, type IChatTerminalToolInvocationData } from '../../chatService/chatService.js';
12 > import { IPreparedToolInvocation, isToolResultOutputDetails, IToolConfirmationMessages, IToolData, IToolProgressStep, IToolResult, ToolDataSource } from '../../tools/languageModelToolsService.js';
13 >
14 > export interface IStreamingToolCallOptions {
15 > toolCallId: string;
16 > toolId: string;
17 > toolData: IToolData;
18 > subagentInvocationId?: string;
19 > chatRequestId?: string;
20 > }
21 >
22 > export class ChatToolInvocation implements IChatToolInvocation {
23 > public readonly kind: 'toolInvocation' = 'toolInvocation';
24 >
25 > public invocationMessage: string | IMarkdownString;
26 > public readonly originMessage: string | IMarkdownString | undefined;
27 > public pastTenseMessage: string | IMarkdownString | undefined;
28 > public confirmationMessages: IToolConfirmationMessages | undefined;
29 > public presentation: IPreparedToolInvocation['presentation'];
30 > public readonly toolId: string;
31 > public readonly icon?: ThemeIcon;
32 > public source: ToolDataSource;
33 > public readonly subAgentInvocationId: string | undefined;
34 > public parameters: unknown;
35 > public generatedTitle?: string;
36 > public readonly chatRequestId?: string;
37 > public isAttachedToThinking: boolean = false;
38 > public otherClientToolCall?: IChatToolInvocationOtherClientData;
39 >
40 > private _toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData | IChatSearchToolInvocationData | IChatModifiedFilesConfirmationData | IChatAgentFeedbackReviewConfirmationData | IChatSessionCreatedData | IChatAutomationConfigurationData | IChatAutomationConfiguredData;
41 > private readonly _toolSpecificDataKind = observableValue<string | undefined>(this, undefined);
42 > public readonly toolSpecificDataKind: IObservable<string | undefined> = this._toolSpecificDataKind;
43 >
44 > public get toolSpecificData() {
45 > return this._toolSpecificData;
46 > }
47 >
48 > public set toolSpecificData(value: typeof this._toolSpecificData) {
49 this._toolSpecificData = value;
50 this._toolSpecificDataKind.set(value?.kind, undefined);
51 }
53 > private readonly _progress = observableValue<{ message?: string | IMarkdownString; progress: number | undefined }>(this, { progress: 0 });
54 > private readonly _state: ISettableObservable<IChatToolInvocation.State>;
55 >
56 > // Streaming-related observables
57 > private readonly _partialInput = observableValue<unknown>(this, undefined);
58 > private readonly _streamingMessage = observableValue<string | IMarkdownString | undefined>(this, undefined);
59 >
60 > public get state(): IObservable<IChatToolInvocation.State> {
61 return this._state;
62 }
64 > /**
65 > * Create a tool invocation in streaming state.
66 > * Use this when the tool call is beginning to stream partial input from the LM.
67 > */
68 > public static createStreaming(options: IStreamingToolCallOptions): ChatToolInvocation {
69 return new ChatToolInvocation(undefined, options.toolData, options.toolCallId, options.subagentInvocationId, undefined, { startInStreaming: true }, options.chatRequestId);
70 }
72 > /**
73 > * Create a tool invocation already in cancelled state.
74 > * Use this when a hook denies tool execution before it even starts.
75 > */
76 > public static createCancelled(options: IStreamingToolCallOptions, parameters: unknown, reason: ToolConfirmKind.Denied | ToolConfirmKind.Skipped, reasonMessage?: string | IMarkdownString): ChatToolInvocation {
77 return new ChatToolInvocation(undefined, options.toolData, options.toolCallId, options.subagentInvocationId, parameters, { startInCancelled: true, cancelReason: reason, cancelReasonMessage: reasonMessage }, options.chatRequestId);
78 }
80 > constructor(
81 preparedInvocation: IPreparedToolInvocation | undefined,
82 toolData: IToolData,
140 }
141 }
143 > /**
144 > * Shared confirmation handler used by every `WaitingForConfirmation` state
145 > * this invocation can enter (initial construction, transition out of
146 > * streaming, and re-arming via {@link requestConfirmation}). Denials/skips
147 > * cancel; anything else moves to executing.
148 > */
149 > private _confirm(reason: ConfirmedReason): void {
150 if (reason.type === ToolConfirmKind.Denied || reason.type === ToolConfirmKind.Skipped) {
151 this._state.set({
165 }
166 }
168 > /**
169 > * Update the partial input observable during streaming.
170 > */
171 > public updatePartialInput(input: unknown): void {
172 if (this._state.get().type !== IChatToolInvocation.StateKind.Streaming) {
173 return; // Only update in streaming state
175 this._partialInput.set(input, undefined);
176 }
178 > /**
179 > * Update the streaming message (from handleToolStream).
180 > */
181 > public updateStreamingMessage(message: string | IMarkdownString): void {
182 const state = this._state.get();
183 if (state.type !== IChatToolInvocation.StateKind.Streaming) {
186 this._streamingMessage.set(message, undefined);
187 }
189 > /**
190 > * Notifies state observers that `toolSpecificData` has been mutated.
191 > * Since `toolSpecificData` isn't observable, this re-sets the internal
192 > * state to trigger autoruns that need to re-read tool metadata.
193 > */
194 > public notifyToolSpecificDataChanged(): void {
195 const current = this._state.get();
196 this._state.set({ ...current }, undefined);
197 }
199 > public updateConfirmationMessages(confirmationMessages: IToolConfirmationMessages): void {
200 const current = this._state.get();
201 if (current.type !== IChatToolInvocation.StateKind.WaitingForConfirmation) {
205 this._state.set({ ...current, confirmationMessages }, undefined);
206 }
208 > /**
209 > * Cancel a streaming invocation directly (e.g., when preToolUse hook denies).
210 > * Only works when in Streaming state.
211 > * @returns true if the cancellation was applied, false if not in streaming state
212 > */
213 > public cancelFromStreaming(reason: ToolConfirmKind.Denied | ToolConfirmKind.Skipped, reasonMessage?: string | IMarkdownString): boolean {
214 const currentState = this._state.get();
215 if (currentState.type !== IChatToolInvocation.StateKind.Streaming) {
226 return true;
227 }
229 > /**
230 > * Transition from streaming state to prepared/executing state.
231 > * Called when the full tool call is ready.
232 > */
233 > public transitionFromStreaming(preparedInvocation: IPreparedToolInvocation | undefined, parameters: unknown, autoConfirmed: ConfirmedReason | undefined): void {
234 const currentState = this._state.get();
235 if (currentState.type !== IChatToolInvocation.StateKind.Streaming) {
275 }
276 }
278 > /** Moves an active invocation into confirmation while preserving the same tool card. */
279 > public requestConfirmation(preparedInvocation: IPreparedToolInvocation): void {
280 const currentType = this._state.get().type;
281 if (currentType === IChatToolInvocation.StateKind.Streaming) {
308 }, undefined);
309 }
311 > private _setCompleted(result: IToolResult | undefined, postConfirmed?: ConfirmedReason | undefined) {
312 if (postConfirmed && (postConfirmed.type === ToolConfirmKind.Denied || postConfirmed.type === ToolConfirmKind.Skipped)) {
313 this._state.set({
330 }, undefined);
331 }
333 > public async didExecuteTool(result: IToolResult | undefined, final?: boolean, checkIfResultAutoApproved?: () => Promise<ConfirmedReason | undefined>): Promise<IChatToolInvocation.State> {
334 if (result?.toolSpecificData) {
335 this.toolSpecificData = result.toolSpecificData;
362 return this._state.get();
363 }
365 > public setAuthenticationRequired(server: IChatMcpAuthenticationRequiredServer, cancel: () => void = () => { }): void {
366 const state = this._state.get();
367 if (state.type !== IChatToolInvocation.StateKind.Executing && state.type !== IChatToolInvocation.StateKind.WaitingForAuthentication) {
377 }, undefined);
378 }
380 > public setAuthenticationResolved(): void {
381 const state = this._state.get();
382 if (state.type !== IChatToolInvocation.StateKind.WaitingForAuthentication) {
391 }, undefined);
392 }
394 > public acceptProgress(step: IToolProgressStep) {
395 const prev = this._progress.get();
396 this._progress.set({
399 }, undefined);
400 }
402 > public toJSON(): IChatToolInvocationSerialized {
403 // persist the serialized call as 'skipped' if we were waiting for postapproval
404 const waitingForPostApproval = this.state.get().type === IChatToolInvocation.StateKind.WaitingForPostApproval;
src/vs/workbench/contrib/chat/common/model/chatProgressTypes/chatPlanReviewData.ts 29 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatPlanReviewData.ts
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 { DeferredPromise } from '../../../../../../base/common/async.js';
7 > import { UriComponents } from '../../../../../../base/common/uri.js';
8 > import { IChatPlanApprovalAction, IChatPlanReview, IChatPlanReviewResult } from '../../chatService/chatService.js';
9 > import { ToolDataSource } from '../../tools/languageModelToolsService.js';
10 >
11 > /**
12 > * Runtime representation of a plan review widget with a {@link DeferredPromise}
13 > * that is resolved when the user approves or rejects the plan. {@link toJSON}
14 > * strips the completion so only serialisable data is persisted.
15 > */
16 > export class ChatPlanReviewData implements IChatPlanReview {
17 > public readonly kind = 'planReview' as const;
18 > public readonly completion = new DeferredPromise<IChatPlanReviewResult | undefined>();
19 >
20 > public draftFeedback: string | undefined;
21 > public draftCollapsed: boolean | undefined;
22 >
23 > constructor(
24 public title: string,
25 public content: string,
32 public source?: ToolDataSource,
33 ) { }
35 > /** Dismiss without a user choice (e.g. the response was cancelled). */
36 > dismiss(): void {
37 if (this.isUsed) {
38 return;
43 void this.completion.complete(undefined);
44 }
46 > toJSON(): IChatPlanReview {
47 return {
48 kind: this.kind,
src/vs/workbench/contrib/chat/common/chat.ts 19 introduced LOC · 3 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chat.ts
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 { ResourceSet } from '../../../../base/common/map.js';
7 > import { chatEditingSessionIsReady } from './editing/chatEditingService.js';
8 > import { IChatModel } from './model/chatModel.js';
9 > import { isLegacyChatTerminalToolInvocationData, type IChatSessionStats, type IChatTerminalToolInvocationData, type ILegacyChatTerminalToolInvocationData } from './chatService/chatService.js';
10 > import { ChatModeKind } from './constants.js';
11 >
12 > export function checkModeOption(mode: ChatModeKind, option: boolean | ((mode: ChatModeKind) => boolean) | undefined): boolean | undefined {
13 if (option === undefined) {
14 return undefined;
19 return option;
20 }
21 > chat.ts
22 > /**
23 > * @deprecated This is the old API shape, we should support this for a while before removing it so
24 > * we don't break existing chats
25 > */
26 > export function migrateLegacyTerminalToolSpecificData(data: IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData): IChatTerminalToolInvocationData {
27 if (isLegacyChatTerminalToolInvocationData(data)) {
28 data = {
38 return data;
39 }
40 > chat.ts
41 export async function awaitStatsForSession(model: IChatModel): Promise<IChatSessionStats | undefined> {
42 if (!model.editingSession) {