Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
57e647c
wip
pwang347 Dec 1, 2025
241e775
updates
pwang347 Dec 1, 2025
424a1db
Apply suggestion from @Copilot
pwang347 Dec 1, 2025
dc9551e
test
pwang347 Dec 1, 2025
696ef50
dispose
pwang347 Dec 1, 2025
2441746
clean
pwang347 Dec 1, 2025
112dc09
remove
pwang347 Dec 1, 2025
25cd745
invalidate
pwang347 Dec 1, 2025
1ca3933
Merge branch 'main' of github.com:microsoft/vscode into pawang/orgIns…
pwang347 Dec 31, 2025
1600cdf
wip
pwang347 Dec 31, 2025
88bc61c
pr
pwang347 Dec 31, 2025
d31d7ba
use enum
pwang347 Dec 31, 2025
4dd999d
Merge branch 'main' of github.com:microsoft/vscode into pawang/orgIns…
pwang347 Jan 1, 2026
6320214
Merge branch 'main' of github.com:microsoft/vscode into pawang/orgIns…
pwang347 Jan 7, 2026
3ce36e7
PR
pwang347 Jan 7, 2026
5a44074
clean
pwang347 Jan 7, 2026
846facc
more cleanup
pwang347 Jan 7, 2026
de3f04c
more cleanup
pwang347 Jan 7, 2026
bc3dc9e
more cleanup
pwang347 Jan 7, 2026
50f719a
more cleanup
pwang347 Jan 7, 2026
d334ac1
more cleanup
pwang347 Jan 7, 2026
057840b
nit
pwang347 Jan 7, 2026
d283548
add optional metadata
pwang347 Jan 7, 2026
36346df
use new proposal
pwang347 Jan 7, 2026
2617d68
clean
pwang347 Jan 7, 2026
44f5501
clean
pwang347 Jan 7, 2026
f4e9bc4
nit
pwang347 Jan 7, 2026
a5eec28
Update src/vs/workbench/api/common/extHostChatAgents2.ts
pwang347 Jan 8, 2026
c50fcb8
PR
pwang347 Jan 8, 2026
f07a636
Merge branch 'pawang/orgInstructions' of github.com:microsoft/vscode …
pwang347 Jan 8, 2026
1c07b6e
use type instead of 'in' checks
aeschli Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/vs/workbench/api/browser/mainThreadChatAgents2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIde
import { IChatWidgetService } from '../../contrib/chat/browser/chat.js';
import { AddDynamicVariableAction, IAddDynamicVariableContext } from '../../contrib/chat/browser/contrib/chatDynamicVariables.js';
import { IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/chatAgents.js';
import { ICustomAgentQueryOptions, IPromptsService } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { ICustomAgentQueryOptions, IPromptsService, IInstructionQueryOptions } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { IChatEditingService, IChatRelatedFileProviderMetadata } from '../../contrib/chat/common/chatEditingService.js';
import { IChatModel } from '../../contrib/chat/common/chatModel.js';
import { ChatRequestAgentPart } from '../../contrib/chat/common/chatParserTypes.js';
Expand Down Expand Up @@ -98,6 +98,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA

private readonly _customAgentsProviders = this._register(new DisposableMap<number, IDisposable>());
private readonly _customAgentsProviderEmitters = this._register(new DisposableMap<number, Emitter<void>>());
private readonly _instructionsProviders = this._register(new DisposableMap<number, IDisposable>());
private readonly _instructionsProviderEmitters = this._register(new DisposableMap<number, Emitter<void>>());

private readonly _pendingProgress = new Map<string, { progress: (parts: IChatProgress[]) => void; chatSession: IChatModel | undefined }>();
private readonly _proxy: ExtHostChatAgentsShape2;
Expand Down Expand Up @@ -468,6 +470,46 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
emitter.fire();
}
}

