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

945 LOC · 923 covered · 22 uncovered · 23 ranges · 2050 concepts · 7 introducers · 1133 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

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 related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- chatSessionsService.ts ×12
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { Event } from '../../../../base/common/event.js';
8 > import { IMarkdownString } from '../../../../base/common/htmlContent.js';
9 > import { IDisposable } from '../../../../base/common/lifecycle.js';
10 > import { IObservable } from '../../../../base/common/observable.js';
11 > import { ThemeIcon } from '../../../../base/common/themables.js';
12 > import { URI } from '../../../../base/common/uri.js';
13 > import { IPosition } from '../../../../editor/common/core/position.js';
14 > import { isRemoteAgentHostSessionType } from '../../../../platform/agentHost/common/agentHostSessionType.js';
15 > import { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
16 > import { Registry } from '../../../../platform/registry/common/platform.js';
17 > import { LOCAL_AGENT_HOST_SCHEME_PREFIX } from '../../../../platform/agentHost/common/agentHostConnectionsService.js';
18 > import { IChatAgentAttachmentCapabilities, IChatAgentRequest } from './participants/chatAgents.js';
19 > import { IChatEditingSession } from './editing/chatEditingService.js';
20 > import { IChatRequestModeInstructions, IChatRequestVariableData, ISerializableChatModelInputState } from './model/chatModel.js';
21 > import { IChatProgress, IChatResponseErrorDetails, IChatSessionTiming } from './chatService/chatService.js';
22 > import { Target } from './promptSyntax/promptTypes.js';
23 >
24 > export const enum ChatSessionsExtensions {
25 > AsyncActivation = 'workbench.contrib.chatSessions.asyncActivation'
26 > }
27 >
28 > export interface IAsyncChatSessionActivationContribution {
29 > matchSessionType(sessionType: string): boolean;
30 > waitForActivation(accessor: ServicesAccessor, sessionType: string): Promise<boolean>;
31 > }
32 >
33 > export interface IAsyncChatSessionActivationRegistry {
34 > register(contribution: IAsyncChatSessionActivationContribution): IDisposable;
35 > getActivators(sessionType: string): readonly IAsyncChatSessionActivationContribution[];
36 > }
37 >
38 > class AsyncChatSessionActivationRegistry implements IAsyncChatSessionActivationRegistry {
39 > private readonly _contributions = new Set<IAsyncChatSessionActivationContribution>();
40 >
41 > register(contribution: IAsyncChatSessionActivationContribution): IDisposable {
42 this._contributions.add(contribution);
43 return {
44 dispose: () => this._contributions.delete(contribution)
45 };
46 }
48 > getActivators(sessionType: string): readonly IAsyncChatSessionActivationContribution[] {
49 return Array.from(this._contributions).filter(contribution => contribution.matchSessionType(sessionType));
50 }
52 >
53 > Registry.add(ChatSessionsExtensions.AsyncActivation, new AsyncChatSessionActivationRegistry());
54 >
55 > export const enum ChatSessionStatus {
56 > Failed = 0,
57 > Completed = 1,
58 > InProgress = 2,
59 > NeedsInput = 3
60 > }
61 >
62 > export interface IChatSessionCommandContribution {
63 > readonly name: string;
64 > readonly description: string;
65 > readonly when?: string;
66 > }
67 >
68 > export interface IChatSessionProviderOptionModelMetadata {
69 > readonly name: string;
70 > readonly id: string;
71 > readonly vendor?: string;
72 > readonly version?: string;
73 > readonly family?: string;
74 > readonly tooltip?: string;
75 > readonly pricing?: string;
76 > readonly multiplierNumeric?: number;
77 > readonly inputCost?: number;
78 > readonly outputCost?: number;
79 > readonly cacheCost?: number;
80 > readonly cacheWriteCost?: number;
81 > readonly longContextInputCost?: number;
82 > readonly longContextOutputCost?: number;
83 > readonly longContextCacheCost?: number;
84 > readonly longContextCacheWriteCost?: number;
85 > readonly priceCategory?: string;
86 > readonly promo?: {
87 > readonly id: string;
88 > readonly discountPercent: number;
89 > readonly endsAt: string;
90 > readonly message: string;
91 > };
92 > readonly maxInputTokens?: number;
93 > readonly maxOutputTokens?: number;
94 > readonly capabilities?: {
95 > readonly vision?: boolean;
96 > readonly toolCalling?: boolean;
97 > };
98 > }
99 >
100 > export interface IChatSessionProviderOptionItem {
101 > readonly id: string;
102 > readonly name: string;
103 > readonly description?: string;
104 > readonly detail?: string;
105 > readonly locked?: boolean;
106 > readonly icon?: ThemeIcon;
107 > readonly default?: boolean;
108 > readonly slashCommand?: string;
109 > readonly tooltip?: string;
110 > readonly modelMetadata?: IChatSessionProviderOptionModelMetadata;
111 > // [key: string]: any;
112 > }
113 >
114 > export interface IChatSessionProviderOptionGroupCommand {
115 > readonly command: string;
116 > readonly title: string;
117 > readonly tooltip?: string;
118 > readonly arguments?: readonly unknown[];
119 > }
120 >
121 > export interface IChatSessionProviderOptionGroup {
122 > readonly id: string;
123 > readonly name: string;
124 > readonly description?: string;
125 > readonly detail?: string;
126 > readonly selected?: IChatSessionProviderOptionItem;
127 > readonly items: readonly IChatSessionProviderOptionItem[];
128 > /**
129 > * A context key expression that controls visibility of this option group picker.
130 > * When specified, the picker is only visible when the expression evaluates to true.
131 > * The expression can reference other option group values via `chatSessionOption.<groupId>`.
132 > * Example: `"chatSessionOption.models == 'gpt-4'"`
133 > */
134 > readonly when?: string;
135 > readonly icon?: ThemeIcon;
136 > /**
137 > * Custom commands to show in the option group's picker UI.
138 > * These will be shown in a separate section at the end of the picker.
139 > */
140 > readonly commands?: readonly IChatSessionProviderOptionGroupCommand[];
141 > /**
142 > * Optional kind hint that controls how the group is presented.
143 > * - `'permissions'`: the group's items are surfaced inside the chat permission picker
144 > * instead of being rendered as a standalone picker. At most one group per provider
145 > * may use this kind; if multiple are declared, the first one (in declaration order)
146 > * wins. The group has no UI of its own — it is invisible when the permission
147 > * picker is hidden by its own `when` clauses.
148 > */
149 > readonly kind?: 'permissions';
150 > }
151 >
152 > export interface IChatSessionsExtensionPoint {
153 > readonly type: string;
154 > readonly name: string;
155 > readonly displayName: string;
156 > readonly description: string;
157 > readonly when?: string;
158 > readonly icon?: string | { light: string; dark: string };
159 > readonly order?: number;
160 > readonly alternativeIds?: string[];
161 > readonly welcomeTitle?: string;
162 > readonly welcomeMessage?: string;
163 > readonly welcomeTips?: string;
164 > readonly inputPlaceholder?: string;
165 > readonly capabilities?: IChatAgentAttachmentCapabilities;
166 > readonly commands?: IChatSessionCommandContribution[];
167 > readonly canDelegate?: boolean;
168 > readonly isReadOnly?: boolean;
169 > /**
170 > * When set, the chat session will show a filtered mode picker with custom agents
171 > * that have a matching `target` property. This enables contributed chat sessions
172 > * to reuse the standard agent/mode dropdown with filtered custom agents.
173 > * Custom agents without a `target` property are also shown in all filtered lists
174 > */
175 > readonly customAgentTarget?: Target;
176 > readonly requiresCustomModels?: boolean;
177 > /**
178 > * Whether this session type supports the synthetic "Auto" model fallback.
179 > * Defaults to true. When false and no models are available, the picker
180 > * shows a "No models available" state instead of "Auto".
181 > *
182 > * This is distinct from {@link requiresCustomModels}, which only controls
183 > * whether the picker is filtered to the session's own model pool — a
184 > * session can own a custom pool yet still support Auto (e.g. the Copilot
185 > * CLI agent host).
186 > */
187 > readonly supportsAutoModel?: boolean;
188 > /**
189 > * Logical Agent Host provider ID for Agent Host-backed chat sessions.
190 > * For example, both local `agent-host-copilotcli` and remote
191 > * `remote-{authority}-copilotcli` sessions use `copilotcli`.
192 > */
193 > readonly agentHostProviderId?: string;
194 > /**
195 > * Whether this type needs a GitHub Copilot account and so is unusable until the user signs in. Set by
196 > * Copilot-backed types (Copilot CLI / agent host, cloud agent) where BYOK isn't supported. Defaults to false, so
197 > * third-party types that don't depend on Copilot stay usable while signed out.
198 > */
199 > readonly requiresCopilotSignIn?: boolean;
200 > /**
201 > * When false, the delegation picker is hidden for this session type.
202 > * Defaults to true.
203 > */
204 > readonly supportsDelegation?: boolean;
205 > /**
206 > * Decides whether to automatically attach instruction files to chat requests
207 > * for this session type. Defaults to false when not specified.
208 > */
209 > readonly autoAttachReferences?: boolean;
210 > }
211 >
212 > export interface IChatSessionItem {
213 > readonly resource: URI;
214 > readonly label: string;
215 > readonly iconPath?: ThemeIcon;
216 > readonly badge?: string | IMarkdownString;
217 > readonly description?: string | IMarkdownString;
218 > readonly status?: ChatSessionStatus;
219 > readonly tooltip?: string | IMarkdownString;
220 > readonly timing: IChatSessionTiming;
221 > readonly changes?: {
222 > readonly files: number;
223 > readonly insertions: number;
224 > readonly deletions: number;
225 > } | readonly IChatSessionFileChange[] | readonly IChatSessionFileChange2[];
226 > readonly archived?: boolean;
227 > readonly metadata?: IChatSessionItemMetadata;
228 > /**
229 > * Resource identifier the item was previously known by. When set, host-stored
230 > * per-resource state (archive, pin, read) recorded under that URI is adopted
231 > * forward onto {@link resource} on first state read, and the legacy entry is
232 > * removed. Scheme must match {@link resource}'s scheme; otherwise ignored.
233 > */
234 > readonly legacyResource?: URI;
235 > }
236 >
237 > export interface IChatSessionItemMetadata {
238 > //#region Changes metadata (for sessions window)
239 > readonly repositoryPath?: string;
240 > readonly workingDirectoryPath?: string;
241 > readonly firstCheckpointRef?: string;
242 > readonly lastCheckpointRef?: string;
243 > readonly worktreePath?: string;
244 > readonly uncommittedChanges?: number;
245 > readonly baseRefOid?: string;
246 > readonly headRefOid?: string;
247 > readonly branchName?: string;
248 > readonly branch?: string;
249 > readonly baseBranchName?: string;
250 > readonly baseBranch?: string;
251 > readonly baseBranchProtected?: boolean;
252 > readonly hasGitHubRemote?: boolean;
253 > readonly upstreamBranchName?: string;
254 > readonly incomingChanges?: number;
255 > readonly outgoingChanges?: number;
256 > //#endregion
257 >
258 > readonly [key: string]: unknown;
259 > }
260 >
261 > export interface IChatSessionFileChange {
262 > readonly modifiedUri: URI;
263 > readonly originalUri?: URI;
264 > readonly insertions: number;
265 > readonly deletions: number;
266 > readonly reviewed?: boolean;
267 > }
268 >
269 > export interface IChatSessionFileChange2 {
270 > readonly uri: URI;
271 > readonly originalUri?: URI;
272 > readonly modifiedUri?: URI;
273 > readonly insertions: number;
274 > readonly deletions: number;
275 > readonly reviewed?: boolean;
276 > }
277 >
278 > export type IChatSessionHistoryItem = {
279 > id?: string;
280 > type: 'request';
281 > prompt: string;
282 > participant: string;
283 > command?: string;
284 > variableData?: IChatRequestVariableData;
285 > modelId?: string;
286 > timestamp?: number;
287 > modeInstructions?: IChatRequestModeInstructions;
288 > isSystemInitiated?: boolean;
289 > systemInitiatedLabel?: string;
290 > isTerminalRequest?: boolean;
291 > } | {
292 > type: 'response';
293 > parts: IChatProgress[];
294 > participant: string;
295 > details?: string;
296 > elapsedMs?: number;
297 > completedAt?: number;
298 > /**
299 > * Error details for a failed response. Rendered as a proper chat error
300 > * (including the quota-exceeded upgrade affordance), mirroring the live
301 > * agent result's `errorDetails`.
302 > */
303 > errorDetails?: IChatResponseErrorDetails;
304 > };
305 >
306 > export type IChatSessionRequestHistoryItem = Extract<IChatSessionHistoryItem, { type: 'request' }>;
307 >
308 > export interface IChatSessionServerRequest {
309 > readonly prompt: string;
310 > readonly variableData?: IChatRequestVariableData;
311 > readonly timestamp?: number;
312 > readonly isSystemInitiated?: boolean;
313 > readonly systemInitiatedLabel?: string;
314 > readonly isTerminalRequest?: boolean;
315 > }
316 >
317 > /**
318 > * Whether `text` runs as a terminal command for the given command `prefix`
319 > * (e.g. `!`) — it starts with the prefix and has a non-empty command after it.
320 > * Mirrors the agent host's bang parser, where a lone `!` (or `!` followed only
321 > * by whitespace) is forwarded to the agent rather than executed.
322 > */
323 > export function isTerminalCommandPrompt(text: string, prefix: string | undefined): boolean {
324 > return !!prefix && text.startsWith(prefix) && text.slice(prefix.length).trim().length > 0; chatServiceImpl.ts ×13
325 > }
327 > /**
328 > * A set of well-known session types
329 > */
330 > export namespace SessionType {
331 > export const CopilotCLI = 'copilotcli';
332 > export const CopilotCloud = 'copilot-cloud-agent';
333 > export const Local = 'local';
334 > export const ClaudeCode = 'claude-code';
335 > export const Codex = 'openai-codex';
336 > export const Growth = 'copilot-growth';
337 > export const AgentHostCopilot = 'agent-host-copilotcli';
338 > export const AgentHostClaude = 'agent-host-claude';
339 > export const AgentHostCodex = 'agent-host-codex';
340 > }
341 >
342 > /**
343 > * Returns whether the given session type is a local agent host target.
344 > */
345 > export function isLocalAgentHostTarget(target: string): boolean {
346 > return target === SessionType.AgentHostCopilot || chatSessionsService.ts ×3
347 > target.startsWith(LOCAL_AGENT_HOST_SCHEME_PREFIX); chatSessionsService.ts ×1
350 > /**
351 > * Returns whether the given session type is a remote agent host target.
352 > *
353 > * Note: The `remote-` prefix convention is established by
354 > * `RemoteAgentHostContribution` which generates session types as
355 > * `remote-{sanitizedAddress}-{provider}`. If future remote providers that
356 > * are NOT agent hosts need a different prefix, this function must be updated.
357 > */
358 > export function isRemoteAgentHostTarget(target: string): boolean {
359 > return isRemoteAgentHostSessionType(target); chatSessionsService.ts ×1
360 > }
362 > /**
363 > * Returns whether the given session type is an agent host target.
364 > * Matches the local agent host (`agent-host-*`) and remote agent hosts (`remote-*`).
365 > */
366 > export function isAgentHostTarget(target: string): boolean {
367 > return isLocalAgentHostTarget(target) || isRemoteAgentHostTarget(target); chatSessionsService.ts ×3
368 > }
370 > /**
371 > * The session type used for local agent chat sessions.
372 > */
373 > export const localChatSessionType = SessionType.Local;
374 >
375 > export interface IChatSession extends IDisposable {
376 > readonly onWillDispose: Event<void>;
377 >
378 > readonly sessionResource: URI;
379 >
380 > readonly title?: string;
381 >
382 > readonly history: readonly IChatSessionHistoryItem[];
383 >
384 >
385 > readonly options?: ReadonlyChatSessionOptionsMap;
386 >
387 > readonly progressObs?: IObservable<IChatProgress[]>;
388 > readonly isCompleteObs?: IObservable<boolean>;
389 > readonly isReadOnly?: IObservable<boolean>;
390 > readonly interruptActiveResponseCallback?: () => Promise<boolean>;
391 >
392 > /**
393 > * Event fired when the server initiates a new request (e.g. from a consumed
394 > * queued message). The consumer should create a new request+response pair in
395 > * the model and prepare to receive progress via {@link progressObs}.
396 > */
397 > readonly onDidStartServerRequest?: Event<IChatSessionServerRequest>;
398 >
399 > /**
400 > * Editing session transferred from a previously-untitled chat session in `onDidCommitChatSessionItem`.
401 > */
402 > transferredState?: {
403 > readonly editingSession: IChatEditingSession | undefined;
404 > readonly inputState: ISerializableChatModelInputState | undefined;
405 > };
406 >
407 > requestHandler?: (
408 > request: IChatAgentRequest,
409 > progress: (progress: IChatProgress[]) => void,
410 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
411 > history: any[], // TODO: Nail down types
412 > token: CancellationToken
413 > ) => Promise<void>;
414 >
415 > /**
416 > * Forks the session from the given request point.
417 > * @param request The request history item to fork from, or undefined to fork from the end.
418 > * @param token Cancellation token.
419 > * @returns The forked session item. The promise is rejected if forking fails.
420 > */
421 > forkSession?: (request: IChatSessionRequestHistoryItem | undefined, token: CancellationToken) => Promise<IChatSessionItem>;
422 >
423 > /**
424 > * Renames the session.
425 > * @param title The new title for the session.
426 > * @param token Cancellation token.
427 > * @returns A promise that resolves once the rename has been dispatched. The promise is rejected if renaming fails.
428 > */
429 > renameSession?: (title: string, token: CancellationToken) => Promise<void>;
430 > }
431 >
432 > export interface IChatSessionContentProvider {
433 > provideChatSessionContent(sessionResource: URI, token: CancellationToken): Promise<IChatSession>;
434 >
435 > /** Resolves a parsed response Markdown URI before it is sanitized and rendered. */
436 > resolveChatResponseUri?(sessionResource: URI, href: string, kind: 'link' | 'image'): string;
437 >
438 > /**
439 > * Optional. Compute completion items for an input being composed in this
440 > * session. Returning `undefined` lets the workbench fall back to its
441 > * default in-process completion providers.
442 > */
443 > provideChatInputCompletions?(sessionResource: URI, params: IChatInputCompletionsParams, token: CancellationToken): Promise<IChatInputCompletionsResult | undefined>;
444 >
445 > /**
446 > * Optional. Trigger characters that, when typed in the chat input,
447 > * SHOULD cause the workbench to issue a `provideChatInputCompletions`
448 > * request. Used to register a Monaco completion provider scoped to
449 > * sessions handled by this content provider.
450 > */
451 > provideChatInputCompletionTriggerCharacters?(): Promise<readonly string[]>;
452 > }
453 >
454 > /**
455 > * Inputs for {@link IChatSessionContentProvider.provideChatInputCompletions}
456 > * and {@link IChatSessionsService.provideChatInputCompletions}.
457 > */
458 > export interface IChatInputCompletionsParams {
459 > /**
460 > * The complete text of the input being completed (e.g. the user message
461 > * the user is currently composing).
462 > */
463 > readonly text: string;
464 > /**
465 > * The character offset within {@link text} at which the completion is
466 > * requested, measured in UTF-16 code units. MUST satisfy
467 > * `0 <= offset <= text.length`.
468 > */
469 > readonly offset: number;
470 > }
471 >
472 > /**
473 > * A neutral completion-item shape returned by
474 > * {@link IChatSessionContentProvider.provideChatInputCompletions}. The
475 > * workbench-side completion glue maps these into Monaco completion items
476 > * and the corresponding chat-input attachment.
477 > */
478 > export interface IChatInputCompletionItem {
479 > /** Text inserted into the input when this item is accepted. */
480 > readonly insertText: string;
481 > /**
482 > * Optional display label shown in the completion picker. When omitted, the
483 > * workbench displays {@link insertText}. Set this when the inserted text
484 > * differs from the label — e.g. an action item that inserts nothing
485 > * (`insertText: ''`) but should still be shown to the user.
486 > */
487 > readonly label?: string;
488 > /**
489 > * Half-open range `[start, end)` in the *current* input text that
490 > * {@link insertText} replaces. Positions use 1-based `lineNumber` and
491 > * `column` to match Monaco. When omitted, the workbench replaces the
492 > * word at the cursor.
493 > */
494 > readonly start?: IPosition;
495 > readonly end?: IPosition;
496 > /** Attachment associated with the item. */
497 > readonly attachment: IChatInputCompletionResourceAttachment | IChatInputCompletionCommandAttachment | IChatInputCompletionSkillAttachment;
498 > }
499 >
500 > /**
501 > * Resource attachment associated with a completion item. The workbench
502 > * adds it to the input's variable model when the item is accepted.
503 > */
504 > export interface IChatInputCompletionResourceAttachment {
505 > readonly kind: 'resource';
506 > readonly uri: URI;
507 > readonly displayName?: string;
508 > readonly isDirectory?: boolean;
509 > /**
510 > * Implementation-defined metadata that MUST be preserved by the
511 > * workbench when the accepted completion is sent back as part of a
512 > * user message attachment.
513 > */
514 > readonly _meta?: Record<string, unknown>;
515 > }
516 >
517 > /**
518 > * Command attachment associated with a completion item.
519 > */
520 > export interface IChatInputCompletionCommandAttachment {
521 > readonly kind: 'command';
522 > readonly command: string;
523 > readonly description: string;
524 > /**
525 > * Implementation-defined metadata that MUST be preserved by the
526 > * workbench when the accepted completion is sent back as part of a
527 > * user message attachment.
528 > */
529 > readonly _meta?: Record<string, unknown>;
530 > }
531 >
532 > /**
533 > * Skill attachment associated with a completion item. The workbench
534 > * adds it to the input's variable model when the item is accepted.
535 > */
536 > export interface IChatInputCompletionSkillAttachment {
537 > readonly kind: 'skill';
538 > readonly uri: URI;
539 > readonly displayName?: string;
540 > readonly description?: string;
541 > /**
542 > * Implementation-defined metadata that MUST be preserved by the
543 > * workbench when the accepted completion is sent back as part of a
544 > * user message attachment.
545 > */
546 > readonly _meta?: Record<string, unknown>;
547 > }
548 >
549 > /**
550 > * Result of {@link IChatSessionContentProvider.provideChatInputCompletions}.
551 > */
552 > export interface IChatInputCompletionsResult {
553 > readonly items: readonly IChatInputCompletionItem[];
554 > }
555 >
556 > export interface IChatNewSessionRequest {
557 > readonly prompt: string;
558 > readonly command?: string;
559 >
560 > readonly initialSessionOptions?: ReadonlyChatSessionOptionsMap;
561 >
562 > /**
563 > * The chat-input session resource the user was typing into when this
564 > * request was issued. Set when the chat infrastructure is rewriting an
565 > * untitled session URI to a real one on first send. Controllers can use
566 > * this to bridge any pre-creation state they tracked under the old URI
567 > * (e.g. provisional agent-host sessions) to the new resource that the
568 > * controller returns.
569 > */
570 > readonly untitledResource?: URI;
571 > }
572 >
573 > export interface IChatSessionItemsDelta {
574 > readonly addedOrUpdated?: readonly IChatSessionItem[];
575 > readonly removed?: readonly URI[];
576 > }
577 >
578 > export interface IChatSessionItemController {
579 >
580 > readonly onDidChangeChatSessionItems: Event<IChatSessionItemsDelta>;
581 >
582 > get items(): readonly IChatSessionItem[];
583 >
584 > refresh(token: CancellationToken): Promise<void>;
585 >
586 > newChatSessionItem?(request: IChatNewSessionRequest, token: CancellationToken): Promise<IChatSessionItem | undefined>;
587 >
588 > getNewChatSessionInputState?(sessionResource: URI, token: CancellationToken): Promise<readonly IChatSessionProviderOptionGroup[] | undefined>;
589 >
590 > resolveChatSessionItem?(resource: URI, token: CancellationToken): Promise<IChatSessionItem | undefined>;
591 >
592 > /**
593 > * Permanently delete the session identified by `resource`. Implementations should tear down any backend state for
594 > * the session. The controller is expected to fire an `onDidChangeChatSessionItems` event with the removed resource
595 > * as a result of the deletion.
596 > */
597 > deleteChatSessionItem?(resource: URI, token: CancellationToken): Promise<void>;
598 >
599 > /**
600 > * Set the authoritative archived state for the session identified by `resource`.
601 > */
602 > setChatSessionItemArchived?(resource: URI, archived: boolean): void;
603 > }
604 >
605 > export interface IChatSessionOptionsChangeEvent {
606 > readonly sessionResource: URI;
607 > readonly updates: ReadonlyMap<string, string | IChatSessionProviderOptionItem | undefined>;
608 > }
609 >
610 > export type ResolvedChatSessionsExtensionPoint = Omit<IChatSessionsExtensionPoint, 'icon'> & {
611 > readonly icon: ThemeIcon | URI | undefined;
612 > };
613 >
614 > /**
615 > * Session options as key-value pairs.
616 > *
617 > * Keys correspond to option group IDs (e.g., 'models', 'subagents') and values are either the selected option item IDs (string) or full option items (for locked state).
618 > */
619 > export type ChatSessionOptionsMap = Map<string, string | IChatSessionProviderOptionItem>;
620 >
621 > export namespace ChatSessionOptionsMap {
622 > export function fromRecord(obj: { [key: string]: string | IChatSessionProviderOptionItem }): ChatSessionOptionsMap {
623 return new Map(Object.entries(obj));
624 }
626 > export function toRecord(map: ReadonlyChatSessionOptionsMap): Record<string, string | IChatSessionProviderOptionItem> {
627 const record: Record<string, string | IChatSessionProviderOptionItem> = Object.create(null);
628 const entries = ensureIterable(map);
629 for (const [key, value] of entries) {
630 record[key] = value;
631 }
632 return record;
633 }
635 > export function toStrValueArray(map: ReadonlyChatSessionOptionsMap | undefined): Array<{ optionId: string; value: string }> | undefined {
636 > if (!map) { chatSessionsService.ts ×4
637 return undefined;
638 }
639 > const entries = ensureIterable(map); chatSessionsService.ts ×4
640 > return Array.from(entries, ([optionId, value]) => ({ optionId, value: typeof value === 'string' ? value : value.id }));
641 > }
643 > /**
644 > * Ensures the input is iterable. If a plain object is passed (e.g. due to
645 > * serialization across process boundaries losing the Map prototype), it is
646 > * converted to Map entries on the fly.
647 > */
648 > function ensureIterable(map: ReadonlyChatSessionOptionsMap): Iterable<[string, string | IChatSessionProviderOptionItem]> {
649 > if (map instanceof Map) { chatSessionsService.ts ×4
650 > return map;
651 > }
652 // Fallback: treat as a plain record (e.g. from JSON deserialization)
653 return Object.entries(map as unknown as Record<string, string | IChatSessionProviderOptionItem>);
656 >
657 > /**
658 > * Readonly version of {@link ChatSessionOptionsMap}
659 > */
660 > export type ReadonlyChatSessionOptionsMap = ReadonlyMap<string, string | IChatSessionProviderOptionItem>;
661 >
662 > export interface IChatSessionCustomizationItem {
663 > readonly label: string;
664 > readonly description?: string;
665 > readonly uri: URI;
666 > readonly storageLocation: number;
667 > readonly icon?: ThemeIcon;
668 > }
669 >
670 > export interface IChatSessionCustomizationItemGroup {
671 > readonly id: string;
672 > readonly items: IChatSessionCustomizationItem[];
673 > readonly commands?: readonly { readonly id: string; readonly title: string; readonly arguments?: readonly unknown[] }[];
674 > readonly itemCommands?: readonly { readonly id: string; readonly title: string; readonly arguments?: readonly unknown[] }[];
675 > }
676 >
677 > export interface IChatSessionCustomizationsProvider {
678 > readonly onDidChangeCustomizations: Event<void>;
679 > provideCustomizations(token: CancellationToken): Promise<IChatSessionCustomizationItemGroup[] | undefined>;
680 > }
681 >
682 >
683 > export interface IChatSessionCommitEvent {
684 > /** The original (untitled) session resource. */
685 > readonly original: URI;
686 > /** The committed (real) session resource. */
687 > readonly committed: URI;
688 > }
689 >
690 > export const IChatSessionsService = createDecorator<IChatSessionsService>('chatSessionsService');
691 >
692 > export interface IChatSessionsService {
693 > readonly _serviceBrand: undefined;
694 >
695 > // #region Chat session item provider support
696 > readonly onDidChangeItemsProviders: Event<{ readonly chatSessionType: string }>;
697 > readonly onDidChangeSessionItems: Event<IChatSessionItemsDelta>;
698 >
699 > /**
700 > * Fired when an untitled session is committed (URI swapped to a real resource)
701 > * after the first turn completes.
702 > */
703 > readonly onDidCommitSession: Event<IChatSessionCommitEvent>;
704 >
705 > readonly onDidChangeAvailability: Event<void>;
706 > readonly onDidChangeInProgress: Event<void>;
707 >
708 > getChatSessionContribution(chatSessionType: string): ResolvedChatSessionsExtensionPoint | undefined;
709 > getAllChatSessionContributions(): ResolvedChatSessionsExtensionPoint[];
710 >
711 > /**
712 > * Programmatically register a chat session contribution (for internal session types
713 > * that don't go through the extension point).
714 > */
715 > registerChatSessionContribution(contribution: IChatSessionsExtensionPoint): IDisposable;
716 >
717 > registerChatSessionItemController(chatSessionType: string, controller: IChatSessionItemController): IDisposable;
718 > getRegisteredChatSessionItemProviders(): readonly string[];
719 > activateChatSessionItemProvider(chatSessionType: string): Promise<void>;
720 >
721 > /**
722 > * Get the list of current chat session items grouped by session type.
723 > *
724 > * @param providerTypeFilter If specified, only returns items from the given providers. If undefined, returns items from all providers.
725 > *
726 > * @returns An async iterable that produces the list of session items for each provider. The order is not guaranteed. Some provider may take a long time to resolve.
727 > */
728 > getChatSessionItems(providerTypeFilter: readonly string[] | undefined, token: CancellationToken): AsyncIterable<{ readonly chatSessionType: string; readonly items: readonly IChatSessionItem[] }>;
729 >
730 > /**
731 > * Forces the controllers to refresh their session items, optionally filtered by provider type.
732 > */
733 > refreshChatSessionItems(providerTypeFilter: readonly string[] | undefined, token: CancellationToken): Promise<void>;
734 >
735 > /** @deprecated Use `getChatSessionItems` */
736 > getInProgress(): { chatSessionType: string; count: number }[];
737 >
738 > /**
739 > * Lazily resolves a chat session item, filling in expensive details like timing, changes, and badge.
740 > * Returns the resolved item, or undefined if no resolve handler is available.
741 > */
742 > resolveChatSessionItem(chatSessionType: string, resource: URI, token: CancellationToken): Promise<IChatSessionItem | undefined>;
743 >
744 > /**
745 > * Whether the registered item controller owns archived state for the session.
746 > */
747 > canSetChatSessionItemArchived(sessionResource: URI): boolean;
748 >
749 > /**
750 > * Sets archived state by delegating to the registered item controller.
751 > */
752 > setChatSessionItemArchived(sessionResource: URI, archived: boolean): void;
753 >
754 > // #endregion
755 >
756 > // #region Content provider support
757 > readonly onDidChangeContentProviderSchemes: Event<{ readonly added: string[]; readonly removed: string[] }>;
758 >
759 > getContentProviderSchemes(): string[];
760 >
761 > registerChatSessionContentProvider(scheme: string, provider: IChatSessionContentProvider): IDisposable;
762 > canResolveChatSession(sessionType: string): Promise<boolean>;
763 > getOrCreateChatSession(sessionResource: URI, token: CancellationToken): Promise<IChatSession>;
764 > /** Resolves a parsed response Markdown URI through its session content provider. */
765 > resolveChatResponseUri(sessionResource: URI, href: string, kind: 'link' | 'image'): string;
766 >
767 > /**
768 > * Compute completion items for an input being composed in the chat
769 > * session identified by `sessionResource`. Delegates to the registered
770 > * {@link IChatSessionContentProvider} for the session, if it implements
771 > * {@link IChatSessionContentProvider.provideChatInputCompletions}.
772 > * Returns `undefined` when no provider is available, in which case the
773 > * workbench's default in-process providers should be used.
774 > */
775 > provideChatInputCompletions(sessionResource: URI, params: IChatInputCompletionsParams, token: CancellationToken): Promise<IChatInputCompletionsResult | undefined>;
776 >
777 > /**
778 > * Trigger characters announced by the content provider for the given
779 > * session type. Used to dynamically register Monaco completion
780 > * providers per content-provider scheme. Returns `undefined` when the
781 > * scheme has no content provider, or `[]` when the provider does not
782 > * announce any trigger characters.
783 > */
784 > getChatInputCompletionTriggerCharacters(sessionType: string): Promise<readonly string[] | undefined>;
785 >
786 > getSessionOptions(sessionResource: URI): ReadonlyChatSessionOptionsMap | undefined;
787 > getSessionOption(sessionResource: URI, optionId: string): string | IChatSessionProviderOptionItem | undefined;
788 > setSessionOption(sessionResource: URI, optionId: string, value: string | IChatSessionProviderOptionItem): boolean;
789 > updateSessionOptions(sessionResource: URI, updates: ReadonlyChatSessionOptionsMap): boolean;
790 >
791 > /**
792 > * Fired when options for a chat session change.
793 > */
794 > readonly onDidChangeSessionOptions: Event<IChatSessionOptionsChangeEvent>;
795 >
796 > /**
797 > * Get the capabilities for a specific session type
798 > */
799 > getCapabilitiesForSessionType(chatSessionType: string): IChatAgentAttachmentCapabilities | undefined;
800 >
801 > /**
802 > * Get the customAgentTarget for a specific session type.
803 > * When the Target is not `Target.Undefined`, the mode picker should show filtered custom agents matching this target.
804 > */
805 > getCustomAgentTargetForSessionType(chatSessionType: string): Target;
806 >
807 > /**
808 > * Returns whether the session type requires custom models. When true, the model picker should show filtered custom models.
809 > */
810 > requiresCustomModelsForSessionType(chatSessionType: string): boolean;
811 >
812 > /**
813 > * Returns whether the session type supports the synthetic "Auto" model
814 > * fallback. The built-in local chat always supports it; contributed session
815 > * types default to `false` unless they set `supportsAutoModel`. When false
816 > * and no models are available, the picker shows a "No models available"
817 > * state instead of "Auto".
818 > */
819 > supportsAutoModelForSessionType(chatSessionType: string): boolean;
820 >
821 > /**
822 > * Whether the session type needs a Copilot account and so is unusable until the user signs in (BYOK isn't
823 > * supported). Defaults to false, so third-party types stay usable while signed out.
824 > */
825 > requiresCopilotSignInForSessionType(chatSessionType: string): boolean;
826 >
827 > /**
828 > * Returns whether the session type supports delegation.
829 > * Defaults to true when not explicitly set.
830 > */
831 > supportsDelegationForSessionType(chatSessionType: string): boolean;
832 >
833 > /**
834 > * Returns whether the loaded session supports forking conversations.
835 > */
836 > sessionSupportsFork(sessionResource: URI): boolean;
837 >
838 > /**
839 > * Forks a contributed chat session from the given request point.
840 > * @param sessionResource The session resource to fork.
841 > * @param request The request history item to fork from, or undefined to fork from the end.
842 > * @param token Cancellation token.
843 > * @returns The forked session item, or undefined if forking failed.
844 > */
845 > forkChatSession(sessionResource: URI, request: IChatSessionRequestHistoryItem | undefined, token: CancellationToken): Promise<IChatSessionItem>;
846 >
847 > /**
848 > * Returns whether the loaded session supports renaming.
849 > */
850 > sessionSupportsRename(sessionResource: URI): boolean;
851 >
852 > /**
853 > * Renames a contributed chat session.
854 > * @param sessionResource The session resource to rename.
855 > * @param title The new title for the session.
856 > * @param token Cancellation token.
857 > */
858 > renameChatSession(sessionResource: URI, title: string, token: CancellationToken): Promise<void>;
859 >
860 > readonly onDidChangeOptionGroups: Event<string>;
861 >
862 > getOptionGroupsForSessionType(chatSessionType: string): IChatSessionProviderOptionGroup[] | undefined;
863 > setOptionGroupsForSessionType(chatSessionType: string, handle: number, optionGroups?: readonly IChatSessionProviderOptionGroup[]): void;
864 >
865 > /**
866 > * Get the default options for new sessions of this type, derived from option groups'
867 > * `selected` or `default` items.
868 > */
869 > getNewChatSessionInputState(chatSessionType: string, sessionResource: URI): Promise<readonly IChatSessionProviderOptionGroup[] | undefined>;
870 >
871 > /**
872 > * Creates a new chat session item using the controller's newChatSessionItemHandler.
873 > * Returns undefined if the controller doesn't have a handler or if no controller is registered.
874 > */
875 > createNewChatSessionItem(chatSessionType: string, request: IChatNewSessionRequest, token: CancellationToken): Promise<IChatSessionItem | undefined>;
876 >
877 > /**
878 > * Permanently deletes a chat session item by delegating to the registered controller's `deleteChatSessionItem`
879 > * handler. Throws if the controller does not implement `deleteChatSessionItem`.
880 > */
881 > deleteChatSessionItem(sessionResource: URI, token: CancellationToken): Promise<void>;
882 >
883 > /**
884 > * Records the inverse `real → untitled` alias so option lookups for the real
885 > * session resolve to the untitled session's entry (e.g. {@link updateSessionOptions}).
886 > *
887 > * Call this BEFORE the real session loads, and never remove it — the real
888 > * session keeps reading its options through this alias even after the untitled
889 > * model is disposed. (Only the forward mapping is cleared, via
890 > * {@link clearMaterializedSessionResource}.) Publishing the forward mapping is a
891 > * separate step; see {@link setMaterializedSessionResource}.
892 > */
893 > registerSessionResourceAlias(untitledResource: URI, realResource: URI): void;
894 >
895 > /**
896 > * Records the forward `untitled → real` mapping (read via
897 > * {@link getMaterializedSessionResource}) so a late send still addressed to the
898 > * untitled resource re-targets the real session. Call this only AFTER the real
899 > * session has loaded.
900 > *
901 > * Kept separate from {@link registerSessionResourceAlias} on purpose: the
902 > * inverse alias must exist BEFORE the load (for option lookups), but this
903 > * forward mapping must appear only AFTER the real session exists — published
904 > * earlier, a failed or still-loading session would be re-targeted before it
905 > * exists (a later send would throw "Unknown session").
906 > */
907 > setMaterializedSessionResource(untitledResource: URI, realResource: URI): void;
908 >
909 > /**
910 > * Returns the real session resource that `untitledResource` materialized
911 > * into (via {@link setMaterializedSessionResource}), or `undefined` if it has
912 > * not materialized or the mapping was already cleared.
913 > */
914 > getMaterializedSessionResource(untitledResource: URI): URI | undefined;
915 >
916 > /**
917 > * Clears the forward `untitled → real` mapping for `sessionResource` (passed
918 > * either the untitled key or the real value), so {@link getMaterializedSessionResource}
919 > * stops re-targeting once the session is disposed. Does NOT remove the inverse
920 > * alias, which is intentionally permanent (see {@link registerSessionResourceAlias}).
921 > */
922 > clearMaterializedSessionResource(sessionResource: URI): void;
923 >
924 > /**
925 > * Fires {@link onDidCommitSession} to notify listeners that an untitled
926 > * session has been committed with a real resource URI.
927 > */
928 > fireSessionCommitted(original: URI, committed: URI): void;
929 >
930 > // #region Customizations provider support
931 > readonly onDidChangeCustomizations: Event<{ readonly chatSessionType: string }>;
932 > registerCustomizationsProvider(chatSessionType: string, provider: IChatSessionCustomizationsProvider): IDisposable;
933 > hasCustomizationsProvider(chatSessionType: string): boolean;
934 > getCustomizations(chatSessionType: string, token: CancellationToken): Promise<IChatSessionCustomizationItemGroup[] | undefined>;
935 > // #endregion
936 > }
937 >
938 > export function isSessionInProgressStatus(state: ChatSessionStatus): boolean {
939 return state === ChatSessionStatus.InProgress || state === ChatSessionStatus.NeedsInput;
940 }
942 > export function isIChatSessionFileChange2(obj: unknown): obj is IChatSessionFileChange2 {
943 > const candidate = obj as IChatSessionFileChange2; session.ts ×1
944 > return candidate && candidate.uri instanceof URI && typeof candidate.insertions === 'number' && typeof candidate.deletions === 'number';
945 > }