Path: blob/main/src/vs/workbench/contrib/chat/common/plugins/agentPluginService.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IDisposable } from '../../../../../base/common/lifecycle.js';6import { IObservable } from '../../../../../base/common/observable.js';7import { basename } from '../../../../../base/common/resources.js';8import { URI } from '../../../../../base/common/uri.js';9import { SyncDescriptor0 } from '../../../../../platform/instantiation/common/descriptors.js';10import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';11import { type INamedPluginResource, type IMcpServerDefinition, type IParsedHookCommand } from '../../../../../platform/agentPlugins/common/pluginParsers.js';12import { ContributionEnablementState, IEnablementModel } from '../enablement.js';13import { HookType } from '../promptSyntax/hookTypes.js';14import { IMarketplacePlugin } from './pluginMarketplaceService.js';1516export const IAgentPluginService = createDecorator<IAgentPluginService>('agentPluginService');1718export interface IAgentPluginHook {19readonly type: HookType;20readonly hooks: readonly IParsedHookCommand[];21/** URI where this hook is defined -- not unique, multiple hooks may be in a manifest */22readonly uri: URI;23readonly originalId: string;24}2526export type IAgentPluginCommand = INamedPluginResource;27export type IAgentPluginSkill = INamedPluginResource;28export type IAgentPluginAgent = INamedPluginResource;29export type IAgentPluginInstruction = INamedPluginResource;30export type IAgentPluginMcpServerDefinition = IMcpServerDefinition;3132export interface IAgentPlugin {33readonly uri: URI;34/** Human-readable display name for the plugin. */35readonly label: string;36readonly enablement: IObservable<ContributionEnablementState>;37/** Removes this plugin from its discovery source (config or installed storage). */38remove(): void;39readonly hooks: IObservable<readonly IAgentPluginHook[]>;40readonly commands: IObservable<readonly IAgentPluginCommand[]>;41readonly skills: IObservable<readonly IAgentPluginSkill[]>;42readonly agents: IObservable<readonly IAgentPluginAgent[]>;43readonly instructions: IObservable<readonly IAgentPluginInstruction[]>;44readonly mcpServerDefinitions: IObservable<readonly IAgentPluginMcpServerDefinition[]>;45/** Set when the plugin was installed from a marketplace repository. */46readonly fromMarketplace?: IMarketplacePlugin;47}4849export interface IAgentPluginService {50readonly _serviceBrand: undefined;51readonly plugins: IObservable<readonly IAgentPlugin[]>;52readonly enablementModel: IEnablementModel;53}5455export interface IAgentPluginDiscovery extends IDisposable {56readonly plugins: IObservable<readonly IAgentPlugin[]>;57start(enablementModel: IEnablementModel): void;58}5960export function getCanonicalPluginCommandId(plugin: { readonly uri: URI }, commandName: string): string {61const pluginSegment = basename(plugin.uri);62const prefix = normalizePluginToken(pluginSegment);63const normalizedCommand = normalizePluginToken(commandName);64if (normalizedCommand.startsWith(`${prefix}:`)) {65return normalizedCommand;66}6768// When the skill name matches the plugin name, use just the plugin69// name so the user can invoke `/plugin-name` instead of the redundant70// `/plugin-name:plugin-name`.71if (prefix === normalizedCommand) {72return prefix;73}7475return `${prefix}:${normalizedCommand}`;76}7778function normalizePluginToken(value: string): string {79return value80.trim()81.toLowerCase()82.replace(/\s+/g, '-')83.replace(/[^a-z0-9_.:-]/g, '-')84.replace(/-+/g, '-')85.replace(/^[-:.]+|[-:.]+$/g, '');86}8788class AgentPluginDiscoveryRegistry {89private readonly _discovery: SyncDescriptor0<IAgentPluginDiscovery>[] = [];9091register(descriptor: SyncDescriptor0<IAgentPluginDiscovery>): void {92this._discovery.push(descriptor);93}9495getAll(): readonly SyncDescriptor0<IAgentPluginDiscovery>[] {96return this._discovery;97}98}99100export const agentPluginDiscoveryRegistry = new AgentPluginDiscoveryRegistry();101102103104105