diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index b5006737fb6..e3f20d96726 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -600,7 +600,6 @@ function getDomSanitizerConfig(mdStrConfig: MdStrConfig, options: MarkdownSaniti Schemas.vscodeRemote, Schemas.vscodeRemoteResource, Schemas.vscodeNotebookCell, - Schemas.vscodeChatPrompt, // For links that are handled entirely by the action handler Schemas.internal, ]; diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 00859ca7344..74cb106fd3c 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -90,9 +90,6 @@ export namespace Schemas { /** Scheme used for local chat session content */ export const vscodeLocalChatSession = 'vscode-chat-session'; - /** Scheme used for virtual chat prompt files with embedded content */ - export const vscodeChatPrompt = 'vscode-chat-prompt'; - /** * Scheme used internally for webviews that aren't linked to a resource (i.e. not custom editors) */ diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index f0fd0935c21..e0416a2d09b 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -28,7 +28,6 @@ import { AddDynamicVariableAction, IAddDynamicVariableContext } from '../../cont import { IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/participants/chatAgents.js'; import { IPromptFileContext, IPromptsService } from '../../contrib/chat/common/promptSyntax/service/promptsService.js'; import { isValidPromptType } from '../../contrib/chat/common/promptSyntax/promptTypes.js'; -import { IChatPromptContentStore } from '../../contrib/chat/common/promptSyntax/chatPromptContentStore.js'; import { IChatEditingService, IChatRelatedFileProviderMetadata } from '../../contrib/chat/common/editing/chatEditingService.js'; import { IChatModel } from '../../contrib/chat/common/model/chatModel.js'; import { ChatRequestAgentPart } from '../../contrib/chat/common/requestParser/chatParserTypes.js'; @@ -123,7 +122,6 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA @IExtensionService private readonly _extensionService: IExtensionService, @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, @IPromptsService private readonly _promptsService: IPromptsService, - @IChatPromptContentStore private readonly _chatPromptContentStore: IChatPromptContentStore, @ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService, ) { super(); @@ -487,17 +485,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA } // Convert UriComponents to URI and register any inline content return contributions.map(c => { - const uri = URI.revive(c.uri); - // If this is a virtual prompt with inline content, register it with the store - if (c.content && uri.scheme === Schemas.vscodeChatPrompt) { - const uriKey = uri.toString(); - // Dispose any previous registration for this URI before registering new content - contentRegistrations.deleteAndDispose(uriKey); - contentRegistrations.set(uriKey, this._chatPromptContentStore.registerContent(uri, c.content)); - } return { - uri, - isEditable: c.isEditable + uri: URI.revive(c.uri), }; }); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 96c3e1380c0..c09e72566e7 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1546,19 +1546,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'chatContextProvider'); return extHostChatContext.registerChatContextProvider(selector ? checkSelector(selector) : undefined, `${extension.id}-${id}`, provider); }, - registerCustomAgentProvider(provider: vscode.CustomAgentProvider): vscode.Disposable { + registerCustomAgentProvider(provider: vscode.ChatCustomAgentProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'chatPromptFiles'); return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.agent, provider); }, - registerInstructionsProvider(provider: vscode.InstructionsProvider): vscode.Disposable { + registerInstructionsProvider(provider: vscode.ChatInstructionsProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'chatPromptFiles'); return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.instructions, provider); }, - registerPromptFileProvider(provider: vscode.PromptFileProvider): vscode.Disposable { + registerPromptFileProvider(provider: vscode.ChatPromptFileProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'chatPromptFiles'); return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.prompt, provider); }, - registerSkillProvider(provider: vscode.SkillProvider): vscode.Disposable { + registerSkillProvider(provider: vscode.ChatSkillProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'chatPromptFiles'); return extHostChatAgents2.registerPromptFileProvider(extension, PromptsType.skill, provider); }, @@ -1973,10 +1973,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I McpStdioServerDefinition2: extHostTypes.McpStdioServerDefinition, McpToolAvailability: extHostTypes.McpToolAvailability, SettingsSearchResultKind: extHostTypes.SettingsSearchResultKind, - CustomAgentChatResource: extHostTypes.CustomAgentChatResource, - InstructionsChatResource: extHostTypes.InstructionsChatResource, - PromptFileChatResource: extHostTypes.PromptFileChatResource, - SkillChatResource: extHostTypes.SkillChatResource, }; }; } diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index bc91aeec37d..5d246285967 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -38,7 +38,6 @@ import * as extHostTypes from './extHostTypes.js'; import { IPromptFileContext, IPromptFileResource } from '../../contrib/chat/common/promptSyntax/service/promptsService.js'; import { PromptsType } from '../../contrib/chat/common/promptSyntax/promptTypes.js'; import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js'; -import { Schemas } from '../../../base/common/network.js'; export class ChatAgentResponseStream { @@ -426,7 +425,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private readonly _relatedFilesProviders = new Map(); private static _contributionsProviderIdPool = 0; - private readonly _promptFileProviders = new Map(); + private readonly _promptFileProviders = new Map(); private readonly _sessionDisposables: DisposableResourceMap = this._register(new DisposableResourceMap()); private readonly _completionDisposables: DisposableMap = this._register(new DisposableMap()); @@ -510,7 +509,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS * Internal method that handles all prompt file provider types. * Routes custom agents, instructions, prompt files, and skills to the unified internal implementation. */ - registerPromptFileProvider(extension: IExtensionDescription, type: PromptsType, provider: vscode.CustomAgentProvider | vscode.InstructionsProvider | vscode.PromptFileProvider | vscode.SkillProvider): vscode.Disposable { + registerPromptFileProvider(extension: IExtensionDescription, type: PromptsType, provider: vscode.ChatCustomAgentProvider | vscode.ChatInstructionsProvider | vscode.ChatPromptFileProvider | vscode.ChatSkillProvider): vscode.Disposable { const handle = ExtHostChatAgents2._contributionsProviderIdPool++; this._promptFileProviders.set(handle, { extension, provider }); this._proxy.$registerPromptFileProvider(handle, type, extension.identifier); @@ -522,16 +521,16 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS let changeEvent: vscode.Event | undefined; switch (type) { case PromptsType.agent: - changeEvent = (provider as vscode.CustomAgentProvider).onDidChangeCustomAgents; + changeEvent = (provider as vscode.ChatCustomAgentProvider).onDidChangeCustomAgents; break; case PromptsType.instructions: - changeEvent = (provider as vscode.InstructionsProvider).onDidChangeInstructions; + changeEvent = (provider as vscode.ChatInstructionsProvider).onDidChangeInstructions; break; case PromptsType.prompt: - changeEvent = (provider as vscode.PromptFileProvider).onDidChangePromptFiles; + changeEvent = (provider as vscode.ChatPromptFileProvider).onDidChangePromptFiles; break; case PromptsType.skill: - changeEvent = (provider as vscode.SkillProvider).onDidChangeSkills; + changeEvent = (provider as vscode.ChatSkillProvider).onDidChangeSkills; break; } @@ -566,77 +565,23 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS } const provider = providerData.provider; - let resources: vscode.CustomAgentChatResource[] | vscode.InstructionsChatResource[] | vscode.PromptFileChatResource[] | vscode.SkillChatResource[] | undefined; + let resources: vscode.ChatResource[] | undefined; switch (type) { case PromptsType.agent: - resources = await (provider as vscode.CustomAgentProvider).provideCustomAgents(context, token) ?? undefined; + resources = await (provider as vscode.ChatCustomAgentProvider).provideCustomAgents(context, token) ?? undefined; break; case PromptsType.instructions: - resources = await (provider as vscode.InstructionsProvider).provideInstructions(context, token) ?? undefined; + resources = await (provider as vscode.ChatInstructionsProvider).provideInstructions(context, token) ?? undefined; break; case PromptsType.prompt: - resources = await (provider as vscode.PromptFileProvider).providePromptFiles(context, token) ?? undefined; + resources = await (provider as vscode.ChatPromptFileProvider).providePromptFiles(context, token) ?? undefined; break; case PromptsType.skill: - resources = await (provider as vscode.SkillProvider).provideSkills(context, token) ?? undefined; + resources = await (provider as vscode.ChatSkillProvider).provideSkills(context, token) ?? undefined; break; } - // Convert ChatResourceDescriptor to IPromptFileResource format - return resources?.map(r => this.convertChatResourceDescriptorToPromptFileResource(r.resource, providerData.extension.identifier.value, type)); - } - - /** - * Creates a virtual URI for a prompt file. - * Format varies by type: - * - Skills: /${extensionId}/skills/${id}/SKILL.md - * - Agents: /${extensionId}/agents/${id}.agent.md - * - Instructions: /${extensionId}/instructions/${id}.instructions.md - * - Prompts: /${extensionId}/prompts/${id}.prompt.md - */ - createVirtualPromptUri(id: string, extensionId: string, type: PromptsType): URI { - let path: string; - switch (type) { - case PromptsType.skill: - path = `/${extensionId}/skills/${id}/SKILL.md`; - break; - case PromptsType.agent: - path = `/${extensionId}/agents/${id}.agent.md`; - break; - case PromptsType.instructions: - path = `/${extensionId}/instructions/${id}.instructions.md`; - break; - case PromptsType.prompt: - path = `/${extensionId}/prompts/${id}.prompt.md`; - break; - default: - throw new Error(`Unsupported PromptsType: ${type}`); - } - return URI.from({ - scheme: Schemas.vscodeChatPrompt, - path - }); - } - - convertChatResourceDescriptorToPromptFileResource(resource: vscode.ChatResourceDescriptor, extensionId: string, type: PromptsType): IPromptFileResource { - if (URI.isUri(resource)) { - // Plain URI - return { uri: resource }; - } else if ('id' in resource && 'content' in resource) { - // { id, content } - return { - content: resource.content, - uri: this.createVirtualPromptUri(resource.id, extensionId, type), - isEditable: undefined - }; - } else if ('uri' in resource && URI.isUri(resource.uri)) { - // { uri, isEditable? } - return { - uri: URI.revive(resource.uri), - isEditable: resource.isEditable - }; - } - throw new Error(`Invalid ChatResourceDescriptor: ${JSON.stringify(resource)}`); + return resources; } async $detectChatParticipant(handle: number, requestDto: Dto, context: { history: IChatAgentHistoryEntryDto[] }, options: { location: ChatAgentLocation; participants?: vscode.ChatParticipantMetadata[] }, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index b4f2638fc95..c142462a992 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3895,24 +3895,4 @@ export class McpHttpServerDefinition implements vscode.McpHttpServerDefinition { //#endregion //#region Chat Prompt Files - -@es5ClassCompat -export class CustomAgentChatResource implements vscode.CustomAgentChatResource { - constructor(public readonly resource: vscode.ChatResourceDescriptor) { } -} - -@es5ClassCompat -export class InstructionsChatResource implements vscode.InstructionsChatResource { - constructor(public readonly resource: vscode.ChatResourceDescriptor) { } -} - -@es5ClassCompat -export class PromptFileChatResource implements vscode.PromptFileChatResource { - constructor(public readonly resource: vscode.ChatResourceDescriptor) { } -} - -@es5ClassCompat -export class SkillChatResource implements vscode.SkillChatResource { - constructor(public readonly resource: vscode.ChatResourceDescriptor) { } -} //#endregion diff --git a/src/vs/workbench/api/test/browser/extHostTypes.test.ts b/src/vs/workbench/api/test/browser/extHostTypes.test.ts index ef844667ecc..8ee4767df55 100644 --- a/src/vs/workbench/api/test/browser/extHostTypes.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTypes.test.ts @@ -788,118 +788,4 @@ suite('ExtHostTypes', function () { m.content = 'Hello'; assert.deepStrictEqual(m.content, [new types.LanguageModelTextPart('Hello')]); }); - - test('CustomAgentChatResource - URI constructor', function () { - const uri = URI.file('/path/to/agent.md'); - const resource = new types.CustomAgentChatResource(uri); - - assert.ok(URI.isUri(resource.resource)); - assert.strictEqual(resource.resource.toString(), uri.toString()); - }); - - test('CustomAgentChatResource - URI constructor with options', function () { - const uri = URI.file('/path/to/agent.md'); - const resource = new types.CustomAgentChatResource({ uri, isEditable: true }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { uri: URI; isEditable?: boolean }; - assert.strictEqual(descriptor.uri.toString(), uri.toString()); - assert.strictEqual(descriptor.isEditable, true); - }); - - test('CustomAgentChatResource - content constructor', function () { - const content = '# My Agent\nThis is agent content'; - const resource = new types.CustomAgentChatResource({ id: 'my-agent-id', content }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { id: string; content: string }; - assert.strictEqual(descriptor.id, 'my-agent-id'); - assert.strictEqual(descriptor.content, content); - }); - - - - test('InstructionsChatResource - URI constructor', function () { - const uri = URI.file('/path/to/instructions.md'); - const resource = new types.InstructionsChatResource(uri); - - assert.ok(URI.isUri(resource.resource)); - assert.strictEqual(resource.resource.toString(), uri.toString()); - }); - - test('InstructionsChatResource - URI constructor with options', function () { - const uri = URI.file('/path/to/instructions.md'); - const resource = new types.InstructionsChatResource({ uri, isEditable: true }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { uri: URI; isEditable?: boolean }; - assert.strictEqual(descriptor.uri.toString(), uri.toString()); - assert.strictEqual(descriptor.isEditable, true); - }); - - test('InstructionsChatResource - content constructor', function () { - const content = '# Instructions\nFollow these steps'; - const resource = new types.InstructionsChatResource({ id: 'my-instructions-id', content }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { id: string; content: string }; - assert.strictEqual(descriptor.id, 'my-instructions-id'); - assert.strictEqual(descriptor.content, content); - }); - - - - test('PromptFileChatResource - URI constructor', function () { - const uri = URI.file('/path/to/prompt.md'); - const resource = new types.PromptFileChatResource(uri); - - assert.ok(URI.isUri(resource.resource)); - assert.strictEqual(resource.resource.toString(), uri.toString()); - }); - - test('PromptFileChatResource - URI constructor with options', function () { - const uri = URI.file('/path/to/prompt.md'); - const resource = new types.PromptFileChatResource({ uri, isEditable: true }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { uri: URI; isEditable?: boolean }; - assert.strictEqual(descriptor.uri.toString(), uri.toString()); - assert.strictEqual(descriptor.isEditable, true); - }); - - test('PromptFileChatResource - content constructor', function () { - const content = '# Prompt\nThis is my prompt content'; - const resource = new types.PromptFileChatResource({ id: 'my-prompt-id', content }); - - assert.ok(!URI.isUri(resource.resource)); - const descriptor = resource.resource as { id: string; content: string }; - assert.strictEqual(descriptor.id, 'my-prompt-id'); - assert.strictEqual(descriptor.content, content); - }); - - - - test('Chat prompt resources store different descriptors for different IDs', function () { - const resource1 = new types.CustomAgentChatResource({ id: 'id-one', content: 'content1' }); - const resource2 = new types.CustomAgentChatResource({ id: 'id-two', content: 'content2' }); - - const desc1 = resource1.resource as { id: string; content: string }; - const desc2 = resource2.resource as { id: string; content: string }; - assert.strictEqual(desc1.id, 'id-one'); - assert.strictEqual(desc2.id, 'id-two'); - assert.notStrictEqual(desc1.id, desc2.id); - }); - - test('Chat prompt resources store resource descriptors correctly', function () { - const agent = new types.CustomAgentChatResource({ id: 'test', content: 'content' }); - const instructions = new types.InstructionsChatResource({ id: 'test', content: 'content' }); - const prompt = new types.PromptFileChatResource({ id: 'test', content: 'content' }); - - assert.ok(!URI.isUri(agent.resource)); - assert.ok(!URI.isUri(instructions.resource)); - assert.ok(!URI.isUri(prompt.resource)); - assert.strictEqual((agent.resource as { id: string; content: string }).id, 'test'); - assert.strictEqual((instructions.resource as { id: string; content: string }).id, 'test'); - assert.strictEqual((prompt.resource as { id: string; content: string }).id, 'test'); - }); }); diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 4eb76e92ddd..264c0e1290b 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -53,7 +53,6 @@ import { ILanguageModelStatsService, LanguageModelStatsService } from '../common import { ILanguageModelToolsConfirmationService } from '../common/tools/languageModelToolsConfirmationService.js'; import { ILanguageModelToolsService } from '../common/tools/languageModelToolsService.js'; import { ChatPromptFilesExtensionPointHandler } from '../common/promptSyntax/chatPromptFilesContribution.js'; -import { ChatPromptContentStore, IChatPromptContentStore } from '../common/promptSyntax/chatPromptContentStore.js'; import { PromptsConfig } from '../common/promptSyntax/config/config.js'; import { INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, INSTRUCTION_FILE_EXTENSION, LEGACY_MODE_DEFAULT_SOURCE_FOLDER, LEGACY_MODE_FILE_EXTENSION, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION, DEFAULT_SKILL_SOURCE_FOLDERS, AGENTS_SOURCE_FOLDER, AGENT_FILE_EXTENSION, SKILL_FILENAME } from '../common/promptSyntax/config/promptFileLocations.js'; import { PromptLanguageFeaturesProvider } from '../common/promptSyntax/promptFileContributions.js'; @@ -95,8 +94,6 @@ import { ChatAttachmentResolveService, IChatAttachmentResolveService } from './a import { ChatMarkdownAnchorService, IChatMarkdownAnchorService } from './widget/chatContentParts/chatMarkdownAnchorService.js'; import { ChatContextPickService, IChatContextPickService } from './attachments/chatContextPickService.js'; import { ChatInputBoxContentProvider } from './widget/input/editor/chatEditorInputContentProvider.js'; -import { ChatPromptContentProvider } from './promptSyntax/chatPromptContentProvider.js'; -import './promptSyntax/chatPromptFileSystemProvider.js'; import { ChatEditingEditorAccessibility } from './chatEditing/chatEditingEditorAccessibility.js'; import { registerChatEditorActions } from './chatEditing/chatEditingEditorActions.js'; import { ChatEditingEditorContextKeys } from './chatEditing/chatEditingEditorContextKeys.js'; @@ -1186,7 +1183,6 @@ AccessibleViewRegistry.register(new EditsChatAccessibilityHelp()); AccessibleViewRegistry.register(new AgentChatAccessibilityHelp()); registerEditorFeature(ChatInputBoxContentProvider); -registerEditorFeature(ChatPromptContentProvider); class ChatSlashStaticSlashCommandsContribution extends Disposable { @@ -1348,7 +1344,6 @@ registerSingleton(IChatEditingService, ChatEditingService, InstantiationType.Del registerSingleton(IChatMarkdownAnchorService, ChatMarkdownAnchorService, InstantiationType.Delayed); registerSingleton(ILanguageModelIgnoredFilesService, LanguageModelIgnoredFilesService, InstantiationType.Delayed); registerSingleton(IPromptsService, PromptsService, InstantiationType.Delayed); -registerSingleton(IChatPromptContentStore, ChatPromptContentStore, InstantiationType.Delayed); registerSingleton(IChatContextPickService, ChatContextPickService, InstantiationType.Delayed); registerSingleton(IChatModeService, ChatModeService, InstantiationType.Delayed); registerSingleton(IChatAttachmentResolveService, ChatAttachmentResolveService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptContentProvider.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptContentProvider.ts deleted file mode 100644 index b193910212f..00000000000 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptContentProvider.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { Schemas } from '../../../../../base/common/network.js'; -import { URI } from '../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { IChatPromptContentStore } from '../../common/promptSyntax/chatPromptContentStore.js'; -import { PROMPT_LANGUAGE_ID } from '../../common/promptSyntax/promptTypes.js'; - -/** - * Content provider for virtual chat prompt files created with inline content. - * These URIs have the scheme 'vscode-chat-prompt' and retrieve their content - * from the {@link IChatPromptContentStore} which maintains an in-memory map - * of content indexed by URI. This approach avoids putting content in the URI - * query string which is a misuse of URIs. - */ -export class ChatPromptContentProvider extends Disposable implements ITextModelContentProvider { - constructor( - @ITextModelService textModelService: ITextModelService, - @IModelService private readonly modelService: IModelService, - @ILanguageService private readonly languageService: ILanguageService, - @IChatPromptContentStore private readonly chatPromptContentStore: IChatPromptContentStore - ) { - super(); - this._register(textModelService.registerTextModelContentProvider(Schemas.vscodeChatPrompt, this)); - } - - async provideTextContent(resource: URI): Promise { - const existing = this.modelService.getModel(resource); - if (existing) { - return existing; - } - - // Get the content from the content store - const content = this.chatPromptContentStore.getContent(resource) ?? ''; - - return this.modelService.createModel( - content, - this.languageService.createById(PROMPT_LANGUAGE_ID), - resource - ); - } -} diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptFileSystemProvider.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptFileSystemProvider.ts deleted file mode 100644 index daee1c26006..00000000000 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/chatPromptFileSystemProvider.ts +++ /dev/null @@ -1,119 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from '../../../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; -import { Schemas } from '../../../../../base/common/network.js'; -import { URI } from '../../../../../base/common/uri.js'; -import { VSBuffer } from '../../../../../base/common/buffer.js'; -import { IFileDeleteOptions, IFileOverwriteOptions, FileSystemProviderCapabilities, FileType, IFileWriteOptions, IFileSystemProvider, IFileSystemProviderWithFileReadWriteCapability, IStat, IWatchOptions, createFileSystemProviderError, FileSystemProviderErrorCode, IFileService } from '../../../../../platform/files/common/files.js'; -import { IChatPromptContentStore } from '../../common/promptSyntax/chatPromptContentStore.js'; -import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../../common/contributions.js'; - -/** - * File system provider for virtual chat prompt files created with inline content. - * These URIs have the scheme 'vscode-chat-prompt' and retrieve their content - * from the {@link IChatPromptContentStore} which maintains an in-memory map - * of content indexed by URI. - * - * This enables external extensions to use VS Code's file system API to read - * these virtual prompt files. - */ -export class ChatPromptFileSystemProvider implements IFileSystemProvider, IFileSystemProviderWithFileReadWriteCapability { - - get capabilities() { - return FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.Readonly; - } - - constructor( - private readonly chatPromptContentStore: IChatPromptContentStore - ) { } - - //#region Supported File Operations - - async stat(resource: URI): Promise { - const content = this.chatPromptContentStore.getContent(resource); - if (content === undefined) { - throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); - } - - const size = VSBuffer.fromString(content).byteLength; - - return { - type: FileType.File, - ctime: 0, - mtime: 0, - size - }; - } - - async readFile(resource: URI): Promise { - const content = this.chatPromptContentStore.getContent(resource); - if (content === undefined) { - throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); - } - - return VSBuffer.fromString(content).buffer; - } - - //#endregion - - //#region Unsupported File Operations - - readonly onDidChangeCapabilities = Event.None; - readonly onDidChangeFile = Event.None; - - async writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise { - throw createFileSystemProviderError('not allowed', FileSystemProviderErrorCode.NoPermissions); - } - - async mkdir(resource: URI): Promise { - throw createFileSystemProviderError('not allowed', FileSystemProviderErrorCode.NoPermissions); - } - - async readdir(resource: URI): Promise<[string, FileType][]> { - return []; - } - - async rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise { - throw createFileSystemProviderError('not allowed', FileSystemProviderErrorCode.NoPermissions); - } - - async delete(resource: URI, opts: IFileDeleteOptions): Promise { - throw createFileSystemProviderError('not allowed', FileSystemProviderErrorCode.NoPermissions); - } - - watch(resource: URI, opts: IWatchOptions): IDisposable { - return Disposable.None; - } - - //#endregion -} - -/** - * Workbench contribution that registers the chat prompt file system provider. - */ -export class ChatPromptFileSystemProviderContribution extends Disposable implements IWorkbenchContribution { - - static readonly ID = 'workbench.contrib.chatPromptFileSystemProvider'; - - constructor( - @IFileService fileService: IFileService, - @IChatPromptContentStore chatPromptContentStore: IChatPromptContentStore - ) { - super(); - - this._register(fileService.registerProvider( - Schemas.vscodeChatPrompt, - new ChatPromptFileSystemProvider(chatPromptContentStore) - )); - } -} - -registerWorkbenchContribution2( - ChatPromptFileSystemProviderContribution.ID, - ChatPromptFileSystemProviderContribution, - WorkbenchPhase.Eventually -); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/chatPromptContentStore.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/chatPromptContentStore.ts deleted file mode 100644 index a28402f8897..00000000000 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/chatPromptContentStore.ts +++ /dev/null @@ -1,74 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { URI } from '../../../../../base/common/uri.js'; -import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js'; - -export const IChatPromptContentStore = createDecorator('chatPromptContentStore'); - -/** - * Service for managing virtual chat prompt content. - * - * This store maintains an in-memory map of content indexed by URI. - * URIs use the vscode-chat-prompt scheme with just the ID in the path, - * avoiding the need to encode large content in the URI query string. - */ -export interface IChatPromptContentStore { - readonly _serviceBrand: undefined; - - /** - * Registers content for a given URI. - * @param uri The URI to associate with the content. - * @param content The content to store. - * @returns A disposable that removes the content when disposed. - */ - registerContent(uri: URI, content: string): { dispose: () => void }; - - /** - * Retrieves content by URI. - * @param uri The URI to look up. - * @returns The content if found, or undefined. - */ - getContent(uri: URI): string | undefined; -} - -export class ChatPromptContentStore extends Disposable implements IChatPromptContentStore { - readonly _serviceBrand: undefined; - - private readonly _contentMap = new Map(); - - constructor() { - super(); - } - - /** - * Normalizes a URI by stripping query and fragment for consistent lookup. - * Query parameters like vscodeLinkType are metadata for rendering, not content identification. - */ - private normalizeUri(uri: URI): string { - return uri.with({ query: '', fragment: '' }).toString(); - } - - registerContent(uri: URI, content: string): { dispose: () => void } { - const key = this.normalizeUri(uri); - this._contentMap.set(key, content); - - const dispose = () => { - this._contentMap.delete(key); - }; - - return { dispose }; - } - - getContent(uri: URI): string | undefined { - return this._contentMap.get(this.normalizeUri(uri)); - } - - override dispose(): void { - this._contentMap.clear(); - super.dispose(); - } -} diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 69735174644..604ea54f149 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -36,18 +36,6 @@ export interface IPromptFileResource { * The URI to the agent or prompt resource file. */ readonly uri: URI; - - /** - * Indicates whether the custom agent resource is editable. Defaults to false. - */ - readonly isEditable?: boolean; - - /** - * The inline content for virtual prompt files. This property is only used - * during IPC transfer from extension host to main thread - the content is - * immediately registered with the ChatPromptContentStore and not passed further. - */ - readonly content?: string; } /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts index d25cc1fd8b5..20f827a4d70 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts @@ -34,7 +34,6 @@ import { PromptFileParser, ParsedPromptFile, PromptHeaderAttributes } from '../p import { IAgentInstructions, IAgentSource, IChatPromptSlashCommand, ICustomAgent, IExtensionPromptPath, ILocalPromptPath, IPromptPath, IPromptsService, IAgentSkill, IUserPromptPath, PromptsStorage, ExtensionAgentSourceType, CUSTOM_AGENT_PROVIDER_ACTIVATION_EVENT, INSTRUCTIONS_PROVIDER_ACTIVATION_EVENT, IPromptFileContext, IPromptFileResource, PROMPT_FILE_PROVIDER_ACTIVATION_EVENT, SKILL_PROVIDER_ACTIVATION_EVENT } from './promptsService.js'; import { Delayer } from '../../../../../../base/common/async.js'; import { Schemas } from '../../../../../../base/common/network.js'; -import { IChatPromptContentStore } from '../chatPromptContentStore.js'; /** * Error thrown when a skill file is missing the required name attribute. @@ -129,7 +128,6 @@ export class PromptsService extends Disposable implements IPromptsService { @IStorageService private readonly storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IChatPromptContentStore private readonly chatPromptContentStore: IChatPromptContentStore ) { super(); @@ -298,15 +296,6 @@ export class PromptsService extends Disposable implements IPromptsService { } for (const file of files) { - if (!file.isEditable) { - try { - await this.filesConfigService.updateReadonly(file.uri, true); - } catch (e) { - const msg = e instanceof Error ? e.message : String(e); - this.logger.error(`[listFromProviders] Failed to make file readonly: ${file.uri}`, msg); - } - } - result.push({ uri: file.uri, storage: PromptsStorage.extension, @@ -543,15 +532,6 @@ export class PromptsService extends Disposable implements IPromptsService { return this.getParsedPromptFile(model); } - // Handle virtual prompt URIs - get content from the content store - if (uri.scheme === Schemas.vscodeChatPrompt) { - const content = this.chatPromptContentStore.getContent(uri); - if (content !== undefined) { - return new PromptFileParser().parse(uri, content); - } - throw new Error(`Content not found in store for virtual prompt URI: ${uri.toString()}`); - } - const fileContent = await this.fileService.readFile(uri); if (token.isCancellationRequested) { throw new CancellationError(); diff --git a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptContentProvider.test.ts b/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptContentProvider.test.ts deleted file mode 100644 index 6323d234eba..00000000000 --- a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptContentProvider.test.ts +++ /dev/null @@ -1,215 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import assert from 'assert'; -import { URI } from '../../../../../../base/common/uri.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { ILanguageService, ILanguageSelection } from '../../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; -import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { ChatPromptContentProvider } from '../../../browser/promptSyntax/chatPromptContentProvider.js'; -import { ChatPromptContentStore, IChatPromptContentStore } from '../../../common/promptSyntax/chatPromptContentStore.js'; -import { PROMPT_LANGUAGE_ID } from '../../../common/promptSyntax/promptTypes.js'; -import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; -import { Schemas } from '../../../../../../base/common/network.js'; - -suite('ChatPromptContentProvider', () => { - const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - - let instantiationService: TestInstantiationService; - let contentStore: ChatPromptContentStore; - let mockModelService: MockModelService; - let mockLanguageService: MockLanguageService; - let mockTextModelService: MockTextModelService; - let contentProvider: ChatPromptContentProvider; - - class MockLanguageSelection implements ILanguageSelection { - readonly languageId = PROMPT_LANGUAGE_ID; - readonly onDidChange = testDisposables.add(new (class extends Disposable { readonly event = () => ({ dispose: () => { } }); })()).event; - } - - class MockLanguageService { - createById(languageId: string): ILanguageSelection { - return new MockLanguageSelection(); - } - } - - class MockTextModel implements Partial { - constructor( - readonly uri: URI, - readonly content: string, - readonly languageId: string - ) { } - - getValue(): string { - return this.content; - } - - getLanguageId(): string { - return this.languageId; - } - } - - class MockModelService { - private models = new Map(); - - getModel(resource: URI): ITextModel | null { - return this.models.get(resource.toString()) ?? null; - } - - createModel(content: string, languageSelection: ILanguageSelection, resource: URI): ITextModel { - const model = new MockTextModel(resource, content, languageSelection.languageId) as unknown as ITextModel; - this.models.set(resource.toString(), model); - return model; - } - - setExistingModel(uri: URI, model: ITextModel): void { - this.models.set(uri.toString(), model); - } - - clear(): void { - this.models.clear(); - } - } - - class MockTextModelService { - private providers = new Map Promise }>(); - - registerTextModelContentProvider(scheme: string, provider: { provideTextContent: (resource: URI) => Promise }): IDisposable { - this.providers.set(scheme, provider); - return { dispose: () => this.providers.delete(scheme) }; - } - - getProvider(scheme: string) { - return this.providers.get(scheme); - } - } - - setup(() => { - instantiationService = testDisposables.add(new TestInstantiationService()); - - contentStore = testDisposables.add(new ChatPromptContentStore()); - mockModelService = new MockModelService(); - mockLanguageService = new MockLanguageService(); - mockTextModelService = new MockTextModelService(); - - instantiationService.stub(IChatPromptContentStore, contentStore); - instantiationService.stub(IModelService, mockModelService); - instantiationService.stub(ILanguageService, mockLanguageService as unknown as ILanguageService); - instantiationService.stub(ITextModelService, mockTextModelService as unknown as ITextModelService); - - contentProvider = testDisposables.add(instantiationService.createInstance(ChatPromptContentProvider)); - }); - - teardown(() => { - mockModelService.clear(); - }); - - test('registers as content provider for vscode-chat-prompt scheme', () => { - const provider = mockTextModelService.getProvider(Schemas.vscodeChatPrompt); - assert.ok(provider, 'Provider should be registered for vscode-chat-prompt scheme'); - }); - - test('provideTextContent creates model from stored content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/test-agent'); - const content = '# Test Agent\nThis is the agent content.'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const model = await contentProvider.provideTextContent(uri); - - assert.ok(model, 'Model should be created'); - assert.strictEqual((model as unknown as MockTextModel).getValue(), content); - assert.strictEqual((model as unknown as MockTextModel).getLanguageId(), PROMPT_LANGUAGE_ID); - }); - - test('provideTextContent returns existing model if available', async () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/existing'); - const existingContent = 'Existing model content'; - - const existingModel = new MockTextModel(uri, existingContent, PROMPT_LANGUAGE_ID) as unknown as ITextModel; - mockModelService.setExistingModel(uri, existingModel); - - const model = await contentProvider.provideTextContent(uri); - - assert.strictEqual(model, existingModel, 'Should return existing model'); - }); - - test('provideTextContent creates model with empty content when URI has no stored content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.instructions.md/missing'); - - const model = await contentProvider.provideTextContent(uri); - - assert.ok(model, 'Model should be created even without stored content'); - assert.strictEqual((model as unknown as MockTextModel).getValue(), ''); - }); - - test('provideTextContent uses prompt language ID', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/language-test'); - const content = 'Test content'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const model = await contentProvider.provideTextContent(uri); - - assert.ok(model); - assert.strictEqual((model as unknown as MockTextModel).getLanguageId(), PROMPT_LANGUAGE_ID); - }); - - test('handles multiple sequential requests for different URIs', async () => { - const uri1 = URI.parse('vscode-chat-prompt:/.agent.md/agent-1'); - const uri2 = URI.parse('vscode-chat-prompt:/.instructions.md/instructions-1'); - const uri3 = URI.parse('vscode-chat-prompt:/.prompt.md/prompt-1'); - - const content1 = 'Agent content'; - const content2 = 'Instructions content'; - const content3 = 'Prompt content'; - - testDisposables.add(contentStore.registerContent(uri1, content1)); - testDisposables.add(contentStore.registerContent(uri2, content2)); - testDisposables.add(contentStore.registerContent(uri3, content3)); - - const model1 = await contentProvider.provideTextContent(uri1); - const model2 = await contentProvider.provideTextContent(uri2); - const model3 = await contentProvider.provideTextContent(uri3); - - assert.strictEqual((model1 as unknown as MockTextModel).getValue(), content1); - assert.strictEqual((model2 as unknown as MockTextModel).getValue(), content2); - assert.strictEqual((model3 as unknown as MockTextModel).getValue(), content3); - }); - - test('content with special characters is handled correctly', async () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/special'); - const content = '# Unicode Test\n\n日本語テスト 🎉\n\n```typescript\nconst x = "hello";\n```'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const model = await contentProvider.provideTextContent(uri); - - assert.ok(model); - assert.strictEqual((model as unknown as MockTextModel).getValue(), content); - }); - - test('disposed content results in empty model', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/disposed-test'); - const content = 'Content that will be disposed'; - - const registration = contentStore.registerContent(uri, content); - - // Verify content exists - const model1 = await contentProvider.provideTextContent(uri); - assert.strictEqual((model1 as unknown as MockTextModel).getValue(), content); - - // Clear the model cache and dispose the content - mockModelService.clear(); - registration.dispose(); - - // Now requesting should return model with empty content - const model2 = await contentProvider.provideTextContent(uri); - assert.strictEqual((model2 as unknown as MockTextModel).getValue(), ''); - }); -}); diff --git a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptFileSystemProvider.test.ts b/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptFileSystemProvider.test.ts deleted file mode 100644 index f43e53d2cce..00000000000 --- a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/chatPromptFileSystemProvider.test.ts +++ /dev/null @@ -1,257 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import assert from 'assert'; -import { URI } from '../../../../../../base/common/uri.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { VSBuffer } from '../../../../../../base/common/buffer.js'; -import { FileSystemProviderErrorCode, toFileSystemProviderErrorCode } from '../../../../../../platform/files/common/files.js'; -import { ChatPromptFileSystemProvider } from '../../../browser/promptSyntax/chatPromptFileSystemProvider.js'; -import { ChatPromptContentStore } from '../../../common/promptSyntax/chatPromptContentStore.js'; - -suite('ChatPromptFileSystemProvider', () => { - const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - - let contentStore: ChatPromptContentStore; - let provider: ChatPromptFileSystemProvider; - - setup(() => { - contentStore = testDisposables.add(new ChatPromptContentStore()); - provider = new ChatPromptFileSystemProvider(contentStore); - }); - - suite('stat', () => { - test('returns stat for registered content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/test-agent'); - const content = '# Test Agent\nThis is test content.'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const stat = await provider.stat(uri); - - assert.strictEqual(stat.type, 1); // FileType.File - assert.strictEqual(stat.size, VSBuffer.fromString(content).byteLength); - }); - - test('throws FileNotFound for unregistered URI', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/missing'); - - await assert.rejects( - () => provider.stat(uri), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.FileNotFound; - }, - 'Should throw FileNotFound error' - ); - }); - - test('returns correct size for empty content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/empty'); - - testDisposables.add(contentStore.registerContent(uri, '')); - - const stat = await provider.stat(uri); - - assert.strictEqual(stat.size, 0); - }); - - test('returns correct size for unicode content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.instructions.md/unicode'); - const content = '日本語テスト 🎉'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const stat = await provider.stat(uri); - - assert.strictEqual(stat.size, VSBuffer.fromString(content).byteLength); - }); - }); - - suite('readFile', () => { - test('returns content for registered URI', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/test-agent'); - const content = '# Test Agent\nThis is test content.'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const result = await provider.readFile(uri); - - assert.strictEqual(VSBuffer.wrap(result).toString(), content); - }); - - test('throws FileNotFound for unregistered URI', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/missing'); - - await assert.rejects( - () => provider.readFile(uri), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.FileNotFound; - }, - 'Should throw FileNotFound error' - ); - }); - - test('returns empty buffer for empty content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/empty'); - - testDisposables.add(contentStore.registerContent(uri, '')); - - const result = await provider.readFile(uri); - - assert.strictEqual(result.byteLength, 0); - }); - - test('preserves unicode content', async () => { - const uri = URI.parse('vscode-chat-prompt:/.instructions.md/unicode'); - const content = '日本語テスト 🎉\n\n```typescript\nconst greeting = "こんにちは";\n```'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const result = await provider.readFile(uri); - - assert.strictEqual(VSBuffer.wrap(result).toString(), content); - }); - - test('handles content with special markdown characters', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/markdown'); - const content = '# Heading\n\n- List item\n- Another item\n\n> Blockquote\n\n```\ncode block\n```'; - - testDisposables.add(contentStore.registerContent(uri, content)); - - const result = await provider.readFile(uri); - - assert.strictEqual(VSBuffer.wrap(result).toString(), content); - }); - }); - - suite('content lifecycle', () => { - test('readFile fails after content is disposed', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/lifecycle-test'); - const content = 'Temporary content'; - - const registration = contentStore.registerContent(uri, content); - - // Verify content is readable - const result = await provider.readFile(uri); - assert.strictEqual(VSBuffer.wrap(result).toString(), content); - - // Dispose the content - registration.dispose(); - - // Now reading should fail - await assert.rejects( - () => provider.readFile(uri), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.FileNotFound; - } - ); - }); - - test('stat fails after content is disposed', async () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/lifecycle-stat'); - const content = 'Content for stat test'; - - const registration = contentStore.registerContent(uri, content); - - // Verify stat works - const stat = await provider.stat(uri); - assert.strictEqual(stat.size, VSBuffer.fromString(content).byteLength); - - // Dispose the content - registration.dispose(); - - // Now stat should fail - await assert.rejects( - () => provider.stat(uri), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.FileNotFound; - } - ); - }); - }); - - suite('URI normalization', () => { - test('readFile succeeds when URI has query parameters', async () => { - const baseUri = URI.parse('vscode-chat-prompt:/.agent.md/query-test'); - const content = 'Content for query test'; - - testDisposables.add(contentStore.registerContent(baseUri, content)); - - // Read with query parameters - const uriWithQuery = baseUri.with({ query: 'vscodeLinkType=prompt' }); - const result = await provider.readFile(uriWithQuery); - - assert.strictEqual(VSBuffer.wrap(result).toString(), content); - }); - - test('stat succeeds when URI has fragment', async () => { - const baseUri = URI.parse('vscode-chat-prompt:/.instructions.md/fragment-test'); - const content = 'Content for fragment test'; - - testDisposables.add(contentStore.registerContent(baseUri, content)); - - // Stat with fragment - const uriWithFragment = baseUri.with({ fragment: 'section1' }); - const stat = await provider.stat(uriWithFragment); - - assert.strictEqual(stat.size, VSBuffer.fromString(content).byteLength); - }); - }); - - suite('unsupported operations', () => { - test('writeFile throws NoPermissions error', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/write-test'); - - await assert.rejects( - () => provider.writeFile(uri, new Uint8Array(), { create: true, overwrite: true, unlock: false, atomic: false }), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.NoPermissions; - } - ); - }); - - test('mkdir throws NoPermissions error', async () => { - const uri = URI.parse('vscode-chat-prompt:/test-dir'); - - await assert.rejects( - () => provider.mkdir(uri), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.NoPermissions; - } - ); - }); - - test('delete throws NoPermissions error', async () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/delete-test'); - - await assert.rejects( - () => provider.delete(uri, { recursive: false, useTrash: false, atomic: false }), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.NoPermissions; - } - ); - }); - - test('rename throws NoPermissions error', async () => { - const from = URI.parse('vscode-chat-prompt:/.agent.md/rename-from'); - const to = URI.parse('vscode-chat-prompt:/.agent.md/rename-to'); - - await assert.rejects( - () => provider.rename(from, to, { overwrite: false }), - (err: Error & { code?: string }) => { - return toFileSystemProviderErrorCode(err) === FileSystemProviderErrorCode.NoPermissions; - } - ); - }); - - test('readdir returns empty array', async () => { - const uri = URI.parse('vscode-chat-prompt:/'); - - const result = await provider.readdir(uri); - - assert.deepStrictEqual(result, []); - }); - }); -}); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/chatPromptContentStore.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/chatPromptContentStore.test.ts deleted file mode 100644 index 75f4496a0e3..00000000000 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/chatPromptContentStore.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import assert from 'assert'; -import { URI } from '../../../../../../base/common/uri.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { ChatPromptContentStore } from '../../../common/promptSyntax/chatPromptContentStore.js'; - -suite('ChatPromptContentStore', () => { - const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - - let store: ChatPromptContentStore; - - setup(() => { - store = testDisposables.add(new ChatPromptContentStore()); - }); - - test('registerContent stores content retrievable by URI', () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/test-id'); - const content = '# Test Agent\nThis is test content'; - - const disposable = store.registerContent(uri, content); - testDisposables.add(disposable); - - const retrieved = store.getContent(uri); - assert.strictEqual(retrieved, content); - }); - - test('getContent returns undefined for unregistered URI', () => { - const uri = URI.parse('vscode-chat-prompt:/.agent.md/unknown-id'); - - const retrieved = store.getContent(uri); - assert.strictEqual(retrieved, undefined); - }); - - test('registerContent returns disposable that removes content', () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/disposable-test'); - const content = 'Content to be disposed'; - - const disposable = store.registerContent(uri, content); - - // Content should exist before disposal - assert.strictEqual(store.getContent(uri), content); - - // Dispose and verify content is removed - disposable.dispose(); - assert.strictEqual(store.getContent(uri), undefined); - }); - - test('multiple registrations for different URIs are independent', () => { - const uri1 = URI.parse('vscode-chat-prompt:/.agent.md/id-1'); - const uri2 = URI.parse('vscode-chat-prompt:/.instructions.md/id-2'); - const content1 = 'Content 1'; - const content2 = 'Content 2'; - - const disposable1 = store.registerContent(uri1, content1); - const disposable2 = store.registerContent(uri2, content2); - testDisposables.add(disposable1); - testDisposables.add(disposable2); - - assert.strictEqual(store.getContent(uri1), content1); - assert.strictEqual(store.getContent(uri2), content2); - - // Disposing one should not affect the other - disposable1.dispose(); - assert.strictEqual(store.getContent(uri1), undefined); - assert.strictEqual(store.getContent(uri2), content2); - }); - - test('re-registering same URI overwrites content', () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/overwrite-test'); - const content1 = 'Original content'; - const content2 = 'Updated content'; - - const disposable1 = store.registerContent(uri, content1); - testDisposables.add(disposable1); - - assert.strictEqual(store.getContent(uri), content1); - - const disposable2 = store.registerContent(uri, content2); - testDisposables.add(disposable2); - - assert.strictEqual(store.getContent(uri), content2); - }); - - test('store disposal clears all content', () => { - const uri1 = URI.parse('vscode-chat-prompt:/.agent.md/clear-1'); - const uri2 = URI.parse('vscode-chat-prompt:/.agent.md/clear-2'); - - store.registerContent(uri1, 'Content 1'); - store.registerContent(uri2, 'Content 2'); - - assert.strictEqual(store.getContent(uri1), 'Content 1'); - assert.strictEqual(store.getContent(uri2), 'Content 2'); - - // Create a new store for this test that we can dispose independently - const localStore = new ChatPromptContentStore(); - const localUri = URI.parse('vscode-chat-prompt:/.agent.md/local'); - localStore.registerContent(localUri, 'Local content'); - - assert.strictEqual(localStore.getContent(localUri), 'Local content'); - - localStore.dispose(); - assert.strictEqual(localStore.getContent(localUri), undefined); - }); - - test('empty string content is stored correctly', () => { - const uri = URI.parse('vscode-chat-prompt:/.prompt.md/empty-content'); - - const disposable = store.registerContent(uri, ''); - testDisposables.add(disposable); - - const retrieved = store.getContent(uri); - assert.strictEqual(retrieved, ''); - }); - - test('content with special characters is stored correctly', () => { - const uri = URI.parse('vscode-chat-prompt:/.instructions.md/special-chars'); - const content = '# Test\n\nUnicode: 你好世界 🎉\nSpecial: ${{variable}} @mention #tag'; - - const disposable = store.registerContent(uri, content); - testDisposables.add(disposable); - - const retrieved = store.getContent(uri); - assert.strictEqual(retrieved, content); - }); - - test('URI comparison is string-based', () => { - // Same logical URI created two different ways - const uri1 = URI.parse('vscode-chat-prompt:/.agent.md/test'); - const uri2 = URI.from({ - scheme: 'vscode-chat-prompt', - path: '/.agent.md/test' - }); - - const content = 'Test content'; - const disposable = store.registerContent(uri1, content); - testDisposables.add(disposable); - - // Should be retrievable with equivalent URI - assert.strictEqual(store.getContent(uri2), content); - }); - - test('getContent normalizes URI by stripping query parameters', () => { - const baseUri = URI.parse('vscode-chat-prompt:/.agent.md/normalize-test'); - const content = 'Normalized content'; - - const disposable = store.registerContent(baseUri, content); - testDisposables.add(disposable); - - // Should retrieve content when queried with extra query parameters - const uriWithQuery = baseUri.with({ query: 'vscodeLinkType=prompt' }); - assert.strictEqual(store.getContent(uriWithQuery), content); - }); - - test('getContent normalizes URI by stripping fragment', () => { - const baseUri = URI.parse('vscode-chat-prompt:/.instructions.md/fragment-test'); - const content = 'Content with fragment lookup'; - - const disposable = store.registerContent(baseUri, content); - testDisposables.add(disposable); - - // Should retrieve content when queried with fragment - const uriWithFragment = baseUri.with({ fragment: 'section1' }); - assert.strictEqual(store.getContent(uriWithFragment), content); - }); - - test('getContent normalizes URI by stripping both query and fragment', () => { - const baseUri = URI.parse('vscode-chat-prompt:/.prompt.md/full-normalize'); - const content = 'Fully normalized content'; - - const disposable = store.registerContent(baseUri, content); - testDisposables.add(disposable); - - // Should retrieve content when queried with both query and fragment - const uriWithBoth = baseUri.with({ query: 'vscodeLinkType=skill&foo=bar', fragment: 'heading' }); - assert.strictEqual(store.getContent(uriWithBoth), content); - }); - - test('registerContent normalizes URI so content registered with query is found without it', () => { - const uriWithQuery = URI.parse('vscode-chat-prompt:/.agent.md/register-with-query?vscodeLinkType=agent'); - const content = 'Content registered with query'; - - const disposable = store.registerContent(uriWithQuery, content); - testDisposables.add(disposable); - - // Should retrieve content using base URI without query - const baseUri = uriWithQuery.with({ query: '' }); - assert.strictEqual(store.getContent(baseUri), content); - }); -}); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts index af040c6e439..044a0f1db1a 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts @@ -1742,78 +1742,6 @@ suite('PromptsService', () => { assert.strictEqual(actualAfterDispose.length, 0); }); - test('Custom agent provider with isEditable', async () => { - const readonlyAgentUri = URI.parse('file://extensions/my-extension/readonlyAgent.agent.md'); - const editableAgentUri = URI.parse('file://extensions/my-extension/editableAgent.agent.md'); - const extension = { - identifier: { value: 'test.my-extension' }, - enabledApiProposals: ['chatParticipantPrivate'] - } as unknown as IExtensionDescription; - - // Mock the agent file content - await mockFiles(fileService, [ - { - path: readonlyAgentUri.path, - contents: [ - '---', - 'description: \'Readonly agent from provider\'', - '---', - 'I am a readonly agent.', - ] - }, - { - path: editableAgentUri.path, - contents: [ - '---', - 'description: \'Editable agent from provider\'', - '---', - 'I am an editable agent.', - ] - } - ]); - - const provider = { - providePromptFiles: async (_context: IPromptFileContext, _token: CancellationToken) => { - return [ - { - uri: readonlyAgentUri, - isEditable: false - }, - { - uri: editableAgentUri, - isEditable: true - } - ]; - } - }; - - const registered = service.registerPromptFileProvider(extension, PromptsType.agent, provider); - - // Spy on updateReadonly to verify it's called correctly - const filesConfigService = instaService.get(IFilesConfigurationService); - const updateReadonlySpy = sinon.spy(filesConfigService, 'updateReadonly'); - - // List prompt files to trigger the readonly check - await service.listPromptFiles(PromptsType.agent, CancellationToken.None); - - // Verify updateReadonly was called only for the non-editable agent - assert.strictEqual(updateReadonlySpy.callCount, 1, 'updateReadonly should be called once'); - assert.ok(updateReadonlySpy.calledWith(readonlyAgentUri, true), 'updateReadonly should be called with readonly agent URI and true'); - - const actual = await service.getCustomAgents(CancellationToken.None); - assert.strictEqual(actual.length, 2); - - const readonlyAgent = actual.find(a => a.name === 'readonlyAgent'); - const editableAgent = actual.find(a => a.name === 'editableAgent'); - - assert.ok(readonlyAgent, 'Readonly agent should be found'); - assert.ok(editableAgent, 'Editable agent should be found'); - assert.strictEqual(readonlyAgent!.description, 'Readonly agent from provider'); - assert.strictEqual(editableAgent!.description, 'Editable agent from provider'); - - registered.dispose(); - }); - test('Contributed agent file that does not exist should not crash', async () => { const nonExistentUri = URI.parse('file://extensions/my-extension/nonexistent.agent.md'); const existingUri = URI.parse('file://extensions/my-extension/existing.agent.md'); @@ -1911,68 +1839,6 @@ suite('PromptsService', () => { assert.strictEqual(foundAfterDispose, undefined); }); - test('Instructions provider with isEditable flag', async () => { - const readonlyInstructionUri = URI.parse('file://extensions/my-extension/readonly.instructions.md'); - const editableInstructionUri = URI.parse('file://extensions/my-extension/editable.instructions.md'); - const extension = { - identifier: { value: 'test.my-extension' }, - enabledApiProposals: ['chatParticipantPrivate'] - } as unknown as IExtensionDescription; - - // Mock the instruction file content - await mockFiles(fileService, [ - { - path: readonlyInstructionUri.path, - contents: [ - '# Readonly instruction content' - ] - }, - { - path: editableInstructionUri.path, - contents: [ - '# Editable instruction content' - ] - } - ]); - - const provider = { - providePromptFiles: async (_context: IPromptFileContext, _token: CancellationToken) => { - return [ - { - uri: readonlyInstructionUri, - isEditable: false - }, - { - uri: editableInstructionUri, - isEditable: true - } - ]; - } - }; - - const registered = service.registerPromptFileProvider(extension, PromptsType.instructions, provider); - - // Spy on updateReadonly to verify it's called correctly - const filesConfigService = instaService.get(IFilesConfigurationService); - const updateReadonlySpy = sinon.spy(filesConfigService, 'updateReadonly'); - - // List prompt files to trigger the readonly check - await service.listPromptFiles(PromptsType.instructions, CancellationToken.None); - - // Verify updateReadonly was called only for the non-editable instruction - assert.strictEqual(updateReadonlySpy.callCount, 1, 'updateReadonly should be called once'); - assert.ok(updateReadonlySpy.calledWith(readonlyInstructionUri, true), 'updateReadonly should be called with readonly instruction URI and true'); - - const actual = await service.listPromptFiles(PromptsType.instructions, CancellationToken.None); - const readonlyInstruction = actual.find(i => i.uri.toString() === readonlyInstructionUri.toString()); - const editableInstruction = actual.find(i => i.uri.toString() === editableInstructionUri.toString()); - - assert.ok(readonlyInstruction, 'Readonly instruction should be found'); - assert.ok(editableInstruction, 'Editable instruction should be found'); - - registered.dispose(); - }); - test('Prompt file provider', async () => { const promptUri = URI.parse('file://extensions/my-extension/myPrompt.prompt.md'); const extension = { @@ -2018,68 +1884,6 @@ suite('PromptsService', () => { assert.strictEqual(foundAfterDispose, undefined); }); - test('Prompt file provider with isEditable flag', async () => { - const readonlyPromptUri = URI.parse('file://extensions/my-extension/readonly.prompt.md'); - const editablePromptUri = URI.parse('file://extensions/my-extension/editable.prompt.md'); - const extension = { - identifier: { value: 'test.my-extension' }, - enabledApiProposals: ['chatParticipantPrivate'] - } as unknown as IExtensionDescription; - - // Mock the prompt file content - await mockFiles(fileService, [ - { - path: readonlyPromptUri.path, - contents: [ - '# Readonly prompt content' - ] - }, - { - path: editablePromptUri.path, - contents: [ - '# Editable prompt content' - ] - } - ]); - - const provider = { - providePromptFiles: async (_context: IPromptFileContext, _token: CancellationToken) => { - return [ - { - uri: readonlyPromptUri, - isEditable: false - }, - { - uri: editablePromptUri, - isEditable: true - } - ]; - } - }; - - const registered = service.registerPromptFileProvider(extension, PromptsType.prompt, provider); - - // Spy on updateReadonly to verify it's called correctly - const filesConfigService = instaService.get(IFilesConfigurationService); - const updateReadonlySpy = sinon.spy(filesConfigService, 'updateReadonly'); - - // List prompt files to trigger the readonly check - await service.listPromptFiles(PromptsType.prompt, CancellationToken.None); - - // Verify updateReadonly was called only for the non-editable prompt - assert.strictEqual(updateReadonlySpy.callCount, 1, 'updateReadonly should be called once'); - assert.ok(updateReadonlySpy.calledWith(readonlyPromptUri, true), 'updateReadonly should be called with readonly prompt URI and true'); - - const actual = await service.listPromptFiles(PromptsType.prompt, CancellationToken.None); - const readonlyPrompt = actual.find(i => i.uri.toString() === readonlyPromptUri.toString()); - const editablePrompt = actual.find(i => i.uri.toString() === editablePromptUri.toString()); - - assert.ok(readonlyPrompt, 'Readonly prompt should be found'); - assert.ok(editablePrompt, 'Editable prompt should be found'); - - registered.dispose(); - }); - test('Skill file provider', async () => { const skillUri = URI.parse('file://extensions/my-extension/mySkill/SKILL.md'); const extension = { @@ -2129,76 +1933,6 @@ suite('PromptsService', () => { assert.strictEqual(foundAfterDispose, undefined); }); - test('Skill file provider with isEditable flag', async () => { - const readonlySkillUri = URI.parse('file://extensions/my-extension/readonlySkill/SKILL.md'); - const editableSkillUri = URI.parse('file://extensions/my-extension/editableSkill/SKILL.md'); - const extension = { - identifier: { value: 'test.my-extension' }, - enabledApiProposals: ['chatParticipantPrivate'] - } as unknown as IExtensionDescription; - - // Mock the skill file content - await mockFiles(fileService, [ - { - path: readonlySkillUri.path, - contents: [ - '---', - 'name: "Readonly Skill"', - 'description: "A readonly skill"', - '---', - 'Readonly skill content.', - ] - }, - { - path: editableSkillUri.path, - contents: [ - '---', - 'name: "Editable Skill"', - 'description: "An editable skill"', - '---', - 'Editable skill content.', - ] - } - ]); - - const provider = { - providePromptFiles: async (_context: IPromptFileContext, _token: CancellationToken) => { - return [ - { - uri: readonlySkillUri, - isEditable: false - }, - { - uri: editableSkillUri, - isEditable: true - } - ]; - } - }; - - const registered = service.registerPromptFileProvider(extension, PromptsType.skill, provider); - - // Spy on updateReadonly to verify it's called correctly - const filesConfigService = instaService.get(IFilesConfigurationService); - const updateReadonlySpy = sinon.spy(filesConfigService, 'updateReadonly'); - - // List prompt files to trigger the readonly check - await service.listPromptFiles(PromptsType.skill, CancellationToken.None); - - // Verify updateReadonly was called only for the non-editable skill - assert.strictEqual(updateReadonlySpy.callCount, 1, 'updateReadonly should be called once'); - assert.ok(updateReadonlySpy.calledWith(readonlySkillUri, true), 'updateReadonly should be called with readonly skill URI and true'); - - const actual = await service.listPromptFiles(PromptsType.skill, CancellationToken.None); - const readonlySkill = actual.find(i => i.uri.toString() === readonlySkillUri.toString()); - const editableSkill = actual.find(i => i.uri.toString() === editableSkillUri.toString()); - - assert.ok(readonlySkill, 'Readonly skill should be found'); - assert.ok(editableSkill, 'Editable skill should be found'); - - registered.dispose(); - }); - suite('findAgentSkills', () => { teardown(() => { sinon.restore(); diff --git a/src/vscode-dts/vscode.proposed.chatPromptFiles.d.ts b/src/vscode-dts/vscode.proposed.chatPromptFiles.d.ts index f97cb106c10..e683d6ce600 100644 --- a/src/vscode-dts/vscode.proposed.chatPromptFiles.d.ts +++ b/src/vscode-dts/vscode.proposed.chatPromptFiles.d.ts @@ -9,99 +9,23 @@ declare module 'vscode' { // #region Resource Classes /** - * Describes a chat resource file. + * Represents a chat-related resource, such as a custom agent, instructions, prompt file, or skill. */ - export type ChatResourceDescriptor = - | Uri - | { uri: Uri; isEditable?: boolean } - | { - id: string; - content: string; - }; - - /** - * Represents a custom agent resource file (e.g., .agent.md). - */ - export class CustomAgentChatResource { + export interface ChatResource { /** - * The custom agent resource descriptor. + * Uri to the chat resource. This is typically a `.agent.md`, `.instructions.md`, `.prompt.md`, or `SKILL.md` file. */ - readonly resource: ChatResourceDescriptor; - - /** - * Creates a new custom agent resource from the specified resource. - * @param resource The chat resource descriptor. - */ - constructor(resource: ChatResourceDescriptor); - } - - /** - * Represents an instructions resource file. - */ - export class InstructionsChatResource { - /** - * The instructions resource descriptor. - */ - readonly resource: ChatResourceDescriptor; - - /** - * Creates a new instructions resource from the specified resource. - * @param resource The chat resource descriptor. - */ - constructor(resource: ChatResourceDescriptor); - } - - /** - * Represents a prompt file resource (e.g., .prompt.md). - */ - export class PromptFileChatResource { - /** - * The prompt file resource descriptor. - */ - readonly resource: ChatResourceDescriptor; - - /** - * Creates a new prompt file resource from the specified resource. - * @param resource The chat resource descriptor. - */ - constructor(resource: ChatResourceDescriptor); - } - - /** - * Represents a skill file resource (SKILL.md) - */ - export class SkillChatResource { - /** - * The skill resource descriptor. - */ - readonly resource: ChatResourceDescriptor; - - /** - * Creates a new skill resource from the specified resource URI pointing to SKILL.md. - * The parent folder name needs to match the name of the skill in the frontmatter. - * @param resource The chat resource descriptor. - */ - constructor(resource: ChatResourceDescriptor); + readonly uri: Uri; } // #endregion // #region Providers - /** - * Options for querying custom agents. - */ - export type CustomAgentContext = object; - /** * A provider that supplies custom agent resources (from .agent.md files) for repositories. */ - export interface CustomAgentProvider { - /** - * A human-readable label for this provider. - */ - readonly label: string; - + export interface ChatCustomAgentProvider { /** * An optional event to signal that custom agents have changed. */ @@ -109,30 +33,17 @@ declare module 'vscode' { /** * Provide the list of custom agents available. - * @param context Context for the query. + * @param context Context for the provide call. * @param token A cancellation token. * @returns An array of custom agents or a promise that resolves to such. */ - provideCustomAgents( - context: CustomAgentContext, - token: CancellationToken - ): ProviderResult; + provideCustomAgents(context: unknown, token: CancellationToken): ProviderResult; } - /** - * Context for querying instructions. - */ - export type InstructionsContext = object; - /** * A provider that supplies instructions resources for repositories. */ - export interface InstructionsProvider { - /** - * A human-readable label for this provider. - */ - readonly label: string; - + export interface ChatInstructionsProvider { /** * An optional event to signal that instructions have changed. */ @@ -140,30 +51,17 @@ declare module 'vscode' { /** * Provide the list of instructions available. - * @param context Context for the query. + * @param context Context for the provide call. * @param token A cancellation token. * @returns An array of instructions or a promise that resolves to such. */ - provideInstructions( - context: InstructionsContext, - token: CancellationToken - ): ProviderResult; + provideInstructions(context: unknown, token: CancellationToken): ProviderResult; } - /** - * Context for querying prompt files. - */ - export type PromptFileContext = object; - /** * A provider that supplies prompt file resources (from .prompt.md files) for repositories. */ - export interface PromptFileProvider { - /** - * A human-readable label for this provider. - */ - readonly label: string; - + export interface ChatPromptFileProvider { /** * An optional event to signal that prompt files have changed. */ @@ -171,34 +69,21 @@ declare module 'vscode' { /** * Provide the list of prompt files available. - * @param context Context for the query. + * @param context Context for the provide call. * @param token A cancellation token. * @returns An array of prompt files or a promise that resolves to such. */ - providePromptFiles( - context: PromptFileContext, - token: CancellationToken - ): ProviderResult; + providePromptFiles(context: unknown, token: CancellationToken): ProviderResult; } // #endregion // #region SkillProvider - /** - * Context for querying skills. - */ - export type SkillContext = object; - /** * A provider that supplies SKILL.md resources for agents. */ - export interface SkillProvider { - /** - * A human-readable label for this provider. - */ - readonly label: string; - + export interface ChatSkillProvider { /** * An optional event to signal that skills have changed. */ @@ -206,14 +91,11 @@ declare module 'vscode' { /** * Provide the list of skills available. - * @param context Context for the query. + * @param context Context for the provide call. * @param token A cancellation token. * @returns An array of skill resources or a promise that resolves to such. */ - provideSkills( - context: SkillContext, - token: CancellationToken - ): ProviderResult; + provideSkills(context: unknown, token: CancellationToken): ProviderResult; } // #endregion @@ -226,34 +108,28 @@ declare module 'vscode' { * @param provider The custom agent provider. * @returns A disposable that unregisters the provider when disposed. */ - export function registerCustomAgentProvider( - provider: CustomAgentProvider - ): Disposable; + export function registerCustomAgentProvider(provider: ChatCustomAgentProvider): 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; + export function registerInstructionsProvider(provider: ChatInstructionsProvider): Disposable; /** * Register a provider for prompt files. * @param provider The prompt file provider. * @returns A disposable that unregisters the provider when disposed. */ - export function registerPromptFileProvider( - provider: PromptFileProvider - ): Disposable; + export function registerPromptFileProvider(provider: ChatPromptFileProvider): Disposable; /** * Register a provider for skills. * @param provider The skill provider. * @returns A disposable that unregisters the provider when disposed. */ - export function registerSkillProvider(provider: SkillProvider): Disposable; + export function registerSkillProvider(provider: ChatSkillProvider): Disposable; } // #endregion