diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 1abc2a81ab3..4f886cbde2c 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -20,7 +20,6 @@ import { StatusBarItemsExtensionPoint } from 'vs/workbench/api/browser/statusBar import './mainThreadLocalization'; import './mainThreadBulkEdits'; import './mainThreadChatProvider'; -import './mainThreadChatSlashCommands'; import './mainThreadChatAgents'; import './mainThreadChatVariables'; import './mainThreadCodeInsets'; diff --git a/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts b/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts deleted file mode 100644 index b5955be9401..00000000000 --- a/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts +++ /dev/null @@ -1,61 +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 { DisposableMap } from 'vs/base/common/lifecycle'; -import { revive } from 'vs/base/common/marshalling'; -import { IProgress } from 'vs/platform/progress/common/progress'; -import { ExtHostChatSlashCommandsShape, ExtHostContext, MainContext, MainThreadChatSlashCommandsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; -import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; - - -@extHostNamedCustomer(MainContext.MainThreadChatSlashCommands) -export class MainThreadChatSlashCommands implements MainThreadChatSlashCommandsShape { - - private readonly _commands = new DisposableMap; - private readonly _pendingProgress = new Map>(); - private readonly _proxy: ExtHostChatSlashCommandsShape; - - constructor( - extHostContext: IExtHostContext, - @IChatSlashCommandService private readonly _chatSlashCommandService: IChatSlashCommandService - ) { - this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatSlashCommands); - } - - dispose(): void { - this._commands.clearAndDisposeAll(); - } - - $registerCommand(handle: number, name: string, detail: string): void { - - if (!this._chatSlashCommandService.hasCommand(name)) { - // dynamic slash commands! - this._chatSlashCommandService.registerSlashData({ - command: name, - detail - }); - } - - const d = this._chatSlashCommandService.registerSlashCallback(name, async (prompt, progress, history, token) => { - const requestId = Math.random(); - this._pendingProgress.set(requestId, progress); - try { - return await this._proxy.$executeCommand(handle, requestId, prompt, { history }, token); - } finally { - this._pendingProgress.delete(requestId); - } - }); - this._commands.set(handle, d); - } - - async $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise { - this._pendingProgress.get(requestId)?.report(revive(chunk)); - } - - $unregisterCommand(handle: number): void { - this._commands.deleteAndDispose(handle); - } -} diff --git a/src/vs/workbench/api/browser/mainThreadChatVariables.ts b/src/vs/workbench/api/browser/mainThreadChatVariables.ts index 2f731a5b62f..02303bb2ea8 100644 --- a/src/vs/workbench/api/browser/mainThreadChatVariables.ts +++ b/src/vs/workbench/api/browser/mainThreadChatVariables.ts @@ -9,7 +9,7 @@ import { IChatVariableData, IChatVariablesService } from 'vs/workbench/contrib/c import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; @extHostNamedCustomer(MainContext.MainThreadChatVariables) -export class MainThreadChatSlashCommands implements MainThreadChatVariablesShape { +export class MainThreadChatVariables implements MainThreadChatVariablesShape { private readonly _proxy: ExtHostChatVariablesShape; private readonly _variables = new DisposableMap(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 948c269fa21..814b302ee26 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -104,7 +104,6 @@ import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReport import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets'; import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; -import { ExtHostChatSlashCommands } from 'vs/workbench/api/common/extHostChatSlashCommand'; import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation'; import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector'; @@ -209,7 +208,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService)); const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService)); - const extHostChatSlashCommands = rpcProtocol.set(ExtHostContext.ExtHostChatSlashCommands, new ExtHostChatSlashCommands(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatAgents = rpcProtocol.set(ExtHostContext.ExtHostChatAgents, new ExtHostChatAgents(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); @@ -1347,10 +1345,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'chatProvider'); return extHostChatProvider.registerProvider(extension.identifier, id, provider, metadata); }, - registerSlashCommand(name: string, command: vscode.SlashCommand, metadata?: vscode.SlashCommandMetadata) { - checkProposedApiEnabled(extension, 'chatSlashCommands'); - return extHostChatSlashCommands.registerCommand(extension.identifier, name, command, metadata ?? { description: '' }); - }, requestChatAccess(id: string) { checkProposedApiEnabled(extension, 'chatRequestAccess'); return extHostChatProvider.requestChatResponseProvider(extension.identifier, id); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 53224b35c02..e8b0ae1ed72 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1145,16 +1145,6 @@ export interface ExtHostChatProviderShape { $handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise; } -export interface MainThreadChatSlashCommandsShape extends IDisposable { - $registerCommand(handle: number, name: string, detail: string): void; - $unregisterCommand(handle: number): void; - $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise; -} - -export interface ExtHostChatSlashCommandsShape { - $executeCommand(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise; -} - export interface MainThreadChatAgentsShape extends IDisposable { $registerAgent(handle: number, name: string, metadata: IChatAgentMetadata): void; $unregisterAgent(handle: number): void; @@ -2661,7 +2651,6 @@ export const MainContext = { MainThreadAuthentication: createProxyIdentifier('MainThreadAuthentication'), MainThreadBulkEdits: createProxyIdentifier('MainThreadBulkEdits'), MainThreadChatProvider: createProxyIdentifier('MainThreadChatProvider'), - MainThreadChatSlashCommands: createProxyIdentifier('MainThreadChatSlashCommands'), MainThreadChatAgents: createProxyIdentifier('MainThreadChatAgents'), MainThreadChatVariables: createProxyIdentifier('MainThreadChatVariables'), MainThreadClipboard: createProxyIdentifier('MainThreadClipboard'), @@ -2782,7 +2771,6 @@ export const ExtHostContext = { ExtHostInteractive: createProxyIdentifier('ExtHostInteractive'), ExtHostInlineChat: createProxyIdentifier('ExtHostInlineChatShape'), ExtHostChat: createProxyIdentifier('ExtHostChat'), - ExtHostChatSlashCommands: createProxyIdentifier('ExtHostChatSlashCommands'), ExtHostChatAgents: createProxyIdentifier('ExtHostChatAgents'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), diff --git a/src/vs/workbench/api/common/extHostChatSlashCommand.ts b/src/vs/workbench/api/common/extHostChatSlashCommand.ts deleted file mode 100644 index 5c3e6284650..00000000000 --- a/src/vs/workbench/api/common/extHostChatSlashCommand.ts +++ /dev/null @@ -1,92 +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 { CancellationToken } from 'vs/base/common/cancellation'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostChatSlashCommandsShape, IMainContext, MainContext, MainThreadChatSlashCommandsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; -import { ChatMessageRole } from 'vs/workbench/api/common/extHostTypes'; -import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; -import type * as vscode from 'vscode'; -import { Progress } from 'vs/platform/progress/common/progress'; -import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; -import { DeferredPromise, raceCancellation } from 'vs/base/common/async'; - -export class ExtHostChatSlashCommands implements ExtHostChatSlashCommandsShape { - - private static _idPool = 0; - - private readonly _commands = new Map(); - private readonly _proxy: MainThreadChatSlashCommandsShape; - - constructor( - mainContext: IMainContext, - private readonly _extHostChatProvider: ExtHostChatProvider, - private readonly _logService: ILogService, - ) { - this._proxy = mainContext.getProxy(MainContext.MainThreadChatSlashCommands); - } - - registerCommand(extension: ExtensionIdentifier, name: string, command: vscode.SlashCommand, metadata: vscode.SlashCommandMetadata): IDisposable { - - const handle = ExtHostChatSlashCommands._idPool++; - this._commands.set(handle, { extension, command }); - this._proxy.$registerCommand(handle, name, metadata.description); - - return toDisposable(() => { - this._proxy.$unregisterCommand(handle); - this._commands.delete(handle); - }); - } - - async $executeCommand(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise { - const data = this._commands.get(handle); - if (!data) { - this._logService.warn(`[CHAT](${handle}) CANNOT execute command because the command is not registered`); - return; - } - - let done = false; - function throwIfDone() { - if (done) { - throw new Error('Only valid while executing the command'); - } - } - - const commandExecution = new DeferredPromise(); - token.onCancellationRequested(() => commandExecution.complete()); - setTimeout(() => commandExecution.complete(), 10 * 1000); - this._extHostChatProvider.allowListExtensionWhile(data.extension, commandExecution.p); - - const task = data.command( - { role: ChatMessageRole.User, content: prompt }, - { history: context.history.map(typeConvert.ChatMessage.to) }, - new Progress(p => { - throwIfDone(); - this._proxy.$handleProgressChunk(requestId, { content: isInteractiveProgressFileTree(p.message) ? p.message : p.message.value }); - }), - token - ); - - try { - return await raceCancellation(Promise.resolve(task).then((v) => { - if (v && 'followUp' in v) { - const convertedFollowup = v?.followUp?.map(f => typeConvert.ChatFollowup.from(f)); - return { followUp: convertedFollowup }; - } - return undefined; - }), token); - } finally { - done = true; - commandExecution.complete(); - } - } -} - -function isInteractiveProgressFileTree(thing: unknown): thing is vscode.InteractiveProgressFileTree { - return !!thing && typeof thing === 'object' && 'treeData' in thing; -} diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 715840f6b80..e7bbdf2b299 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -4,53 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Iterable } from 'vs/base/common/iterator'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Disposable, DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatFollowup, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService'; -import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; - -//#region extension point - -const slashItem: IJSONSchema = { - type: 'object', - required: ['command', 'detail'], - properties: { - command: { - type: 'string', - markdownDescription: localize('command', "The name of the slash command which will be used as prefix.") - }, - detail: { - type: 'string', - markdownDescription: localize('details', "The details of the slash command.") - }, - } -}; - -const slashItems: IJSONSchema = { - description: localize('vscode.extension.contributes.slashes', "Contributes slash commands to chat"), - oneOf: [ - slashItem, - { - type: 'array', - items: slashItem - } - ] -}; - -export const slashesExtPoint = ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'slashes', - jsonSchema: slashItems -}); +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; //#region slash service, commands etc @@ -66,14 +26,6 @@ export interface IChatSlashData { executeImmediately?: boolean; } -function isChatSlashData(data: any): data is IChatSlashData { - return typeof data === 'object' && data && - typeof data.command === 'string' && - typeof data.detail === 'string' && - (typeof data.sortText === 'undefined' || typeof data.sortText === 'string') && - (typeof data.executeImmediately === 'undefined' || typeof data.executeImmediately === 'boolean'); -} - export interface IChatSlashFragment { content: string | { treeData: IChatResponseProgressFileTreeData }; } @@ -82,6 +34,9 @@ export type IChatSlashCallback = { (prompt: string, progress: IProgress('chatSlashCommandService'); +/** + * This currently only exists to drive /clear. Delete this when the agent service can handle that scenario + */ export interface IChatSlashCommandService { _serviceBrand: undefined; readonly onDidChangeCommands: Event; @@ -166,34 +121,3 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom return await data.command(prompt, progress, history, token); } } - -class ChatSlashCommandContribution implements IWorkbenchContribution { - constructor(@IChatSlashCommandService slashCommandService: IChatSlashCommandService) { - const contributions = new DisposableStore(); - - slashesExtPoint.setHandler(extensions => { - contributions.clear(); - - for (const entry of extensions) { - if (!isProposedApiEnabled(entry.description, 'chatSlashCommands')) { - entry.collector.error(`The ${slashesExtPoint.name} is proposed API`); - continue; - } - - const { value } = entry; - - for (const candidate of Iterable.wrap(value)) { - - if (!isChatSlashData(candidate)) { - entry.collector.error(localize('invalid', "Invalid {0}: {1}", slashesExtPoint.name, JSON.stringify(candidate))); - continue; - } - - contributions.add(slashCommandService.registerSlashData({ ...candidate })); - } - } - }); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ChatSlashCommandContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 58141dd1bc6..c7e91c49959 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -14,7 +14,6 @@ export const allApiProposals = Object.freeze({ chatAgents: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents.d.ts', chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', chatRequestAccess: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts', - chatSlashCommands: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts', chatVariables: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariables.d.ts', codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts', commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts', diff --git a/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts b/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts deleted file mode 100644 index 7229fb15679..00000000000 --- a/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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' { - - export interface SlashCommandContext { - - // messages so far - history: ChatMessage[]; - - // TODO: access to embeddings - // embeddings: {}; - - // TODO: access to "InputSourceId" - // DebugConsoleOutput - // Terminal - // CorrespondingTestFile - // CorrespondingImplementationFile - // ExtensionApi - // VSCode - // Workspace - } - - export interface SlashResponse { - message: MarkdownString | InteractiveProgressFileTree; - // edits?: TextEdit[] | WorkspaceEdit; - } - - export interface SlashResult { - followUp?: InteractiveSessionFollowup[]; - } - - export interface SlashCommandMetadata { - description: string; - } - - export interface SlashCommand { - - (prompt: ChatMessage, context: SlashCommandContext, progress: Progress, token: CancellationToken): Thenable; - } - - export namespace chat { - export function registerSlashCommand(name: string, command: SlashCommand, metadata: SlashCommandMetadata): Disposable; - } -}