async $registerInstructionsProvider(handle: number, extensionId: ExtensionIdentifier): Promise<void> {
const extension = await this._extensionService.getExtension(extensionId.value);
if (!extension) {
this._logService.error(`[MainThreadChatAgents2] Could not find extension for InstructionsProvider: ${extensionId.value}`);
return;
}

const emitter = new Emitter<void>();
this._instructionsProviderEmitters.set(handle, emitter);

const disposable = this._promptsService.registerInstructionsProvider(extension, {
onDidChangeInstructions: emitter.event,
provideInstructions: async (options: IInstructionQueryOptions, token: CancellationToken) => {
const instructions = await this._proxy.$provideInstructions(handle, options, token);
if (!instructions) {
return undefined;
}
// Convert UriComponents to URI
return instructions.map(instruction => ({
...instruction,
uri: URI.revive(instruction.uri)
}));
}
});

this._instructionsProviders.set(handle, disposable);
}

$unregisterInstructionsProvider(handle: number): void {
this._instructionsProviders.deleteAndDispose(handle);
this._instructionsProviderEmitters.deleteAndDispose(handle);
}

$onDidChangeInstructions(handle: number): void {
const emitter = this._instructionsProviderEmitters.get(handle);
if (emitter) {
emitter.fire();
}
}
}


Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension, 'chatParticipantPrivate');
return extHostChatAgents2.registerCustomAgentsProvider(extension, provider);
},
registerInstructionsProvider(provider: vscode.InstructionsProvider): vscode.Disposable {
checkProposedApiEnabled(extension, 'chatParticipantPrivate');
return extHostChatAgents2.registerInstructionsProvider(extension, provider);
},
};

