chatAgents.ts ×42

Frontier kind: Code frontier

unlabeled · c_d650517f7f4a

346 tests · 29797 LOC · 144 files · introduces 0 tests · 439 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
42 ranges439 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2865 ranges29797 lines · 144 files · Browse complete extent
All tests (intent)
346 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.

1 file ranked by introduced lines: 439 introduced LOC across 42 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/participants/chatAgents.ts 439 introduced LOC · 42 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- chatAgents.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 { findLast } from '../../../../../base/common/arraysFind.js';
7 > import { CancellationToken } from '../../../../../base/common/cancellation.js';
8 > import { IStringDictionary } from '../../../../../base/common/collections.js';
9 > import { Emitter, Event } from '../../../../../base/common/event.js';
10 > import { IMarkdownString } from '../../../../../base/common/htmlContent.js';
11 > import { Iterable } from '../../../../../base/common/iterator.js';
12 > import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
13 > import { revive, Revived } from '../../../../../base/common/marshalling.js';
14 > import { IObservable } from '../../../../../base/common/observable.js';
15 > import { equalsIgnoreCase } from '../../../../../base/common/strings.js';
16 > import { ThemeIcon } from '../../../../../base/common/themables.js';
17 > import { URI } from '../../../../../base/common/uri.js';
18 > import { Command } from '../../../../../editor/common/languages.js';
19 > import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
20 > import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';
21 > import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js';
22 > import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
23 > import { ChatContextKeys } from '../actions/chatContextKeys.js';
24 > import { IChatAgentEditedFileEvent, IChatProgressHistoryResponseContent, IChatRequestModeInstructions, IChatRequestVariableData, ISerializableChatAgentData } from '../model/chatModel.js';
25 > import { ChatRequestHooks } from '../promptSyntax/hookSchema.js';
26 > import { IRawChatCommandContribution } from './chatParticipantContribTypes.js';
27 > import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from '../chatService/chatService.js';
28 > import { ChatAgentLocation, ChatConfiguration, ChatModeKind, ChatPermissionLevel } from '../constants.js';
29 > import { ILanguageModelsService } from '../languageModels.js';
30 > import { ChatPerfMark, markChat } from '../chatPerf.js';
31 >
32 > //#region agent service, commands etc
33 >
34 > export interface IChatAgentHistoryEntry {
35 > request: IChatAgentRequest;
36 > response: ReadonlyArray<IChatProgressHistoryResponseContent | IChatTaskDto>;
37 > result: IChatAgentResult;
38 > }
39 >
40 > export interface IChatAgentAttachmentCapabilities {
41 > supportsFileAttachments?: boolean;
42 > supportsToolAttachments?: boolean;
43 > supportsMCPAttachments?: boolean;
44 > supportsImageAttachments?: boolean;
45 > supportsSearchResultAttachments?: boolean;
46 > supportsInstructionAttachments?: boolean;
47 > supportsSourceControlAttachments?: boolean;
48 > supportsProblemAttachments?: boolean;
49 > supportsSymbolAttachments?: boolean;
50 > supportsTerminalAttachments?: boolean;
51 > supportsPromptAttachments?: boolean;
52 > supportsHandOffs?: boolean;
53 > supportsCheckpoints?: boolean;
54 > /**
55 > * The prefix (e.g. `!`) that marks a message in this
56 > * session type as a terminal command rather than a message to the agent.
57 > * Undefined when the session type has no terminal command support.
58 > */
59 > terminalCommandPrefix?: string;
60 > }
61 >
62 > export interface IChatAgentData {
63 > id: string;
64 > name: string;
65 > fullName?: string;
66 > description?: string;
67 > /** This is string, not ContextKeyExpression, because dealing with serializing/deserializing is hard and need a better pattern for this */
68 > when?: string;
69 > extensionId: ExtensionIdentifier;
70 > extensionVersion: string | undefined;
71 > extensionPublisherId: string;
72 > /** This is the extension publisher id, or, in the case of a dynamically registered participant (remote agent), whatever publisher name we have for it */
73 > publisherDisplayName?: string;
74 > extensionDisplayName: string;
75 > /** The agent invoked when no agent is specified */
76 > isDefault?: boolean;
77 > /** This agent is not contributed in package.json, but is registered dynamically */
78 > isDynamic?: boolean;
79 > /** This agent is contributed from core and not from an extension */
80 > isCore?: boolean;
81 > canAccessPreviousChatHistory?: boolean;
82 > metadata: IChatAgentMetadata;
83 > slashCommands: IChatAgentCommand[];
84 > locations: ChatAgentLocation[];
85 > /** This is only relevant for isDefault agents. Others should have all modes available. */
86 > modes: ChatModeKind[];
87 > disambiguation: { category: string; description: string; examples: string[] }[];
88 > capabilities?: IChatAgentAttachmentCapabilities;
89 > }
90 >
91 > export interface IChatWelcomeMessageContent {
92 > icon: ThemeIcon;
93 > title: string;
94 > message: IMarkdownString;
95 > }
96 >
97 > export interface IChatAgentImplementation {
98 > invoke(request: IChatAgentRequest, progress: (parts: IChatProgress[]) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult>;
99 > setRequestTools?(requestId: string, tools: UserSelectedTools): void;
100 > setYieldRequested?(requestId: string, value: boolean): void;
101 > provideFollowups?(request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]>;
102 > provideChatTitle?: (history: IChatAgentHistoryEntry[], token: CancellationToken) => Promise<string | undefined>;
103 > provideChatSummary?: (history: IChatAgentHistoryEntry[], token: CancellationToken) => Promise<string | undefined>;
104 > }
105 >
106 > export interface IChatParticipantDetectionResult {
107 > participant: string;
108 > command?: string;
109 > }
110 >
111 > export interface IChatParticipantMetadata {
112 > participant: string;
113 > command?: string;
114 > disambiguation: { category: string; description: string; examples: string[] }[];
115 > }
116 >
117 > export interface IChatParticipantDetectionProvider {
118 > provideParticipantDetection(request: IChatAgentRequest, history: IChatAgentHistoryEntry[], options: { location: ChatAgentLocation; participants: IChatParticipantMetadata[] }, token: CancellationToken): Promise<IChatParticipantDetectionResult | null | undefined>;
119 > }
120 >
121 > export type IChatAgent = IChatAgentData & IChatAgentImplementation;
122 >
123 > export interface IChatAgentCommand extends IRawChatCommandContribution {
124 > followupPlaceholder?: string;
125 > }
126 >
127 > export interface IChatAgentMetadata {
128 > helpTextPrefix?: string | IMarkdownString;
129 > helpTextPostfix?: string | IMarkdownString;
130 > icon?: URI;
131 > iconDark?: URI;
132 > themeIcon?: ThemeIcon;
133 > sampleRequest?: string;
134 > supportIssueReporting?: boolean;
135 > followupPlaceholder?: string;
136 > isSticky?: boolean;
137 > additionalWelcomeMessage?: string | IMarkdownString;
138 > }
139 >
140 > export type UserSelectedTools = Record<string, boolean>;
141 >
142 >
143 > export interface IChatAgentRequest {
144 > sessionResource: URI;
145 > requestId: string;
146 > agentId: string;
147 > command?: string;
148 > message: string;
149 > attempt?: number;
150 > enableCommandDetection?: boolean;
151 > isParticipantDetected?: boolean;
152 > variables: IChatRequestVariableData;
153 > location: ChatAgentLocation;
154 > locationData?: Revived<IChatLocationData>;
155 > acceptedConfirmationData?: unknown[];
156 > rejectedConfirmationData?: unknown[];
157 > agentHostSessionConfig?: Record<string, unknown>;
158 > userSelectedModelId?: string;
159 > modelConfiguration?: IStringDictionary<unknown>;
160 > userSelectedTools?: UserSelectedTools;
161 > modeInstructions?: IChatRequestModeInstructions;
162 > editedFileEvents?: IChatAgentEditedFileEvent[];
163 > /**
164 > * The working directory URI for the session, if set.
165 > * In the agents window, each session can have its own working directory
166 > * that differs from the current workspace folders.
167 > */
168 > workingDirectory?: URI;
169 > /**
170 > * Collected hooks configuration for this request.
171 > * Contains all hooks defined in hooks .json files, organized by hook type.
172 > */
173 > hooks?: ChatRequestHooks;
174 > /**
175 > * Whether any hooks are enabled for this request.
176 > */
177 > hasHooksEnabled?: boolean;
178 > /**
179 > * The permission level for tool auto-approval in this request.
180 > * - `'autoApprove'`: Auto-approve all tool calls and retry on errors.
181 > * - `'autopilot'`: Everything autoApprove does plus continues until the task is done.
182 > */
183 > permissionLevel?: ChatPermissionLevel;
184 > /**
185 > * Unique ID for the subagent invocation, used to group tool calls from the same subagent run together.
186 > */
187 > subAgentInvocationId?: string;
188 > /**
189 > * Display name of the subagent that is invoking this request.
190 > */
191 > subAgentName?: string;
192 > /**
193 > * The request ID of the parent request that invoked this subagent.
194 > */
195 > parentRequestId?: string;
196 >
197 > /**
198 > * When true, this request was initiated by the system rather than the user.
199 > */
200 > isSystemInitiated?: boolean;
201 > }
202 >
203 > export interface IChatQuestion {
204 > readonly prompt: string;
205 > readonly participant?: string;
206 > readonly command?: string;
207 > }
208 >
209 > export interface IChatAgentResultTimings {
210 > firstProgress?: number;
211 > totalElapsed: number;
212 > }
213 >
214 > export interface IChatAgentResult {
215 > errorDetails?: IChatResponseErrorDetails;
216 > timings?: IChatAgentResultTimings;
217 > /** Extra properties that the agent can use to identify a result */
218 > readonly metadata?: { readonly [key: string]: unknown };
219 > readonly details?: string;
220 > nextQuestion?: IChatQuestion;
221 > }
222 >
223 > export const IChatAgentService = createDecorator<IChatAgentService>('chatAgentService');
224 >
225 > interface IChatAgentEntry {
226 > data: IChatAgentData;
227 > impl?: IChatAgentImplementation;
228 > }
229 >
230 > export interface IChatAgentCompletionItem {
231 > id: string;
232 > name?: string;
233 > fullName?: string;
234 > icon?: ThemeIcon;
235 > value: unknown;
236 > command?: Command;
237 > }
238 >
239 > export interface IChatAgentInvocationEvent {
240 > readonly agentId: string;
241 > readonly request: Readonly<IChatAgentRequest>;
242 > }
243 >
244 > export interface IChatAgentService {
245 > _serviceBrand: undefined;
246 > /**
247 > * undefined when an agent was removed
248 > */
249 > readonly onDidChangeAgents: Event<IChatAgent | undefined>;
250 > readonly onWillInvokeAgent: Event<IChatAgentInvocationEvent>;
251 > readonly hasToolsAgent: boolean;
252 > registerAgent(id: string, data: IChatAgentData): IDisposable;
253 > registerAgentImplementation(id: string, agent: IChatAgentImplementation): IDisposable;
254 > registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable;
255 > registerAgentCompletionProvider(id: string, provider: (query: string, token: CancellationToken) => Promise<IChatAgentCompletionItem[]>): IDisposable;
256 > getAgentCompletionItems(id: string, query: string, token: CancellationToken): Promise<IChatAgentCompletionItem[]>;
257 > registerChatParticipantDetectionProvider(handle: number, provider: IChatParticipantDetectionProvider): IDisposable;
258 > detectAgentOrCommand(request: IChatAgentRequest, history: IChatAgentHistoryEntry[], options: { location: ChatAgentLocation }, token: CancellationToken): Promise<{ agent: IChatAgentData; command?: IChatAgentCommand } | undefined>;
259 > hasChatParticipantDetectionProviders(): boolean;
260 > invokeAgent(agent: string, request: IChatAgentRequest, progress: (parts: IChatProgress[]) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult>;
261 > setRequestTools(agent: string, requestId: string, tools: UserSelectedTools): void;
262 > setYieldRequested(agent: string, requestId: string, value: boolean): void;
263 > getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]>;
264 > getChatTitle(id: string, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<string | undefined>;
265 > getChatSummary(id: string, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<string | undefined>;
266 > getAgent(id: string, includeDisabled?: boolean): IChatAgentData | undefined;
267 > getAgentByFullyQualifiedId(id: string): IChatAgentData | undefined;
268 > getAgents(): IChatAgentData[];
269 > getActivatedAgents(): Array<IChatAgent>;
270 > getAgentsByName(name: string): IChatAgentData[];
271 > agentHasDupeName(id: string): boolean;
272 >
273 > /**
274 > * Get the default agent (only if activated)
275 > */
276 > getDefaultAgent(location: ChatAgentLocation, mode?: ChatModeKind): IChatAgent | undefined;
277 >
278 > /**
279 > * Get the default agent data that has been contributed (may not be activated yet)
280 > */
281 > getContributedDefaultAgent(location: ChatAgentLocation): IChatAgentData | undefined;
282 > updateAgent(id: string, updateMetadata: IChatAgentMetadata): void;
283 > }
284 >
285 > export class ChatAgentService extends Disposable implements IChatAgentService {
286 >
287 > public static readonly AGENT_LEADER = '@';
288 >
289 > declare _serviceBrand: undefined;
290 >
291 > private _agents = new Map<string, IChatAgentEntry>();
292 >
293 > private readonly _onDidChangeAgents = this._register(new Emitter<IChatAgent | undefined>());
294 > readonly onDidChangeAgents: Event<IChatAgent | undefined> = this._onDidChangeAgents.event;
295 > private readonly _onWillInvokeAgent = this._register(new Emitter<IChatAgentInvocationEvent>());
296 > readonly onWillInvokeAgent: Event<IChatAgentInvocationEvent> = this._onWillInvokeAgent.event;
297 >
298 > private readonly _agentsContextKeys = new Set<string>();
299 > private readonly _hasDefaultAgent: IContextKey<boolean>;
300 > private readonly _extensionAgentRegistered: IContextKey<boolean>;
301 > private readonly _defaultAgentRegistered: IContextKey<boolean>;
302 > private _hasToolsAgent = false;
303 >
304 > private _chatParticipantDetectionProviders = new Map<number, IChatParticipantDetectionProvider>();
305 >
306 > constructor(
307 @IContextKeyService private readonly contextKeyService: IContextKeyService,
308 @IConfigurationService private readonly configurationService: IConfigurationService,
318 }));
319 }
321 > registerAgent(id: string, data: IChatAgentData): IDisposable {
322 const existingAgent = this.getAgent(id);
323 if (existingAgent) {
346 });
347 }
349 > private _updateAgentsContextKeys(): void {
350 // Update the set of context keys used by all agents
351 this._agentsContextKeys.clear();
359 }
360 }
362 > private _updateContextKeys(): void {
363 let extensionAgentRegistered = false;
364 let defaultAgentRegistered = false;
384 }
385 }
387 > registerAgentImplementation(id: string, agentImpl: IChatAgentImplementation): IDisposable {
388 const entry = this._agents.get(id);
389 if (!entry) {
411 });
412 }
414 > registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable {
415 data.isDynamic = true;
416 const agent = { data, impl: agentImpl };
423 });
424 }
426 > private _agentCompletionProviders = new Map<string, (query: string, token: CancellationToken) => Promise<IChatAgentCompletionItem[]>>();
427 >
428 > registerAgentCompletionProvider(id: string, provider: (query: string, token: CancellationToken) => Promise<IChatAgentCompletionItem[]>) {
429 this._agentCompletionProviders.set(id, provider);
430 return {
432 };
433 }
435 > async getAgentCompletionItems(id: string, query: string, token: CancellationToken) {
436 return await this._agentCompletionProviders.get(id)?.(query, token) ?? [];
437 }
439 > updateAgent(id: string, updateMetadata: IChatAgentMetadata): void {
440 const agent = this._agents.get(id);
441 if (!agent?.impl) {
445 this._onDidChangeAgents.fire(new MergedChatAgent(agent.data, agent.impl));
446 }
448 > getDefaultAgent(location: ChatAgentLocation, mode: ChatModeKind = ChatModeKind.Ask): IChatAgent | undefined {
449 return this._preferExtensionAgent(this.getActivatedAgents().filter(a => {
450 if (mode && !a.modes.includes(mode)) {
455 }));
456 }
458 > public get hasToolsAgent(): boolean {
459 // The chat participant enablement is just based on this setting. Don't wait for the extension to be loaded.
460 return !!this.configurationService.getValue(ChatConfiguration.AgentEnabled);
461 }
463 > getContributedDefaultAgent(location: ChatAgentLocation): IChatAgentData | undefined {
464 return this._preferExtensionAgent(this.getAgents().filter(a => !!a.isDefault && a.locations.includes(location)));
465 }
467 > private _preferExtensionAgent<T extends IChatAgentData>(agents: T[]): T | undefined {
468 // We potentially have multiple agents on the same location,
469 // contributed from core and from extensions.
472 return findLast(agents, agent => !agent.isCore) ?? agents.at(-1);
473 }
475 > getAgent(id: string, includeDisabled = false): IChatAgentData | undefined {
476 if (!this._agentIsEnabled(id) && !includeDisabled) {
477 return;
480 return this._agents.get(id)?.data;
481 }
483 > private _agentIsEnabled(idOrAgent: string | IChatAgentEntry): boolean {
484 const entry = typeof idOrAgent === 'string' ? this._agents.get(idOrAgent) : idOrAgent;
485 return !entry?.data.when || this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(entry.data.when));
486 }
488 > getAgentByFullyQualifiedId(id: string): IChatAgentData | undefined {
489 const agent = Iterable.find(this._agents.values(), a => getFullyQualifiedId(a.data) === id)?.data;
490 if (agent && !this._agentIsEnabled(agent.id)) {
494 return agent;
495 }
497 > /**
498 > * Returns all agent datas that exist- static registered and dynamic ones.
499 > */
500 > getAgents(): IChatAgentData[] {
501 return Array.from(this._agents.values())
502 .map(entry => entry.data)
503 .filter(a => this._agentIsEnabled(a.id));
504 }
506 > getActivatedAgents(): IChatAgent[] {
507 return Array.from(this._agents.values())
508 .filter(a => !!a.impl)
510 .map(a => new MergedChatAgent(a.data, a.impl!));
511 }
513 > getAgentsByName(name: string): IChatAgentData[] {
514 return this._preferExtensionAgents(this.getAgents().filter(a => a.name === name));
515 }
517 > private _preferExtensionAgents<T extends IChatAgentData>(agents: T[]): T[] {
518 // We potentially have multiple agents on the same location,
519 // contributed from core and from extensions.
523 return extensionAgents.length > 0 ? extensionAgents : agents;
524 }
526 > agentHasDupeName(id: string): boolean {
527 const agent = this.getAgent(id);
528 if (!agent) {
533 .filter(a => a.extensionId.value !== agent.extensionId.value).length > 0;
534 }
536 > async invokeAgent(id: string, request: IChatAgentRequest, progress: (parts: IChatProgress[]) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> {
537 markChat(request.sessionResource, ChatPerfMark.AgentWillInvoke);
538 const data = this._agents.get(id);
546 return result;
547 }
549 > setRequestTools(id: string, requestId: string, tools: UserSelectedTools): void {
550 const data = this._agents.get(id);
551 if (!data?.impl) {
555 data.impl.setRequestTools?.(requestId, tools);
556 }
558 > setYieldRequested(id: string, requestId: string, value: boolean): void {
559 const data = this._agents.get(id);
560 if (!data?.impl) {
564 data.impl.setYieldRequested?.(requestId, value);
565 }
567 > async getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]> {
568 const data = this._agents.get(id);
569 if (!data?.impl?.provideFollowups) {
573 return data.impl.provideFollowups(request, result, history, token);
574 }
576 > async getChatTitle(id: string, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<string | undefined> {
577 const data = this._agents.get(id);
578 if (!data?.impl?.provideChatTitle) {
582 return data.impl.provideChatTitle(history, token);
583 }
585 > async getChatSummary(id: string, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<string | undefined> {
586 const data = this._agents.get(id);
587 if (!data?.impl?.provideChatSummary) {
591 return data.impl.provideChatSummary(history, token);
592 }
594 > registerChatParticipantDetectionProvider(handle: number, provider: IChatParticipantDetectionProvider) {
595 this._chatParticipantDetectionProviders.set(handle, provider);
596 return toDisposable(() => {
598 });
599 }
601 > hasChatParticipantDetectionProviders() {
602 return this._chatParticipantDetectionProviders.size > 0;
603 }
605 > async detectAgentOrCommand(request: IChatAgentRequest, history: IChatAgentHistoryEntry[], options: { location: ChatAgentLocation }, token: CancellationToken): Promise<{ agent: IChatAgentData; command?: IChatAgentCommand } | undefined> {
606 // TODO@joyceerhl should we have a selector to be able to narrow down which provider to use
607 const provider = Iterable.first(this._chatParticipantDetectionProviders.values());
643 return { agent, command };
644 }
645 > } chatAgents.ts
646 >
647 > export class MergedChatAgent implements IChatAgent {
648 > constructor(
649 private readonly data: IChatAgentData,
650 private readonly impl: IChatAgentImplementation
651 ) { }
652 > when?: string | undefined; chatAgents.ts
653 > publisherDisplayName?: string | undefined;
654 > isDynamic?: boolean | undefined;
655 >
656 > get id(): string { return this.data.id; }
657 > get name(): string { return this.data.name ?? ''; }
658 > get fullName(): string { return this.data.fullName ?? ''; }
659 > get description(): string { return this.data.description ?? ''; }
660 > get extensionId(): ExtensionIdentifier { return this.data.extensionId; }
661 > get extensionVersion(): string | undefined { return this.data.extensionVersion; }
662 > get extensionPublisherId(): string { return this.data.extensionPublisherId; }
663 > get extensionPublisherDisplayName() { return this.data.publisherDisplayName; }
664 > get extensionDisplayName(): string { return this.data.extensionDisplayName; }
665 > get isDefault(): boolean | undefined { return this.data.isDefault; }
666 > get isCore(): boolean | undefined { return this.data.isCore; }
667 > get metadata(): IChatAgentMetadata { return this.data.metadata; }
668 > get slashCommands(): IChatAgentCommand[] { return this.data.slashCommands; }
669 > get locations(): ChatAgentLocation[] { return this.data.locations; }
670 > get modes(): ChatModeKind[] { return this.data.modes; }
671 > get disambiguation(): { category: string; description: string; examples: string[] }[] { return this.data.disambiguation; }
672 >
673 > async invoke(request: IChatAgentRequest, progress: (parts: IChatProgress[]) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatAgentResult> {
674 return this.impl.invoke(request, progress, history, token);
675 }
677 > setRequestTools(requestId: string, tools: UserSelectedTools): void {
678 this.impl.setRequestTools?.(requestId, tools);
679 }
681 > setYieldRequested(requestId: string, value: boolean): void {
682 this.impl.setYieldRequested?.(requestId, value);
683 }
685 > async provideFollowups(request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise<IChatFollowup[]> {
686 if (this.impl.provideFollowups) {
687 return this.impl.provideFollowups(request, result, history, token);
690 return [];
691 }
693 > toJSON(): IChatAgentData {
694 return this.data;
695 }
696 > } chatAgents.ts
697 >
698 > export const IChatAgentNameService = createDecorator<IChatAgentNameService>('chatAgentNameService');
699 >
700 > export interface IChatAgentNameService {
701 > _serviceBrand: undefined;
702 > getAgentNameRestriction(chatAgentData: IChatAgentData): boolean;
703 > }
704 >
705 > export class ChatAgentNameService implements IChatAgentNameService {
706 >
707 > declare _serviceBrand: undefined;
708 >
709 > constructor(
710 @ILanguageModelsService private readonly languageModelsService: ILanguageModelsService,
711 ) {
712 }
714 > /**
715 > * Returns true if the agent is allowed to use this name
716 > */
717 > getAgentNameRestriction(chatAgentData: IChatAgentData): boolean {
718 if (chatAgentData.isCore) {
719 return true; // core agents are always allowed to use any name
725 return nameAllowed && fullNameAllowed;
726 }
728 > private checkAgentNameRestriction(name: string, chatAgentData: IChatAgentData): IObservable<boolean> {
729 // Registry is a map of name to an array of extension publisher IDs or extension IDs that are allowed to use it.
730 // Look up the list of extensions that are allowed to use this name
738 });
739 }
740 > } chatAgents.ts
741 >
742 > export function getFullyQualifiedId(chatAgentData: IChatAgentData): string {
743 return `${chatAgentData.extensionId.value}.${chatAgentData.id}`;
744 }
746 > /**
747 > * There was a period where serialized chat agent data used 'id' instead of 'name'.
748 > * Don't copy this pattern, serialized data going forward should be versioned with strict interfaces.
749 > */
750 > interface IOldSerializedChatAgentData extends Omit<ISerializableChatAgentData, 'name'> {
751 > id: string;
752 > extensionPublisher?: string;
753 > }
754 >
755 function isSerializableChatAgentData(obj: ISerializableChatAgentData | IOldSerializedChatAgentData): obj is ISerializableChatAgentData {
756 return (obj as ISerializableChatAgentData).name !== undefined;
757 }
759 > export function reviveSerializedAgent(raw: ISerializableChatAgentData | IOldSerializedChatAgentData): IChatAgentData {
760 const normalized: ISerializableChatAgentData = isSerializableChatAgentData(raw) ?
761 raw :