diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 0b570051b19..51a74ad7b00 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -7,8 +7,6 @@ "enabledApiProposals": [ "activeComment", "authSession", - "chatParticipant", - "languageModels", "defaultChatParticipant", "chatVariableResolver", "contribViewsRemote", diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5e7e148ff36..680d04cb5dd 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -209,7 +209,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol)); const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); - const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, initData.quality)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol)); @@ -1427,7 +1427,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostChatAgents2.createChatAgent(extension, id, handler); }, createDynamicChatParticipant(id: string, dynamicProps: vscode.DynamicChatParticipantProps, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant { - checkProposedApiEnabled(extension, 'chatParticipantAdditions'); + checkProposedApiEnabled(extension, 'chatParticipantPrivate'); return extHostChatAgents2.createDynamicChatAgent(extension, id, dynamicProps, handler); }, attachContext(name: string, value: string | vscode.Uri | vscode.Location | unknown, location: vscode.ChatLocation.Panel) { diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 09639dd0f3a..435c4c45f69 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -263,6 +263,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS mainContext: IMainContext, private readonly _logService: ILogService, private readonly commands: ExtHostCommands, + private readonly quality: string | undefined ) { super(); this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); @@ -274,16 +275,19 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS createChatAgent(extension: IExtensionDescription, id: string, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant { const handle = ExtHostChatAgents2._idPool++; - const agent = new ExtHostChatAgent(extension, id, this._proxy, handle, handler); + const agent = new ExtHostChatAgent(extension, this.quality, id, this._proxy, handle, handler); this._agents.set(handle, agent); - this._proxy.$registerAgent(handle, extension.identifier, id, {}, undefined); + if (agent.isAgentEnabled()) { + this._proxy.$registerAgent(handle, extension.identifier, id, {}, undefined); + } + return agent.apiAgent; } createDynamicChatAgent(extension: IExtensionDescription, id: string, dynamicProps: vscode.DynamicChatParticipantProps, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant { const handle = ExtHostChatAgents2._idPool++; - const agent = new ExtHostChatAgent(extension, id, this._proxy, handle, handler); + const agent = new ExtHostChatAgent(extension, this.quality, id, this._proxy, handle, handler); this._agents.set(handle, agent); this._proxy.$registerAgent(handle, extension.identifier, id, { isSticky: true } satisfies IExtensionChatAgentMetadata, dynamicProps); @@ -330,6 +334,10 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS responseIsIncomplete: true }; } + if (errorDetails?.responseIsRedacted) { + checkProposedApiEnabled(agent.extension, 'chatParticipantPrivate'); + } + return { errorDetails, timings: stream.timings, metadata: result?.metadata } satisfies IChatAgentResult; }), token); } catch (e) { @@ -485,6 +493,7 @@ class ExtHostChatAgent { constructor( public readonly extension: IExtensionDescription, + private readonly quality: string | undefined, public readonly id: string, private readonly _proxy: MainThreadChatAgentsShape2, private readonly _handle: number, @@ -507,6 +516,11 @@ class ExtHostChatAgent { return await this._agentVariableProvider.provider.provideCompletionItems(query, token) ?? []; } + public isAgentEnabled() { + // If in stable and this extension doesn't have the right proposed API, then don't register the agent + return !(this.quality === 'stable' && !isProposedApiEnabled(this.extension, 'chatParticipantPrivate')); + } + async provideFollowups(result: vscode.ChatResult, context: vscode.ChatContext, token: CancellationToken): Promise { if (!this._followupProvider) { return []; @@ -564,6 +578,10 @@ class ExtHostChatAgent { } updateScheduled = true; queueMicrotask(() => { + if (!that.isAgentEnabled()) { + return; + } + this._proxy.$updateAgent(this._handle, { icon: !this._iconPath ? undefined : this._iconPath instanceof URI ? this._iconPath : @@ -658,11 +676,11 @@ class ExtHostChatAgent { updateMetadataSoon(); }, get supportIssueReporting() { - checkProposedApiEnabled(that.extension, 'chatParticipantAdditions'); + checkProposedApiEnabled(that.extension, 'chatParticipantPrivate'); return that._supportIssueReporting; }, set supportIssueReporting(v) { - checkProposedApiEnabled(that.extension, 'chatParticipantAdditions'); + checkProposedApiEnabled(that.extension, 'chatParticipantPrivate'); that._supportIssueReporting = v; updateMetadataSoon(); }, @@ -707,10 +725,12 @@ class ExtHostChatAgent { return that._requester; }, set supportsSlowReferences(v) { + checkProposedApiEnabled(that.extension, 'chatParticipantPrivate'); that._supportsSlowReferences = v; updateMetadataSoon(); }, get supportsSlowReferences() { + checkProposedApiEnabled(that.extension, 'chatParticipantPrivate'); return that._supportsSlowReferences; }, dispose() { diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts index e9e074f0cfb..c89a6a4d712 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts @@ -159,6 +159,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { private handleAndRegisterChatExtensions(): void { chatParticipantExtensionPoint.setHandler((extensions, delta) => { for (const extension of delta.added) { + if (this.productService.quality === 'stable' && !isProposedApiEnabled(extension.description, 'chatParticipantPrivate')) { + this.logService.warn(`Chat participants are not yet enabled in VS Code Stable (${extension.description.identifier.value})`); + continue; + } + for (const providerDescriptor of extension.value) { if (!providerDescriptor.name.match(/^[\w0-9_-]+$/)) { this.logService.error(`Extension '${extension.description.identifier.value}' CANNOT register participant with invalid name: ${providerDescriptor.name}. Name must match /^[\\w0-9_-]+$/.`); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 3dae0a8c70d..28cf88a67b3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -15,6 +15,7 @@ export const allApiProposals = Object.freeze({ authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts', chatParticipantAdditions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts', + chatParticipantPrivate: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts', chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', chatTab: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts', chatVariableResolver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts', diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index 2e89410250c..cd2ec7ba919 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -5,60 +5,8 @@ declare module 'vscode' { - /** - * The location at which the chat is happening. - */ - export enum ChatLocation { - /** - * The chat panel - */ - Panel = 1, - /** - * Terminal inline chat - */ - Terminal = 2, - /** - * Notebook inline chat - */ - Notebook = 3, - /** - * Code editor inline chat - */ - Editor = 4 - } - - export interface ChatRequest { - /** - * The attempt number of the request. The first request has attempt number 0. - */ - readonly attempt: number; - - /** - * If automatic command detection is enabled. - */ - readonly enableCommandDetection: boolean; - - /** - * The location at which the chat is happening. This will always be one of the supported values - */ - readonly location: ChatLocation; - } - export interface ChatParticipant { onDidPerformAction: Event; - supportIssueReporting?: boolean; - - /** - * Temp, support references that are slow to resolve and should be tools rather than references. - */ - supportsSlowReferences?: boolean; - } - - export interface ChatErrorDetails { - /** - * If set to true, the message content is completely hidden. Only ChatErrorDetails#message will be shown. - */ - responseIsRedacted?: boolean; } /** @@ -224,8 +172,6 @@ declare module 'vscode' { */ export function createChatParticipant(id: string, handler: ChatExtendedRequestHandler): ChatParticipant; - export function createDynamicChatParticipant(id: string, dynamicProps: DynamicChatParticipantProps, handler: ChatExtendedRequestHandler): ChatParticipant; - /** * Current version of the proposal. Changes whenever backwards-incompatible changes are made. * If a new feature is added that doesn't break existing code, the version is not incremented. When the extension uses this new feature, it should set its engines.vscode version appropriately. @@ -235,16 +181,6 @@ declare module 'vscode' { export const _version: 1 | number; } - /** - * These don't get set on the ChatParticipant after creation, like other props, because they are typically defined in package.json and we want them at the time of creation. - */ - export interface DynamicChatParticipantProps { - name: string; - publisherName: string; - description?: string; - fullName?: string; - } - /* * User action events */ @@ -313,71 +249,4 @@ declare module 'vscode' { */ readonly name: string; } - - /** - * The detail level of this chat variable value. - */ - export enum ChatVariableLevel { - Short = 1, - Medium = 2, - Full = 3 - } - - export interface ChatVariableValue { - /** - * The detail level of this chat variable value. If possible, variable resolvers should try to offer shorter values that will consume fewer tokens in an LLM prompt. - */ - level: ChatVariableLevel; - - /** - * The variable's value, which can be included in an LLM prompt as-is, or the chat participant may decide to read the value and do something else with it. - */ - value: string | Uri; - - /** - * A description of this value, which could be provided to the LLM as a hint. - */ - description?: string; - } - - export interface ChatVariableResolverResponseStream { - /** - * Push a progress part to this stream. Short-hand for - * `push(new ChatResponseProgressPart(value))`. - * - * @param value - * @returns This stream. - */ - progress(value: string): ChatVariableResolverResponseStream; - - /** - * Push a reference to this stream. Short-hand for - * `push(new ChatResponseReferencePart(value))`. - * - * *Note* that the reference is not rendered inline with the response. - * - * @param value A uri or location - * @returns This stream. - */ - reference(value: Uri | Location): ChatVariableResolverResponseStream; - - /** - * Pushes a part to this stream. - * - * @param part A response part, rendered or metadata - */ - push(part: ChatVariableResolverResponsePart): ChatVariableResolverResponseStream; - } - - export type ChatVariableResolverResponsePart = ChatResponseProgressPart | ChatResponseReferencePart; - - export interface ChatVariableResolver { - /** - * A callback to resolve the value of a chat variable. - * @param name The name of the variable. - * @param context Contextual information about this chat request. - * @param token A cancellation token. - */ - resolve2?(name: string, context: ChatVariableContext, stream: ChatVariableResolverResponseStream, token: CancellationToken): ProviderResult; - } } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts new file mode 100644 index 00000000000..4e328978a9d --- /dev/null +++ b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + /** + * The location at which the chat is happening. + */ + export enum ChatLocation { + /** + * The chat panel + */ + Panel = 1, + /** + * Terminal inline chat + */ + Terminal = 2, + /** + * Notebook inline chat + */ + Notebook = 3, + /** + * Code editor inline chat + */ + Editor = 4 + } + + export interface ChatRequest { + /** + * The attempt number of the request. The first request has attempt number 0. + */ + readonly attempt: number; + + /** + * If automatic command detection is enabled. + */ + readonly enableCommandDetection: boolean; + + /** + * The location at which the chat is happening. This will always be one of the supported values + */ + readonly location: ChatLocation; + } + + export interface ChatParticipant { + supportIssueReporting?: boolean; + + /** + * Temp, support references that are slow to resolve and should be tools rather than references. + */ + supportsSlowReferences?: boolean; + } + + export interface ChatErrorDetails { + /** + * If set to true, the message content is completely hidden. Only ChatErrorDetails#message will be shown. + */ + responseIsRedacted?: boolean; + } + + export namespace chat { + export function createDynamicChatParticipant(id: string, dynamicProps: DynamicChatParticipantProps, handler: ChatExtendedRequestHandler): ChatParticipant; + } + + /** + * These don't get set on the ChatParticipant after creation, like other props, because they are typically defined in package.json and we want them at the time of creation. + */ + export interface DynamicChatParticipantProps { + name: string; + publisherName: string; + description?: string; + fullName?: string; + } +} diff --git a/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts b/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts index 298fc82f61b..1b404980e2c 100644 --- a/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts +++ b/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts @@ -66,5 +66,71 @@ declare module 'vscode' { * @param token A cancellation token. */ resolve(name: string, context: ChatVariableContext, token: CancellationToken): ProviderResult; + + /** + * A callback to resolve the value of a chat variable. + * @param name The name of the variable. + * @param context Contextual information about this chat request. + * @param token A cancellation token. + */ + resolve2?(name: string, context: ChatVariableContext, stream: ChatVariableResolverResponseStream, token: CancellationToken): ProviderResult; } + + + /** + * The detail level of this chat variable value. + */ + export enum ChatVariableLevel { + Short = 1, + Medium = 2, + Full = 3 + } + + export interface ChatVariableValue { + /** + * The detail level of this chat variable value. If possible, variable resolvers should try to offer shorter values that will consume fewer tokens in an LLM prompt. + */ + level: ChatVariableLevel; + + /** + * The variable's value, which can be included in an LLM prompt as-is, or the chat participant may decide to read the value and do something else with it. + */ + value: string | Uri; + + /** + * A description of this value, which could be provided to the LLM as a hint. + */ + description?: string; + } + + export interface ChatVariableResolverResponseStream { + /** + * Push a progress part to this stream. Short-hand for + * `push(new ChatResponseProgressPart(value))`. + * + * @param value + * @returns This stream. + */ + progress(value: string): ChatVariableResolverResponseStream; + + /** + * Push a reference to this stream. Short-hand for + * `push(new ChatResponseReferencePart(value))`. + * + * *Note* that the reference is not rendered inline with the response. + * + * @param value A uri or location + * @returns This stream. + */ + reference(value: Uri | Location): ChatVariableResolverResponseStream; + + /** + * Pushes a part to this stream. + * + * @param part A response part, rendered or metadata + */ + push(part: ChatVariableResolverResponsePart): ChatVariableResolverResponseStream; + } + + export type ChatVariableResolverResponsePart = ChatResponseProgressPart | ChatResponseReferencePart; }