// namespace: lm
Expand Down
6 changes: 5 additions & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import { IChatRequestVariableValue } from '../../contrib/chat/common/chatVariabl
import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
import { IChatMessage, IChatResponsePart, ILanguageModelChatMetadataAndIdentifier, ILanguageModelChatSelector } from '../../contrib/chat/common/languageModels.js';
import { IPreparedToolInvocation, IToolInvocation, IToolInvocationPreparationContext, IToolProgressStep, IToolResult, ToolDataSource } from '../../contrib/chat/common/languageModelToolsService.js';
import { ICustomAgentQueryOptions, IExternalCustomAgent } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { ICustomAgentQueryOptions, IExternalCustomAgent, IInstructionQueryOptions, IExternalInstruction } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugTestRunReference, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from '../../contrib/debug/common/debug.js';
import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerLaunch } from '../../contrib/mcp/common/mcpTypes.js';
import * as notebookCommon from '../../contrib/notebook/common/notebookCommon.js';
Expand Down Expand Up @@ -1397,6 +1397,9 @@ export interface MainThreadChatAgentsShape2 extends IChatAgentProgressShape, IDi
$registerCustomAgentsProvider(handle: number, extension: ExtensionIdentifier): void;
$unregisterCustomAgentsProvider(handle: number): void;
$onDidChangeCustomAgents(handle: number): void;
$registerInstructionsProvider(handle: number, extension: ExtensionIdentifier): void;
$unregisterInstructionsProvider(handle: number): void;
$onDidChangeInstructions(handle: number): void;
$registerAgentCompletionsProvider(handle: number, id: string, triggerCharacters: string[]): void;
$unregisterAgentCompletionsProvider(handle: number, id: string): void;
$updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void;
Expand Down Expand Up @@ -1462,6 +1465,7 @@ export interface ExtHostChatAgentsShape2 {
$detectChatParticipant(handle: number, request: Dto<IChatAgentRequest>, context: { history: IChatAgentHistoryEntryDto[] }, options: { participants: IChatParticipantMetadata[]; location: ChatAgentLocation }, token: CancellationToken): Promise<IChatParticipantDetectionResult | null | undefined>;
$provideRelatedFiles(handle: number, request: Dto<IChatRequestDraft>, token: CancellationToken): Promise<Dto<IChatRelatedFile>[] | undefined>;
$provideCustomAgents(handle: number, options: ICustomAgentQueryOptions, token: CancellationToken): Promise<Dto<IExternalCustomAgent>[] | undefined>;
$provideInstructions(handle: number, options: IInstructionQueryOptions, token: CancellationToken): Promise<Dto<IExternalInstruction>[] | undefined>;
$setRequestTools(requestId: string, tools: UserSelectedTools): void;
}
export interface IChatParticipantMetadata {
Expand Down
35 changes: 34 additions & 1 deletion src/vs/workbench/api/common/extHostChatAgents2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { ExtHostLanguageModels } from './extHostLanguageModels.js';
import { ExtHostLanguageModelTools } from './extHostLanguageModelTools.js';
import * as typeConvert from './extHostTypeConverters.js';
import * as extHostTypes from './extHostTypes.js';
import { ICustomAgentQueryOptions, IExternalCustomAgent } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { ICustomAgentQueryOptions, IExternalCustomAgent, IInstructionQueryOptions, IExternalInstruction } from '../../contrib/chat/common/promptSyntax/service/promptsService.js';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js';

export class ChatAgentResponseStream {
Expand Down Expand Up @@ -400,6 +400,8 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS

private static _customAgentsProviderIdPool = 0;
private readonly _customAgentsProviders = new Map<number, { extension: IExtensionDescription; provider: vscode.CustomAgentsProvider }>();
private static _instructionsProviderIdPool = 0;
private readonly _instructionsProviders = new Map<number, { extension: IExtensionDescription; provider: vscode.InstructionsProvider }>();

private readonly _sessionDisposables: DisposableResourceMap<DisposableStore> = this._register(new DisposableResourceMap());
private readonly _completionDisposables: DisposableMap<number, DisposableStore> = this._register(new DisposableMap());
Expand Down Expand Up @@ -501,6 +503,28 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
return disposables;
}

registerInstructionsProvider(extension: IExtensionDescription, provider: vscode.InstructionsProvider): vscode.Disposable {
const handle = ExtHostChatAgents2._instructionsProviderIdPool++;
this._instructionsProviders.set(handle, { extension, provider });
this._proxy.$registerInstructionsProvider(handle, extension.identifier);

const disposables = new DisposableStore();

// Listen to provider change events and notify main thread
if (provider.onDidChangeInstructions) {
disposables.add(provider.onDidChangeInstructions(() => {
this._proxy.$onDidChangeInstructions(handle);
}));
}

disposables.add(toDisposable(() => {
this._instructionsProviders.delete(handle);
this._proxy.$unregisterInstructionsProvider(handle);
}));

return disposables;
}

async $provideRelatedFiles(handle: number, request: IChatRequestDraft, token: CancellationToken): Promise<Dto<IChatRelatedFile>[] | undefined> {
const provider = this._relatedFilesProviders.get(handle);
if (!provider) {
Expand All @@ -520,6 +544,15 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
return await providerData.provider.provideCustomAgents(options, token) ?? undefined;
}

async $provideInstructions(handle: number, options: IInstructionQueryOptions, token: CancellationToken): Promise<IExternalInstruction[] | undefined> {
const providerData = this._instructionsProviders.get(handle);
if (!providerData) {
return Promise.resolve(undefined);
}

return await providerData.provider.provideInstructions(options, token) ?? undefined;
}

async $detectChatParticipant(handle: number, requestDto: Dto<IChatAgentRequest>, context: { history: IChatAgentHistoryEntryDto[] }, options: { location: ChatAgentLocation; participants?: vscode.ChatParticipantMetadata[] }, token: CancellationToken): Promise<vscode.ChatParticipantDetectionResult | null | undefined> {
const detector = this._participantDetectionProviders.get(handle);
if (!detector) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,21 @@ import { ResourceSet } from '../../../../../../base/common/map.js';
*/
export const CUSTOM_AGENTS_PROVIDER_ACTIVATION_EVENT = 'onCustomAgentsProvider';

/**
* Activation event for instructions providers.
*/
export const INSTRUCTIONS_PROVIDER_ACTIVATION_EVENT = 'onInstructionsProvider';

/**
* Options for querying custom agents.
*/
export interface ICustomAgentQueryOptions { }

/**
* Options for querying instructions.
*/
export interface IInstructionQueryOptions { }

/**
* Represents a custom agent resource from an external provider.
*/
Expand All @@ -50,6 +60,31 @@ export interface IExternalCustomAgent {
readonly isEditable?: boolean;
}

/**
* Represents an instruction resource from an external provider.
*/
export interface IExternalInstruction {
/**
* The unique identifier/name of the instruction resource.
*/
readonly name: string;

/**
* A description of what the instruction resource does.
*/
readonly description: string;

/**
* The URI to the instruction resource file.
*/
readonly uri: URI;

/**
* Indicates whether the instruction resource is editable. Defaults to false.
*/
readonly isEditable?: boolean;
}

/**
* Provides prompt services.
*/
Expand Down Expand Up @@ -327,6 +362,18 @@ export interface IPromptsService extends IDisposable {
provideCustomAgents: (options: ICustomAgentQueryOptions, token: CancellationToken) => Promise<IExternalCustomAgent[] | undefined>;
}): IDisposable;

/**
* Registers an InstructionsProvider that can provide instructions for repositories.
* This is part of the proposed API and requires the chatParticipantPrivate proposal.
* @param extension The extension registering the provider.
* @param provider The provider implementation with optional change event.
* @returns A disposable that unregisters the provider when disposed.
*/
registerInstructionsProvider(extension: IExtensionDescription, provider: {
onDidChangeInstructions?: Event<void>;
provideInstructions: (options: IInstructionQueryOptions, token: CancellationToken) => Promise<IExternalInstruction[] | undefined>;
}): IDisposable;

/**
* Gets list of claude skills files.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { getCleanPromptName } from '../config/promptFileLocations.js';
import { PROMPT_LANGUAGE_ID, PromptsType, getPromptsTypeForLanguageId } from '../promptTypes.js';
import { PromptFilesLocator } from '../utils/promptFilesLocator.js';
import { PromptFileParser, ParsedPromptFile, PromptHeaderAttributes } from '../promptFileParser.js';
import { IAgentInstructions, IAgentSource, IChatPromptSlashCommand, ICustomAgent, IExtensionPromptPath, ILocalPromptPath, IPromptPath, IPromptsService, IClaudeSkill, IUserPromptPath, PromptsStorage, ICustomAgentQueryOptions, IExternalCustomAgent, ExtensionAgentSourceType, CUSTOM_AGENTS_PROVIDER_ACTIVATION_EVENT } from './promptsService.js';
import { IAgentInstructions, IAgentSource, IChatPromptSlashCommand, ICustomAgent, IExtensionPromptPath, ILocalPromptPath, IPromptPath, IPromptsService, IClaudeSkill, IUserPromptPath, PromptsStorage, ICustomAgentQueryOptions, IExternalCustomAgent, ExtensionAgentSourceType, CUSTOM_AGENTS_PROVIDER_ACTIVATION_EVENT, IInstructionQueryOptions, IExternalInstruction, INSTRUCTIONS_PROVIDER_ACTIVATION_EVENT } from './promptsService.js';
import { Delayer } from '../../../../../../base/common/async.js';
import { Schemas } from '../../../../../../base/common/network.js';

Expand Down Expand Up @@ -169,6 +169,15 @@ export class PromptsService extends Disposable implements IPromptsService {
provideCustomAgents: (options: ICustomAgentQueryOptions, token: CancellationToken) => Promise<IExternalCustomAgent[] | undefined>;
}> = [];

/**
* Registry of InstructionsProvider instances. Extensions can register providers via the proposed API.
*/
private readonly instructionsProviders: Array<{
extension: IExtensionDescription;
onDidChangeInstructions?: Event<void>;
provideInstructions: (options: IInstructionQueryOptions, token: CancellationToken) => Promise<IExternalInstruction[] | undefined>;
}> = [];

/**
* Registers a CustomAgentsProvider. This will be called by the extension host bridge when
* an extension registers a provider via vscode.chat.registerCustomAgentsProvider().
Expand Down Expand Up @@ -208,6 +217,39 @@ export class PromptsService extends Disposable implements IPromptsService {
return disposables;
}

/**
* Registers an InstructionsProvider. This will be called by the extension host bridge when
* an extension registers a provider via vscode.chat.registerInstructionsProvider().
*/
public registerInstructionsProvider(extension: IExtensionDescription, provider: {
onDidChangeInstructions?: Event<void>;
provideInstructions: (options: IInstructionQueryOptions, token: CancellationToken) => Promise<IExternalInstruction[] | undefined>;
}): IDisposable {
const providerEntry = { extension, ...provider };
this.instructionsProviders.push(providerEntry);

const disposables = new DisposableStore();

// Listen to provider change events
if (provider.onDidChangeInstructions) {
disposables.add(provider.onDidChangeInstructions(() => {
// Invalidate instruction cache when providers change
// Instructions might be used for various purposes, trigger refresh
}));
}

disposables.add({
dispose: () => {
const index = this.instructionsProviders.findIndex((p) => p === providerEntry);
if (index >= 0) {
this.instructionsProviders.splice(index, 1);
}
}
});

return disposables;
}

private async listCustomAgentsFromProvider(token: CancellationToken): Promise<IPromptPath[]> {
const result: IPromptPath[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ITextModel } from '../../../../../editor/common/model.js';
import { IExtensionDescription } from '../../../../../platform/extensions/common/extensions.js';
import { PromptsType } from '../../common/promptSyntax/promptTypes.js';
import { ParsedPromptFile } from '../../common/promptSyntax/promptFileParser.js';
import { IClaudeSkill, ICustomAgent, ICustomAgentQueryOptions, IExternalCustomAgent, IPromptPath, IPromptsService, PromptsStorage } from '../../common/promptSyntax/service/promptsService.js';
import { IClaudeSkill, ICustomAgent, ICustomAgentQueryOptions, IExternalCustomAgent, IPromptPath, IPromptsService, PromptsStorage, IInstructionQueryOptions, IExternalInstruction } from '../../common/promptSyntax/service/promptsService.js';
import { ResourceSet } from '../../../../../base/common/map.js';

export class MockPromptsService implements IPromptsService {
Expand Down Expand Up @@ -61,6 +61,7 @@ export class MockPromptsService implements IPromptsService {
getDisabledPromptFiles(type: PromptsType): ResourceSet { throw new Error('Method not implemented.'); }
setDisabledPromptFiles(type: PromptsType, uris: ResourceSet): void { throw new Error('Method not implemented.'); }
registerCustomAgentsProvider(extension: IExtensionDescription, provider: { provideCustomAgents: (options: ICustomAgentQueryOptions, token: CancellationToken) => Promise<IExternalCustomAgent[] | undefined> }): IDisposable { throw new Error('Method not implemented.'); }
registerInstructionsProvider(extension: IExtensionDescription, provider: { provideInstructions: (options: IInstructionQueryOptions, token: CancellationToken) => Promise<IExternalInstruction[] | undefined> }): IDisposable { throw new Error('Method not implemented.'); }
findClaudeSkills(token: CancellationToken): Promise<IClaudeSkill[] | undefined> { throw new Error('Method not implemented.'); }
dispose(): void { }
}
34 changes: 34 additions & 0 deletions src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,47 @@ declare module 'vscode' {
provideCustomAgents(options: CustomAgentQueryOptions, token: CancellationToken): ProviderResult<CustomAgentResource[]>;
}

// #endregion

// #region InstructionsProvider

/**
* Options for querying instructions.
*/
export interface InstructionQueryOptions { }

/**
* A provider that supplies instruction resources for repositories.
*/
export interface InstructionsProvider {
/**
* An optional event to signal that instructions have changed.
*/
readonly onDidChangeInstructions?: Event<void>;

/**
* Provide the list of instruction resources available for a given repository.
* @param options Optional query parameters.
* @param token A cancellation token.
* @returns An array of instruction resources or a promise that resolves to such.
*/
provideInstructions(options: InstructionQueryOptions, token: CancellationToken): ProviderResult<CustomAgentResource[]>;
}

export namespace chat {
/**
* Register a provider for custom agents.
* @param provider The custom agents provider.
* @returns A disposable that unregisters the provider when disposed.
*/
export function registerCustomAgentsProvider(provider: CustomAgentsProvider): Disposable;

/**
* Register a provider for instructions.
* @param provider The instructions provider.
* @returns A disposable that unregisters the provider when disposed.
*/
export function registerInstructionsProvider(provider: InstructionsProvider): Disposable;
}

// #endregion
Expand Down
Loading