Skip to content

Commit 88d46fe

Browse files
authored
Add proposed API for prompt files providers (#280426)
1 parent efcd799 commit 88d46fe

File tree

11 files changed

+584
-187
lines changed

11 files changed

+584
-187
lines changed

src/vs/platform/extensions/common/extensionsApiProposals.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const _allApiProposals = {
5353
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts',
5454
version: 11
5555
},
56+
chatPromptFiles: {
57+
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatPromptFiles.d.ts',
58+
version: 1
59+
},
5660
chatProvider: {
5761
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts',
5862
version: 4

src/vs/workbench/api/browser/mainThreadChatAgents2.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIde
2626
import { IChatWidgetService } from '../../contrib/chat/browser/chat.js';
2727
import { AddDynamicVariableAction, IAddDynamicVariableContext } from '../../contrib/chat/browser/attachments/chatDynamicVariables.js';
2828
import { IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/participants/chatAgents.js';
29-
import { ICustomAgentQueryOptions, IPromptsService } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
29+
import { IPromptFileContext, IPromptsService } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
30+
import { isValidPromptType } from '../../contrib/chat/common/promptSyntax/promptTypes.js';
3031
import { IChatEditingService, IChatRelatedFileProviderMetadata } from '../../contrib/chat/common/editing/chatEditingService.js';
3132
import { IChatModel } from '../../contrib/chat/common/model/chatModel.js';
3233
import { ChatRequestAgentPart } from '../../contrib/chat/common/requestParser/chatParserTypes.js';
@@ -96,8 +97,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
9697

9798
private readonly _chatRelatedFilesProviders = this._register(new DisposableMap<number, IDisposable>());
9899

99-
private readonly _customAgentsProviders = this._register(new DisposableMap<number, IDisposable>());
100-
private readonly _customAgentsProviderEmitters = this._register(new DisposableMap<number, Emitter<void>>());
100+
private readonly _promptFileProviders = this._register(new DisposableMap<number, IDisposable>());
101+
private readonly _promptFileProviderEmitters = this._register(new DisposableMap<number, Emitter<void>>());
101102

102103
private readonly _pendingProgress = new Map<string, { progress: (parts: IChatProgress[]) => void; chatSession: IChatModel | undefined }>();
103104
private readonly _proxy: ExtHostChatAgentsShape2;
@@ -435,41 +436,46 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
435436
this._chatRelatedFilesProviders.deleteAndDispose(handle);
436437
}
437438

438-
async $registerCustomAgentsProvider(handle: number, extensionId: ExtensionIdentifier): Promise<void> {
439+
async $registerPromptFileProvider(handle: number, type: string, extensionId: ExtensionIdentifier): Promise<void> {
439440
const extension = await this._extensionService.getExtension(extensionId.value);
440441
if (!extension) {
441-
this._logService.error(`[MainThreadChatAgents2] Could not find extension for CustomAgentsProvider: ${extensionId.value}`);
442+
this._logService.error(`[MainThreadChatAgents2] Could not find extension for prompt file provider: ${extensionId.value}`);
443+
return;
444+
}
445+
446+
if (!isValidPromptType(type)) {
447+
this._logService.error(`[MainThreadChatAgents2] Invalid contribution type: ${type}`);
442448
return;
443449
}
444450

445451
const emitter = new Emitter<void>();
446-
this._customAgentsProviderEmitters.set(handle, emitter);
452+
this._promptFileProviderEmitters.set(handle, emitter);
447453

448-
const disposable = this._promptsService.registerCustomAgentsProvider(extension, {
449-
onDidChangeCustomAgents: emitter.event,
450-
provideCustomAgents: async (options: ICustomAgentQueryOptions, token: CancellationToken) => {
451-
const agents = await this._proxy.$provideCustomAgents(handle, options, token);
452-
if (!agents) {
454+
const disposable = this._promptsService.registerPromptFileProvider(extension, type, {
455+
onDidChangePromptFiles: emitter.event,
456+
providePromptFiles: async (context: IPromptFileContext, token: CancellationToken) => {
457+
const contributions = await this._proxy.$providePromptFiles(handle, type, context, token);
458+
if (!contributions) {
453459
return undefined;
454460
}
455461
// Convert UriComponents to URI
456-
return agents.map(agent => ({
457-
...agent,
458-
uri: URI.revive(agent.uri)
462+
return contributions.map(c => ({
463+
...c,
464+
uri: URI.revive(c.uri)
459465
}));
460466
}
461467
});
462468

463-
this._customAgentsProviders.set(handle, disposable);
469+
this._promptFileProviders.set(handle, disposable);
464470
}
465471

466-
$unregisterCustomAgentsProvider(handle: number): void {
467-
this._customAgentsProviders.deleteAndDispose(handle);
468-
this._customAgentsProviderEmitters.deleteAndDispose(handle);
472+
$unregisterPromptFileProvider(handle: number): void {
473+
this._promptFileProviders.deleteAndDispose(handle);
474+
this._promptFileProviderEmitters.deleteAndDispose(handle);
469475
}
470476

471-
$onDidChangeCustomAgents(handle: number): void {
472-
const emitter = this._customAgentsProviderEmitters.get(handle);
477+
$onDidChangePromptFiles(handle: number): void {
478+
const emitter = this._promptFileProviderEmitters.get(handle);
473479
if (emitter) {
474480
emitter.fire();
475481
}

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getRemoteName } from '../../../platform/remote/common/remoteHosts.js';
2323
import { TelemetryTrustedValue } from '../../../platform/telemetry/common/telemetryUtils.js';
2424
import { EditSessionIdentityMatch } from '../../../platform/workspace/common/editSessions.js';
2525
import { DebugConfigurationProviderTriggerKind } from '../../contrib/debug/common/debug.js';
26+
import { PromptsType } from '../../contrib/chat/common/promptSyntax/promptTypes.js';
2627
import { ExtensionDescriptionRegistry } from '../../services/extensions/common/extensionDescriptionRegistry.js';
2728
import { UIKind } from '../../services/extensions/common/extensionHostProtocol.js';
2829
import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
@@ -1541,9 +1542,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
15411542
checkProposedApiEnabled(extension, 'chatContextProvider');
15421543
return extHostChatContext.registerChatContextProvider(selector ? checkSelector(selector) : undefined, `${extension.id}-${id}`, provider);
15431544
},
1544-
registerCustomAgentsProvider(provider: vscode.CustomAgentsProvider): vscode.Disposable {
1545-
checkProposedApiEnabled(extension, 'chatParticipantPrivate');
1546-
return extHostChatAgents2.registerCustomAgentsProvider(extension, provider);
1545+
registerCustomAgentProvider(provider: vscode.CustomAgentProvider): vscode.Disposable {
1546+
checkProposedApiEnabled(extension, 'chatPromptFiles');
1547+
return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.agent, provider);
1548+
},
1549+
registerInstructionsProvider(provider: vscode.InstructionsProvider): vscode.Disposable {
1550+
checkProposedApiEnabled(extension, 'chatPromptFiles');
1551+
return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.instructions, provider);
1552+
},
1553+
registerPromptFileProvider(provider: vscode.PromptFileProvider): vscode.Disposable {
1554+
checkProposedApiEnabled(extension, 'chatPromptFiles');
1555+
return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.prompt, provider);
15471556
},
15481557
};
15491558

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ import { IChatRequestVariableValue } from '../../contrib/chat/common/attachments
6565
import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
6666
import { IChatMessage, IChatResponsePart, ILanguageModelChatMetadataAndIdentifier, ILanguageModelChatSelector } from '../../contrib/chat/common/languageModels.js';
6767
import { IPreparedToolInvocation, IToolInvocation, IToolInvocationPreparationContext, IToolProgressStep, IToolResult, ToolDataSource } from '../../contrib/chat/common/tools/languageModelToolsService.js';
68-
import { ICustomAgentQueryOptions, IExternalCustomAgent } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
68+
import { IPromptFileContext, IPromptFileResource } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
6969
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugTestRunReference, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from '../../contrib/debug/common/debug.js';
7070
import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerLaunch } from '../../contrib/mcp/common/mcpTypes.js';
7171
import * as notebookCommon from '../../contrib/notebook/common/notebookCommon.js';
@@ -99,6 +99,7 @@ import { ISaveProfileResult } from '../../services/userDataProfile/common/userDa
9999
import { IExtHostDocumentSaveDelegate } from './extHostDocumentData.js';
100100
import { TerminalShellExecutionCommandLineConfidence } from './extHostTypes.js';
101101
import * as tasks from './shared/tasks.js';
102+
import { PromptsType } from '../../contrib/chat/common/promptSyntax/promptTypes.js';
102103

103104
export type IconPathDto =
104105
| UriComponents
@@ -1394,9 +1395,9 @@ export interface MainThreadChatAgentsShape2 extends IChatAgentProgressShape, IDi
13941395
$unregisterChatParticipantDetectionProvider(handle: number): void;
13951396
$registerRelatedFilesProvider(handle: number, metadata: IChatRelatedFilesProviderMetadata): void;
13961397
$unregisterRelatedFilesProvider(handle: number): void;
1397-
$registerCustomAgentsProvider(handle: number, extension: ExtensionIdentifier): void;
1398-
$unregisterCustomAgentsProvider(handle: number): void;
1399-
$onDidChangeCustomAgents(handle: number): void;
1398+
$registerPromptFileProvider(handle: number, type: string, extension: ExtensionIdentifier): void;
1399+
$unregisterPromptFileProvider(handle: number): void;
1400+
$onDidChangePromptFiles(handle: number): void;
14001401
$registerAgentCompletionsProvider(handle: number, id: string, triggerCharacters: string[]): void;
14011402
$unregisterAgentCompletionsProvider(handle: number, id: string): void;
14021403
$updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void;
@@ -1462,7 +1463,7 @@ export interface ExtHostChatAgentsShape2 {
14621463
$releaseSession(sessionResource: UriComponents): void;
14631464
$detectChatParticipant(handle: number, request: Dto<IChatAgentRequest>, context: { history: IChatAgentHistoryEntryDto[] }, options: { participants: IChatParticipantMetadata[]; location: ChatAgentLocation }, token: CancellationToken): Promise<IChatParticipantDetectionResult | null | undefined>;
14641465
$provideRelatedFiles(handle: number, request: Dto<IChatRequestDraft>, token: CancellationToken): Promise<Dto<IChatRelatedFile>[] | undefined>;
1465-
$provideCustomAgents(handle: number, options: ICustomAgentQueryOptions, token: CancellationToken): Promise<Dto<IExternalCustomAgent>[] | undefined>;
1466+
$providePromptFiles(handle: number, type: PromptsType, context: IPromptFileContext, token: CancellationToken): Promise<Dto<IPromptFileResource>[] | undefined>;
14661467
$setRequestTools(requestId: string, tools: UserSelectedTools): void;
14671468
}
14681469
export interface IChatParticipantMetadata {

src/vs/workbench/api/common/extHostChatAgents2.ts

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ import { ExtHostLanguageModels } from './extHostLanguageModels.js';
3535
import { ExtHostLanguageModelTools } from './extHostLanguageModelTools.js';
3636
import * as typeConvert from './extHostTypeConverters.js';
3737
import * as extHostTypes from './extHostTypes.js';
38-
import { ICustomAgentQueryOptions, IExternalCustomAgent } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
38+
import { IPromptFileContext, IPromptFileResource } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
39+
import { PromptsType } from '../../contrib/chat/common/promptSyntax/promptTypes.js';
3940
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js';
4041

4142
export class ChatAgentResponseStream {
@@ -398,8 +399,8 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
398399
private static _relatedFilesProviderIdPool = 0;
399400
private readonly _relatedFilesProviders = new Map<number, ExtHostRelatedFilesProvider>();
400401

401-
private static _customAgentsProviderIdPool = 0;
402-
private readonly _customAgentsProviders = new Map<number, { extension: IExtensionDescription; provider: vscode.CustomAgentsProvider }>();
402+
private static _contributionsProviderIdPool = 0;
403+
private readonly _promptFileProviders = new Map<number, { extension: IExtensionDescription; provider: vscode.CustomAgentProvider | vscode.InstructionsProvider | vscode.PromptFileProvider }>();
403404

404405
private readonly _sessionDisposables: DisposableResourceMap<DisposableStore> = this._register(new DisposableResourceMap());
405406
private readonly _completionDisposables: DisposableMap<number, DisposableStore> = this._register(new DisposableMap());
@@ -479,23 +480,41 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
479480
});
480481
}
481482

482-
registerCustomAgentsProvider(extension: IExtensionDescription, provider: vscode.CustomAgentsProvider): vscode.Disposable {
483-
const handle = ExtHostChatAgents2._customAgentsProviderIdPool++;
484-
this._customAgentsProviders.set(handle, { extension, provider });
485-
this._proxy.$registerCustomAgentsProvider(handle, extension.identifier);
483+
/**
484+
* Internal method that handles all prompt file provider types.
485+
* Routes custom agents, instructions, and prompt files to the unified internal implementation.
486+
*/
487+
registerPromptFileProvider(extension: IExtensionDescription, type: PromptsType, provider: vscode.CustomAgentProvider | vscode.InstructionsProvider | vscode.PromptFileProvider): vscode.Disposable {
488+
const handle = ExtHostChatAgents2._contributionsProviderIdPool++;
489+
this._promptFileProviders.set(handle, { extension, provider });
490+
this._proxy.$registerPromptFileProvider(handle, type, extension.identifier);
486491

487492
const disposables = new DisposableStore();
488493

489494
// Listen to provider change events and notify main thread
490-
if (provider.onDidChangeCustomAgents) {
491-
disposables.add(provider.onDidChangeCustomAgents(() => {
492-
this._proxy.$onDidChangeCustomAgents(handle);
495+
// Check for the appropriate event based on the provider type
496+
let changeEvent: vscode.Event<void> | undefined;
497+
switch (type) {
498+
case PromptsType.agent:
499+
changeEvent = (provider as vscode.CustomAgentProvider).onDidChangeCustomAgents;
500+
break;
501+
case PromptsType.instructions:
502+
changeEvent = (provider as vscode.InstructionsProvider).onDidChangeInstructions;
503+
break;
504+
case PromptsType.prompt:
505+
changeEvent = (provider as vscode.PromptFileProvider).onDidChangePromptFiles;
506+
break;
507+
}
508+
509+
if (changeEvent) {
510+
disposables.add(changeEvent(() => {
511+
this._proxy.$onDidChangePromptFiles(handle);
493512
}));
494513
}
495514

496515
disposables.add(toDisposable(() => {
497-
this._customAgentsProviders.delete(handle);
498-
this._proxy.$unregisterCustomAgentsProvider(handle);
516+
this._promptFileProviders.delete(handle);
517+
this._proxy.$unregisterPromptFileProvider(handle);
499518
}));
500519

501520
return disposables;
@@ -511,13 +530,21 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
511530
return await provider.provider.provideRelatedFiles(extRequestDraft, token) ?? undefined;
512531
}
513532

514-
async $provideCustomAgents(handle: number, options: ICustomAgentQueryOptions, token: CancellationToken): Promise<IExternalCustomAgent[] | undefined> {
515-
const providerData = this._customAgentsProviders.get(handle);
533+
async $providePromptFiles(handle: number, type: PromptsType, context: IPromptFileContext, token: CancellationToken): Promise<IPromptFileResource[] | undefined> {
534+
const providerData = this._promptFileProviders.get(handle);
516535
if (!providerData) {
517-
return Promise.resolve(undefined);
536+
return undefined;
518537
}
519538

520-
return await providerData.provider.provideCustomAgents(options, token) ?? undefined;
539+
const provider = providerData.provider;
540+
switch (type) {
541+
case PromptsType.agent:
542+
return await (provider as vscode.CustomAgentProvider).provideCustomAgents(context, token) ?? undefined;
543+
case PromptsType.instructions:
544+
return await (provider as vscode.InstructionsProvider).provideInstructions(context, token) ?? undefined;
545+
case PromptsType.prompt:
546+
return await (provider as vscode.PromptFileProvider).providePromptFiles(context, token) ?? undefined;
547+
}
521548
}
522549

523550
async $detectChatParticipant(handle: number, requestDto: Dto<IChatAgentRequest>, context: { history: IChatAgentHistoryEntryDto[] }, options: { location: ChatAgentLocation; participants?: vscode.ChatParticipantMetadata[] }, token: CancellationToken): Promise<vscode.ChatParticipantDetectionResult | null | undefined> {

src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,21 @@ import { IHandOff, ParsedPromptFile } from '../promptFileParser.js';
1616
import { ResourceSet } from '../../../../../../base/common/map.js';
1717

1818
/**
19-
* Activation event for custom agent providers.
19+
* Activation events for prompt file providers.
2020
*/
21-
export const CUSTOM_AGENTS_PROVIDER_ACTIVATION_EVENT = 'onCustomAgentsProvider';
21+
export const CUSTOM_AGENT_PROVIDER_ACTIVATION_EVENT = 'onCustomAgentProvider';
22+
export const INSTRUCTIONS_PROVIDER_ACTIVATION_EVENT = 'onInstructionsProvider';
23+
export const PROMPT_FILE_PROVIDER_ACTIVATION_EVENT = 'onPromptFileProvider';
2224

2325
/**
24-
* Options for querying custom agents.
26+
* Context for querying prompt files.
2527
*/
26-
export interface ICustomAgentQueryOptions { }
28+
export interface IPromptFileContext { }
2729

2830
/**
29-
* Represents a custom agent resource from an external provider.
31+
* Represents a prompt file resource from an external provider.
3032
*/
31-
export interface IExternalCustomAgent {
32-
/**
33-
* The unique identifier/name of the custom agent resource.
34-
*/
35-
readonly name: string;
36-
37-
/**
38-
* A description of what the custom agent resource does.
39-
*/
40-
readonly description: string;
41-
33+
export interface IPromptFileResource {
4234
/**
4335
* The URI to the agent or prompt resource file.
4436
*/
@@ -316,15 +308,15 @@ export interface IPromptsService extends IDisposable {
316308
setDisabledPromptFiles(type: PromptsType, uris: ResourceSet): void;
317309

318310
/**
319-
* Registers a CustomAgentsProvider that can provide custom agents for repositories.
320-
* This is part of the proposed API and requires the chatParticipantPrivate proposal.
311+
* Registers a prompt file provider that can provide prompt files for repositories.
321312
* @param extension The extension registering the provider.
313+
* @param type The type of contribution.
322314
* @param provider The provider implementation with optional change event.
323315
* @returns A disposable that unregisters the provider when disposed.
324316
*/
325-
registerCustomAgentsProvider(extension: IExtensionDescription, provider: {
326-
onDidChangeCustomAgents?: Event<void>;
327-
provideCustomAgents: (options: ICustomAgentQueryOptions, token: CancellationToken) => Promise<IExternalCustomAgent[] | undefined>;
317+
registerPromptFileProvider(extension: IExtensionDescription, type: PromptsType, provider: {
318+
onDidChangePromptFiles?: Event<void>;
319+
providePromptFiles: (context: IPromptFileContext, token: CancellationToken) => Promise<IPromptFileResource[] | undefined>;
328320
}): IDisposable;
329321

330322
/**

0 commit comments

Comments
 (0)