From dc7891db87d22412861a58feb19996a2e0a446d9 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 15 Jun 2023 15:22:20 -0700 Subject: [PATCH 01/11] Use vscode watches for tsserver --- .../src/tsServer/protocol/protocol.const.ts | 3 ++ .../src/tsServer/spawner.ts | 1 + .../src/typescriptService.ts | 1 + .../src/typescriptServiceClient.ts | 41 +++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts b/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts index deb3357e6ae30..4f02ed29427e0 100644 --- a/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts +++ b/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts @@ -88,6 +88,9 @@ export enum EventName { surveyReady = 'surveyReady', projectLoadingStart = 'projectLoadingStart', projectLoadingFinish = 'projectLoadingFinish', + createFileWatcher = 'createFileWatcher', + createDirectoryWatcher = 'createDirectoryWatcher', + closeFileWatcher = 'closeFileWatcher', } export enum OrganizeImportsMode { diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 52dcf5baa1939..fed9c1ec0f916 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -270,6 +270,7 @@ export class TypeScriptServerSpawner { args.push('--locale', TypeScriptServerSpawner.getTsLocale(configuration)); args.push('--noGetErrOnBackgroundUpdate'); + args.push('--canUseWatchEvents'); // TODO check ts version args.push('--validateDefaultNpmLocation'); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 6eb30e2098633..a35df36570f2c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -85,6 +85,7 @@ interface NoResponseTsServerRequests { 'compilerOptionsForInferredProjects': [Proto.SetCompilerOptionsForInferredProjectsArgs, null]; 'reloadProjects': [null, null]; 'configurePlugin': [Proto.ConfigurePluginRequest, Proto.ConfigurePluginResponse]; + 'watchChange': [Proto.Request, null]; } interface AsyncTsServerRequests { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 7553be7ed4990..9fa0c08144bf3 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -128,6 +128,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType private readonly versionProvider: ITypeScriptVersionProvider; private readonly processFactory: TsServerProcessFactory; + private readonly watches = new Map(); + constructor( private readonly context: vscode.ExtensionContext, onCaseInsenitiveFileSystem: boolean, @@ -973,6 +975,45 @@ export default class TypeScriptServiceClient extends Disposable implements IType case EventName.projectLoadingFinish: this.loadingIndicator.finishedLoadingProject((event as Proto.ProjectLoadingFinishEvent).body.projectName); break; + + case EventName.createDirectoryWatcher: + this.createFileSystemWatcher(event.body.id, new vscode.RelativePattern(vscode.Uri.file(event.body.path), event.body.recursive ? '**' : '*')); + break; + + case EventName.createFileWatcher: + this.createFileSystemWatcher(event.body.id, event.body.path); + break; + + case EventName.closeFileWatcher: + this.closeFileSystemWatcher(event.body.id); + break; + } + } + + private createFileSystemWatcher( + id: number, + pattern: vscode.GlobPattern, + ) { + const watcher = vscode.workspace.createFileSystemWatcher(pattern); + watcher.onDidChange(changeFile => + this.executeWithoutWaitingForResponse('watchChange', { id, path: changeFile.fsPath, eventType: 'update' }) + ); + watcher.onDidCreate(createFile => + this.executeWithoutWaitingForResponse('watchChange', { id, path: createFile.fsPath, eventType: 'create' }) + ); + watcher.onDidDelete(deletedFile => + this.executeWithoutWaitingForResponse('watchChange', { id, path: deletedFile.fsPath, eventType: 'delete' }) + ); + this.watches.set(id, watcher); + } + + private closeFileSystemWatcher( + id: number, + ) { + const existing = this.watches.get(id); + if (existing) { + existing.dispose(); + this.watches.delete(id); } } From 70e0ddd5a116bb51b7524bcb60f60294a9da5a31 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 11 Oct 2023 11:58:14 -0700 Subject: [PATCH 02/11] fix #195374 --- .../browser/terminal.accessibility.contribution.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index cca36e5c80ed3..649ef681da328 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -37,6 +37,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr static get(instance: ITerminalInstance): TextAreaSyncContribution | null { return instance.getContribution(TextAreaSyncContribution.ID); } + private _addon: TextAreaSyncAddon | undefined; constructor( private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, @@ -45,10 +46,13 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr ) { super(); } - xtermReady(xterm: IXtermTerminal & { raw: Terminal }): void { - const addon = this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities); - xterm.raw.loadAddon(addon); - addon.activate(xterm.raw); + layout(xterm: IXtermTerminal & { raw: Terminal }): void { + if (this._addon) { + return; + } + this._addon = this.add(this._instantiationService.createInstance(TextAreaSyncAddon, this._instance.capabilities)); + xterm.raw.loadAddon(this._addon); + this._addon.activate(xterm.raw); } } registerTerminalContribution(TextAreaSyncContribution.ID, TextAreaSyncContribution); From 648864cef9ad9c628cd84bfc98dd32f7cbcdb94b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 11 Oct 2023 12:10:40 -0700 Subject: [PATCH 03/11] must call canActivityBarBeHidden after state is set (#195392) --- src/vs/workbench/browser/layout.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 3f9a6c1b71b9a..4f33c7fc37d6f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -491,11 +491,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.stateModel.setRuntimeValue(LayoutStateKeys.EDITOR_HIDDEN, false); } - // Activity bar cannot be hidden - if (this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN) && !this.canActivityBarBeHidden()) { - this.stateModel.setRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, false); - } - this.stateModel.onDidChangeState(change => { if (change.key === LayoutStateKeys.ACTIVITYBAR_HIDDEN) { this.setActivityBarHidden(change.value as boolean); @@ -598,6 +593,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // Activity bar cannot be hidden + // This check must be called after state is set + // because canActivityBarBeHidden calls isVisible + if (this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN) && !this.canActivityBarBeHidden()) { + this.stateModel.setRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, false); + } + // Window border this.updateWindowBorder(true); } From 10d7700314bdacb2b73206b2a3cb8c557fca5037 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 11 Oct 2023 12:21:23 -0700 Subject: [PATCH 04/11] fix #195401 --- .../accessibility/browser/terminalAccessibilityHelp.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 07597055c0c35..7815d8776e88d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -76,6 +76,7 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc provideContent(): string { const content = []; content.push(this._descriptionForCommand(TerminalCommandId.FocusAccessibleBuffer, localize('focusAccessibleBuffer', 'The Focus Accessible Buffer ({0}) command enables screen readers to read terminal contents.'), localize('focusAccessibleBufferNoKb', 'The Focus Accessible Buffer command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.'))); + content.push(localize('preserveCursor', 'Customize the behavior of the cursor when toggling between the terminal and accessible view with `terminal.integrated.accessibleViewPreserveCursorPosition.`')); if (this._instance.shellType === WindowsShellType.CommandPrompt) { content.push(localize('commandPromptMigration', "Consider using powershell instead of command prompt for an improved experience")); } From 7479c6917213ca5005fc29502077893c1c8aefbd Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 11 Oct 2023 12:31:59 -0700 Subject: [PATCH 05/11] feat: render welcome message questions near input (#195405) --- src/vs/workbench/api/browser/mainThreadChat.ts | 3 +++ .../workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostChat.ts | 18 ++++++++++++++++++ .../contrib/chat/browser/chatWidget.ts | 2 ++ .../workbench/contrib/chat/common/chatModel.ts | 4 +++- .../contrib/chat/common/chatService.ts | 1 + .../contrib/chat/common/chatServiceImpl.ts | 5 ++++- .../contrib/chat/common/chatViewModel.ts | 1 + .../vscode.proposed.interactive.d.ts | 1 + 9 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChat.ts b/src/vs/workbench/api/browser/mainThreadChat.ts index 8ccce9f3b1fd5..630ea5116d29d 100644 --- a/src/vs/workbench/api/browser/mainThreadChat.ts +++ b/src/vs/workbench/api/browser/mainThreadChat.ts @@ -109,6 +109,9 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { provideWelcomeMessage: (token) => { return this._proxy.$provideWelcomeMessage(handle, token); }, + provideSampleQuestions: (token) => { + return this._proxy.$provideSampleQuestions(handle, token); + }, provideSlashCommands: (session, token) => { return this._proxy.$provideSlashCommands(handle, session.id, token); }, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1dbe62c996d82..710db4904443e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1241,6 +1241,7 @@ export interface MainThreadChatShape extends IDisposable { export interface ExtHostChatShape { $prepareChat(handle: number, initialState: any, token: CancellationToken): Promise; $provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IChatReplyFollowup[])[] | undefined>; + $provideSampleQuestions(handle: number, token: CancellationToken): Promise; $provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise; $provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise; $removeRequest(handle: number, sessionId: number, requestId: string): void; diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index b4b3ce2b22c99..efa6d30c49d9c 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -140,6 +140,24 @@ export class ExtHostChat implements ExtHostChatShape { return rawFollowups?.map(f => typeConvert.ChatFollowup.from(f)); } + async $provideSampleQuestions(handle: number, token: CancellationToken): Promise { + const entry = this._chatProvider.get(handle); + if (!entry) { + return undefined; + } + + if (!entry.provider.provideSampleQuestions) { + return undefined; + } + + const rawFollowups = await entry.provider.provideSampleQuestions(token); + if (!rawFollowups) { + return undefined; + } + + return rawFollowups?.map(f => typeConvert.ChatReplyFollowup.from(f)); + } + $removeRequest(handle: number, sessionId: number, requestId: string): void { const entry = this._chatProvider.get(handle); if (!entry) { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index ece9f0fd488c4..706319d7b6f3f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -264,6 +264,8 @@ export class ChatWidget extends Disposable implements IChatWidget { const lastItem = treeItems[treeItems.length - 1]?.element; if (lastItem && isResponseVM(lastItem) && lastItem.isComplete) { this.renderFollowups(lastItem.replyFollowups); + } else if (lastItem && isWelcomeVM(lastItem)) { + this.renderFollowups(lastItem.sampleQuestions); } else { this.renderFollowups(undefined); } diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index f9ea5dc62ccc7..58e534489e364 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -551,7 +551,7 @@ export class ChatModel extends Disposable implements IChatModel { if (obj.welcomeMessage) { const content = obj.welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item); - this._welcomeMessage = new ChatWelcomeMessageModel(this, content); + this._welcomeMessage = new ChatWelcomeMessageModel(this, content, []); } try { @@ -796,6 +796,7 @@ export type IChatWelcomeMessageContent = IMarkdownString | IChatReplyFollowup[]; export interface IChatWelcomeMessageModel { readonly id: string; readonly content: IChatWelcomeMessageContent[]; + readonly sampleQuestions: IChatReplyFollowup[]; readonly username: string; readonly avatarIconUri?: URI; @@ -812,6 +813,7 @@ export class ChatWelcomeMessageModel implements IChatWelcomeMessageModel { constructor( private readonly session: ChatModel, public readonly content: IChatWelcomeMessageContent[], + public readonly sampleQuestions: IChatReplyFollowup[] ) { this._id = 'welcome_' + ChatWelcomeMessageModel.nextId++; } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 73a0b7a7381b4..3d280772e1ecf 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -108,6 +108,7 @@ export interface IChatProvider { readonly iconUrl?: string; prepareSession(initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult; provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IChatReplyFollowup[])[] | undefined>; + provideSampleQuestions?(token: CancellationToken): ProviderResult; provideFollowups?(session: IChat, token: CancellationToken): ProviderResult; provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult; provideSlashCommands?(session: IChat, token: CancellationToken): ProviderResult; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 1ffbac4491625..14ed59883499d 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -362,7 +362,10 @@ export class ChatService extends Disposable implements IChatService { const welcomeMessage = model.welcomeMessage ? undefined : await provider.provideWelcomeMessage?.(token) ?? undefined; const welcomeModel = welcomeMessage && new ChatWelcomeMessageModel( - model, welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[])); + model, + welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[]), + await provider.provideSampleQuestions?.(token) ?? [] + ); model.initialize(session, welcomeModel); } catch (err) { diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 10cfa2b719b4b..09aa29178b2a7 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -373,5 +373,6 @@ export interface IChatWelcomeMessageViewModel { readonly username: string; readonly avatarIconUri?: URI; readonly content: IChatWelcomeMessageContent[]; + readonly sampleQuestions: IChatReplyFollowup[]; currentRenderedHeight?: number; } diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index e9161140c3365..a3c2fd927bf49 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -204,6 +204,7 @@ declare module 'vscode' { export interface InteractiveSessionProvider { provideWelcomeMessage?(token: CancellationToken): ProviderResult; + provideSampleQuestions?(token: CancellationToken): ProviderResult; provideFollowups?(session: S, token: CancellationToken): ProviderResult<(string | InteractiveSessionFollowup)[]>; provideSlashCommands?(session: S, token: CancellationToken): ProviderResult; From 60310a66e475b8d2e2f34d1d2ea4864e05bc28af Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 11 Oct 2023 14:20:11 -0700 Subject: [PATCH 06/11] fix #195280 --- .../contrib/accessibility/browser/accessibleViewActions.ts | 4 ++-- .../contrib/chat/browser/actions/chatCodeblockActions.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 0721b08a570ab..23bea8fe3a28b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -184,8 +184,8 @@ class AccessibleViewAcceptInlineCompletionAction extends Action2 { id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletion, precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.InlineCompletions)), keybinding: { - primary: KeyMod.CtrlCmd | KeyCode.Slash, - mac: { primary: KeyMod.WinCtrl | KeyCode.Slash }, + primary: KeyMod.CtrlCmd | KeyCode.Enter, + mac: { primary: KeyMod.WinCtrl | KeyCode.Enter }, weight: KeybindingWeight.WorkbenchContrib }, icon: Codicon.check, diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 6899e66a113b5..58a5874b726ff 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -180,7 +180,12 @@ export function registerChatCodeBlockActions() { menu: { id: MenuId.ChatCodeBlock, group: 'navigation', - } + }, + keybinding: { + primary: KeyMod.CtrlCmd | KeyCode.Enter, + mac: { primary: KeyMod.WinCtrl | KeyCode.Enter }, + weight: KeybindingWeight.WorkbenchContrib + }, }); } From ebf16fa676b806ee7a3655d0038f8812556d1ba0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 11 Oct 2023 14:26:43 -0700 Subject: [PATCH 07/11] fix #195281 Open --- .../contrib/chat/browser/actions/chatCodeblockActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 6899e66a113b5..9b9c6d130c959 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -474,7 +474,8 @@ export function registerChatCodeBlockActions() { original: 'Next Code Block' }, keybinding: { - primary: KeyCode.F9, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, }, weight: KeybindingWeight.WorkbenchContrib, when: CONTEXT_IN_CHAT_SESSION, }, @@ -498,7 +499,8 @@ export function registerChatCodeBlockActions() { original: 'Previous Code Block' }, keybinding: { - primary: KeyMod.Shift | KeyCode.F9, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, }, weight: KeybindingWeight.WorkbenchContrib, when: CONTEXT_IN_CHAT_SESSION, }, From a15b22924c06bd9c12de559dd8e9061ae42c0737 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 11 Oct 2023 14:45:25 -0700 Subject: [PATCH 08/11] other approach --- .../chat/browser/actions/chatCodeblockActions.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 58a5874b726ff..ac4e4cb76d76a 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -17,8 +17,10 @@ import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { CopyAction } from 'vs/editor/contrib/clipboard/browser/clipboard'; import { localize } from 'vs/nls'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor'; @@ -182,6 +184,7 @@ export function registerChatCodeBlockActions() { group: 'navigation', }, keybinding: { + when: CONTEXT_ACCESSIBILITY_MODE_ENABLED, primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { primary: KeyMod.WinCtrl | KeyCode.Enter }, weight: KeybindingWeight.WorkbenchContrib @@ -388,14 +391,20 @@ export function registerChatCodeBlockActions() { group: 'navigation', isHiddenByDefault: true, }, - keybinding: { + keybinding: [{ primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter, mac: { primary: KeyMod.WinCtrl | KeyCode.Enter, }, weight: KeybindingWeight.EditorContrib, - when: CONTEXT_IN_CHAT_SESSION - } + when: ContextKeyExpr.and(CONTEXT_IN_CHAT_SESSION, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()), + }, + { + primary: KeyMod.CtrlCmd | KeyCode.Slash, + mac: { primary: KeyMod.WinCtrl | KeyCode.Slash }, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(CONTEXT_IN_CHAT_SESSION, CONTEXT_ACCESSIBILITY_MODE_ENABLED), + }] }); } From b97005b9e08afca8be16df9879636916e62c23e8 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 11 Oct 2023 11:15:56 -0700 Subject: [PATCH 09/11] no need for cell output to update the editor height --- .../contrib/notebook/browser/view/cellParts/cellOutput.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index b675ee6250c39..39491a8f5c925 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -716,9 +716,6 @@ export class CellOutputContainer extends CellContentPart { DOM.hide(this.templateData.outputShowMoreContainer.domNode); } - const editorHeight = this.templateData.editor.getContentHeight(); - this.viewCell.editorHeight = editorHeight; - this._relayoutCell(); // if it's clearing all outputs, or outputs are all rendered synchronously // shrink immediately as the final output height will be zero. From 901ac65ea9f906d6996ad8e7639ba81acecf1d7c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 11 Oct 2023 20:23:51 -0700 Subject: [PATCH 10/11] Implement chatAgent2 proposal (#194635) * Add notes on chat agent API * Add request ID to context * variables * Add partial implementation for another option for a chat agent API * update * Notes from api sync * More notes * Can invoke an agent and get the response * Provide a real request * Notes * add `slashCommandProvider` - not yet hooked up * add metadata properties inline, some comments * some more notes * Put the new API side-by-side with the old one * Fix agent title in response * Fix agent display * Send slashCommand to request * Hook up variables * Get rid of package.json registration option * Start to implement followups provider * Add comment * make it `slashCommandProvider` all the way, use updateAgent for updates icon, fullName, description * update docs * only ask for slash command completions when completing a slash-word * use complex completion item label for command/agent completions * add `promptText` to `IParsedChatRequestPart` so that some parts don't make it into the prompt (like agent and slash commands) * only allow agent and slash command at the beginning of the prompt * remove unused method * some jsdoc, many renames so that stuff starts with `ChatAgent...` * reduce `createChatAgent` to the minimum, let the rest be set via setters * in the renderer know if an agent has slash command and follow ups, safes IPC calls * use `iconPath` to align with other APIs * more jsdoc and more obvious TODOs * fix chat parser with "late" command * handle error so that the request stops. where is the rendering tho? * Show error message in response properly * Don't blow up global / list * Change proposal name * Inline followup types * fix type * Remove brace in error msg --------- Co-authored-by: Johannes --- build/lib/compilation.js | 4 +- build/lib/compilation.ts | 2 +- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadChatAgents.ts | 49 ++-- .../api/browser/mainThreadChatAgents2.ts | 80 ++++++ .../workbench/api/common/extHost.api.impl.ts | 7 +- .../workbench/api/common/extHost.protocol.ts | 24 +- src/vs/workbench/api/common/extHostChat.ts | 2 +- .../api/common/extHostChatAgents2.ts | 252 ++++++++++++++++++ .../contrib/chat/browser/chatVariables.ts | 8 +- .../browser/contrib/chatInputEditorContrib.ts | 32 ++- .../contrib/chat/common/chatAgents.ts | 179 ++++--------- .../contrib/chat/common/chatModel.ts | 8 +- .../contrib/chat/common/chatParserTypes.ts | 29 +- .../contrib/chat/common/chatRequestParser.ts | 30 ++- .../contrib/chat/common/chatServiceImpl.ts | 29 +- .../ChatRequestParser_agent_not_first.0.snap | 14 +- ...uestParser_agent_with_question_mark.0.snap | 32 +-- .../ChatRequestParser_agents.0.snap | 10 +- ...hatRequestParser_agents__subCommand.0.snap | 68 +++++ ..._agents_and_variables_and_multiline.0.snap | 49 ++-- ..._and_variables_and_multiline__part2.0.snap | 109 ++++++++ .../test/common/chatRequestParser.test.ts | 37 ++- .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.chatAgents2.d.ts | 146 ++++++++++ .../vscode.proposed.interactive.d.ts | 2 + ...scode.proposed.interactiveUserActions.d.ts | 4 + 27 files changed, 956 insertions(+), 252 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadChatAgents2.ts create mode 100644 src/vs/workbench/api/common/extHostChatAgents2.ts create mode 100644 src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents__subCommand.0.snap create mode 100644 src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline__part2.0.snap create mode 100644 src/vscode-dts/vscode.proposed.chatAgents2.d.ts diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 64e27dcf45c28..5fecfc82ca385 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -237,7 +237,7 @@ function generateApiProposalNames() { catch { eol = os.EOL; } - const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; + const pattern = /vscode\.proposed\.([a-zA-Z\d]+)\.d\.ts$/; const proposalNames = new Set(); const input = es.through(); const output = input @@ -287,4 +287,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp.dest('src')); }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxDQUFDLENBQUMsc0NBQXNDO1FBQy9FLE9BQU8sQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQzNCLENBQUM7SUFDRCxPQUFPLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUMxQixPQUFPLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUMxQixPQUFPLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0MsT0FBTyxDQUFDLE9BQU8sR0FBRyxNQUFN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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb21waWxhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUVoRyxtQ0FBbUM7QUFDbkMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsMENBQTBDO0FBQzFDLDZCQUE2QjtBQUM3Qix5Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHNDQUFzQztBQUN0QywwQ0FBMEM7QUFDMUMseUJBQXlCO0FBQ3pCLGlDQUFrQztBQUNsQyw4QkFBOEI7QUFDOUIsK0JBQStCO0FBQy9CLDBDQUF5QztBQUV6QyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFHakMsdUVBQXVFO0FBRXZFLE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQWMsR0FBRSxDQUFDO0FBRWxDLFNBQVMsNEJBQTRCLENBQUMsR0FBVztJQUNoRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQXVCLEVBQUUsQ0FBQztJQUN2QyxPQUFPLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUN4QixPQUFPLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQztJQUN6QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsRUFBRSxDQUFDLENBQUMsc0NBQXNDO1FBQy9FLE9BQU8sQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQzNCLENBQUM7SUFDRCxPQUFPLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUMxQixPQUFPLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUMxQixPQUFPLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDN0MsT0FBTyxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNFLE9BQU8sT0FBTyxDQUFDO0FBQ2hCLENBQUM7QUFFRCxTQUFTLGFBQWEsQ0FBQyxHQUFXLEVBQUUsS0FBYyxFQUFFLFNBQWtCLEVBQUUsYUFBeUM7SUFDaEgsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBMkIsQ0FBQztJQUN2RCxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsaUJBQWlCLENBQXFDLENBQUM7SUFHbEYsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxlQUFlLENBQUMsQ0FBQztJQUN6RSxNQUFNLGVBQWUsR0FBRyxFQUFFLEdBQUcsNEJBQTRCLENBQUMsR0FBRyxDQUFDLEVBQUUsYUFBYSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO0lBQ2hHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNaLGVBQWUsQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDO0lBQ3hDLENBQUM7SUFFRCxNQUFNLFdBQVcsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxlQUFlLEVBQUU7UUFDNUQsT0FBTyxFQUFFLEtBQUs7UUFDZCxhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQztRQUNyQyxnQkFBZ0IsRUFBRSxPQUFPLGFBQWEsS0FBSyxTQUFTLElBQUksYUFBYSxDQUFDLEdBQUc7S0FDekUsRUFBRSxHQUFHLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBRXpCLFNBQVMsUUFBUSxDQUFDLEtBQStCO1FBQ2hELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQThCLENBQUM7UUFFN0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDOUQsTUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFPLEVBQUUsRUFBRSxDQUFDLDBCQUEwQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEUsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDeEYsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVoRixNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDM0IsTUFBTSxNQUFNLEdBQUcsS0FBSzthQUNsQixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLHlFQUF5RTthQUMzRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssSUFBSSxXQUFXLEVBQUUsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUMsQ0FBQzthQUNwRSxJQUFJLENBQUMsUUFBUSxDQUFDO2FBQ2QsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQzthQUMzQixJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQ3hCLElBQUksQ0FBQyxvQkFBb0IsQ0FBQzthQUMxQixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7YUFDaEMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLE9BQU8sQ0FBQzthQUNsQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLGFBQWEsRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRTtZQUNwRCxVQUFVLEVBQUUsS0FBSztZQUNqQixjQUFjLEVBQUUsQ0FBQyxDQUFDLEtBQUs7WUFDdkIsVUFBVSxFQUFFLGVBQWUsQ0FBQyxVQUFVO1NBQ3RDLENBQUMsQ0FBQyxDQUFDO2FBQ0gsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7YUFDdEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFFbEMsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBQ0QsUUFBUSxDQUFDLFlBQVksR0FBRyxHQUFHLEVBQUU7UUFDNUIsT0FBTyxXQUFXLENBQUMsR0FBRyxDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDdkMsQ0FBQyxDQUFDO0lBQ0YsUUFBUSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7SUFDbkMsT0FBTyxRQUFRLENBQUM7QUFDakIsQ0FBQztBQUVELFNBQWdCLGFBQWEsQ0FBQyxHQUFXLEVBQUUsR0FBVyxFQUFFLEdBQVk7SUFFbkUsTUFBTSxJQUFJLEdBQUcsR0FBRyxFQUFFO1FBRWpCLE1BQU0sU0FBUyxHQUFHLGFBQWEsQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDM0QsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRTFELE9BQU8sT0FBTzthQUNaLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQzthQUNqQixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3hCLENBQUMsQ0FBQztJQUVGLElBQUksQ0FBQyxRQUFRLEdBQUcsYUFBYSxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7SUFDbEQsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBZEQsc0NBY0M7QUFFRCxTQUFnQixXQUFXLENBQUMsR0FBVyxFQUFFLEdBQVcsRUFBRSxLQUFjLEVBQUUsVUFBdUMsRUFBRTtJQUU5RyxNQUFNLElBQUksR0FBRyxHQUFHLEVBQUU7UUFFakIsSUFBSSxFQUFFLENBQUMsUUFBUSxFQUFFLEdBQUcsVUFBYSxFQUFFLENBQUM7WUFDbkMsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3BELENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDdkQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzFELE1BQU0sU0FBUyxHQUFHLElBQUksZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzdDLElBQUksR0FBRyxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ25CLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNyQixDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLElBQUksWUFBWSxHQUFHLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNoQyxJQUFJLEtBQUssSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUNyQyxJQUFJLFlBQVksR0FBRyxJQUFJLGVBQU8sQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUMsR0FBRyxJQUFJLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLG1CQUFtQixFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDdEssTUFBTSxxQkFBcUIsR0FBRyxZQUFZLENBQUMsc0JBQXNCLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDMUYsWUFBWSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxVQUFVLEtBQUssQ0FBQyxJQUF5QztnQkFFdkYsTUFBTSxZQUFZLEdBQW1CLEVBQUcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNsRSxNQUFNLFdBQVcsR0FBRyxDQUFDLE1BQU0scUJBQXFCLENBQUMsQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQ3BFLElBQUksV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUMvQixJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO29CQUM3QyxJQUFJLENBQUMsU0FBUyxHQUFHLFdBQVcsQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUM7Z0JBQzdFLENBQUM7Z0JBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNqQixDQUFDLEVBQUUsS0FBSyxVQUFVLEdBQUc7Z0JBQ3BCLGlCQUFpQjtnQkFDakIsQ0FBQyxNQUFNLHFCQUFxQixDQUFDLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBRXRDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ1YsWUFBYSxHQUFHLFNBQVMsQ0FBQztZQUNqQyxDQUFDLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxPQUFPLE9BQU87YUFDWixJQUFJLENBQUMsWUFBWSxDQUFDO2FBQ2xCLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDO2FBQ3RCLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUNmLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDeEIsQ0FBQyxDQUFDO0lBRUYsSUFBSSxDQUFDLFFBQVEsR0FBRyxXQUFXLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztJQUNoRCxPQUFPLElBQUksQ0FBQztBQUNiLENBQUM7QUEvQ0Qsa0NBK0NDO0FBRUQsU0FBZ0IsU0FBUyxDQUFDLEdBQVcsRUFBRSxLQUFjO0lBRXBELE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRTtRQUNqQixNQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFMUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNoRCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsUUFBUSxFQUFFLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUVsRSxNQUFNLFNBQVMsR0FBRyxJQUFJLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM1QyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFcEIsT0FBTyxRQUFRO2FBQ2IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUM7YUFDdEIsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQzthQUMxQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3hCLENBQUMsQ0FBQztJQUNGLElBQUksQ0FBQyxRQUFRLEdBQUcsU0FBUyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7SUFDOUMsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBbEJELDhCQWtCQztBQUVELE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxDQUFDO0FBRTFELE1BQU0sZUFBZTtJQUNILFFBQVEsQ0FBVTtJQUNuQixNQUFNLENBQXlCO0lBRTlCLGFBQWEsQ0FBa0M7SUFDL0MsV0FBVyxDQUF1QjtJQUNsQyxvQkFBb0IsQ0FBZ0M7SUFFckUsWUFBWSxPQUFnQjtRQUMzQixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQztRQUN4QixJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixJQUFJLENBQUMsYUFBYSxHQUFHLEVBQUUsQ0FBQztRQUN4QixNQUFNLGNBQWMsR0FBRyxDQUFDLFFBQWdCLEVBQUUsUUFBZ0IsRUFBRSxFQUFFO1lBQzdELElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ3BCLE9BQU87WUFDUixDQUFDO1lBQ0QsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7Z0JBQ2xDLE9BQU87WUFDUixDQUFDO1lBQ0QsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsR0FBRyxJQUFJLENBQUM7WUFFcEMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFO2dCQUMzQixJQUFJLENBQUMsb0JBQW9CLENBQUMsZUFBZSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUNwRCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDckIsQ0FBQyxDQUFDLENBQUM7UUFDSixDQUFDLENBQUM7UUFDRixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksS0FBTSxTQUFRLFNBQVMsQ0FBQyxVQUFVO1lBQ2pELFlBQVksQ0FBQyxRQUFnQixFQUFFLFFBQWdCO2dCQUNyRCxjQUFjLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDO2dCQUNuQyxPQUFPLEtBQUssQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQy9DLENBQUM7U0FDRCxDQUFDO1FBQ0YsSUFBSSxDQUFDLG9CQUFvQixHQUFHLElBQUksU0FBUyxDQUFDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUVoRixJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNuQixFQUFFLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsR0FBRyxFQUFFO2dCQUN4QyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDckIsQ0FBQyxDQUFDLENBQUM7UUFDSixDQUFDO0lBQ0YsQ0FBQztJQUVPLGlCQUFpQixHQUF3QixJQUFJLENBQUM7SUFDOUMsWUFBWTtRQUNuQixJQUFJLElBQUksQ0FBQyxpQkFBaUIsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUNyQyxZQUFZLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztRQUMvQixDQUFDO1FBQ0QsSUFBSSxDQUFDLGlCQUFpQixHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDeEMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQztZQUM5QixJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDaEIsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ1IsQ0FBQztJQUVPLElBQUk7UUFDWCxNQUFNLENBQUMsR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDMUIsNERBQTREO1lBQzVELE1BQU0sSUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBQ0QsT0FBTyxDQUFDLENBQUM7SUFDVixDQUFDO0lBRU8sSUFBSSxDQUFDLE9BQVksRUFBRSxHQUFHLElBQVc7UUFDeEMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVNLE9BQU87UUFDYixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNiLHlCQUF5QjtZQUN6QixPQUFPO1FBQ1IsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ3RCLE9BQU87UUFDUixDQUFDO1FBRUQsRUFBRSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsRCxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLGdEQUFnRCxDQUFDLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzdHLElBQUksQ0FBQyxJQUFJLENBQUMsNENBQTRDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLEtBQUssQ0FBQyxDQUFDO1FBQ25GLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDcEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLHFGQUFxRixDQUFDLENBQUM7UUFDbEgsQ0FBQztJQUNGLENBQUM7Q0FDRDtBQUVELFNBQVMsd0JBQXdCO0lBQ2hDLElBQUksR0FBVyxDQUFDO0lBRWhCLElBQUksQ0FBQztRQUNKLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsdUVBQXVFLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDOUcsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNqQyxHQUFHLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUM7SUFDakMsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNSLEdBQUcsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDO0lBQ2QsQ0FBQztJQUVELE1BQU0sT0FBTyxHQUFHLHlDQUF5QyxDQUFDO0lBQzFELE1BQU0sYUFBYSxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFFeEMsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQzNCLE1BQU0sTUFBTSxHQUFHLEtBQUs7U0FDbEIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7U0FDcEQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFPLEVBQUUsRUFBRTtRQUM1QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuQyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRWpDLElBQUksS0FBSyxFQUFFLENBQUM7WUFDWCxhQUFhLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzdCLENBQUM7SUFDRixDQUFDLEVBQUU7UUFDRixNQUFNLEtBQUssR0FBRyxDQUFDLEdBQUcsYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDakQsTUFBTSxRQUFRLEdBQUc7WUFDaEIsaUdBQWlHO1lBQ2pHLCtEQUErRDtZQUMvRCxrR0FBa0c7WUFDbEcsa0dBQWtHO1lBQ2xHLEVBQUU7WUFDRixvREFBb0Q7WUFDcEQsRUFBRTtZQUNGLGdEQUFnRDtZQUNoRCxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxLQUFLLElBQUksNkZBQTZGLElBQUksUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxFQUFFLENBQUMsRUFBRTtZQUMxSixLQUFLO1lBQ0wsNkRBQTZEO1lBQzdELEVBQUU7U0FDRixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUVaLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksSUFBSSxDQUFDO1lBQzFCLElBQUksRUFBRSxtRUFBbUU7WUFDekUsUUFBUSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO1NBQy9CLENBQUMsQ0FBQyxDQUFDO1FBQ0osSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsQixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRUwsT0FBTyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztBQUNqQyxDQUFDO0FBRUQsTUFBTSx3QkFBd0IsR0FBRyxJQUFBLHlCQUFjLEVBQUMsb0JBQW9CLENBQUMsQ0FBQztBQUV6RCxRQUFBLDJCQUEyQixHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsNEJBQTRCLEVBQUUsR0FBRyxFQUFFO0lBQ3pGLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsQ0FBQztTQUNsQyxJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztTQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUN0QixJQUFJLENBQUMsd0JBQXdCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDNUMsQ0FBQyxDQUFDLENBQUM7QUFFVSxRQUFBLHlCQUF5QixHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsMEJBQTBCLEVBQUUsR0FBRyxFQUFFO0lBQ3JGLE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsbUJBQW1CLENBQUM7U0FDOUMsSUFBSSxDQUFDLHdCQUF3QixFQUFFLENBQUM7U0FDaEMsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNDLE9BQU8sS0FBSyxDQUFDLG1CQUFtQixFQUFFLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDO1NBQ25ELElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDMUIsQ0FBQyxDQUFDLENBQUMifQ== \ No newline at end of file diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index cf2ab921f1c4c..ebc9dedf2e564 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -277,7 +277,7 @@ function generateApiProposalNames() { eol = os.EOL; } - const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; + const pattern = /vscode\.proposed\.([a-zA-Z\d]+)\.d\.ts$/; const proposalNames = new Set(); const input = es.through(); diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 4f886cbde2c6e..85063e0987a61 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -21,6 +21,7 @@ import './mainThreadLocalization'; import './mainThreadBulkEdits'; import './mainThreadChatProvider'; import './mainThreadChatAgents'; +import './mainThreadChatAgents2'; import './mainThreadChatVariables'; import './mainThreadCodeInsets'; import './mainThreadCLICommands'; diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents.ts b/src/vs/workbench/api/browser/mainThreadChatAgents.ts index 52b8106cba8b9..82e0f735c6dd3 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents.ts @@ -7,7 +7,8 @@ import { DisposableMap } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { IProgress } from 'vs/platform/progress/common/progress'; import { ExtHostChatAgentsShape, ExtHostContext, MainContext, MainThreadChatAgentsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IChatAgentMetadata, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentCommand, IChatAgentMetadata, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -16,7 +17,7 @@ import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/ext export class MainThreadChatAgents implements MainThreadChatAgentsShape { private readonly _agents = new DisposableMap; - private readonly _pendingProgress = new Map>(); + private readonly _pendingProgress = new Map>(); private readonly _proxy: ExtHostChatAgentsShape; constructor( @@ -34,29 +35,37 @@ export class MainThreadChatAgents implements MainThreadChatAgentsShape { this._agents.clearAndDisposeAll(); } - $registerAgent(handle: number, name: string, metadata: IChatAgentMetadata): void { - if (!this._chatAgentService.hasAgent(name)) { - // dynamic! - this._chatAgentService.registerAgentData({ - id: name, - metadata: revive(metadata) - }); - } - - const d = this._chatAgentService.registerAgentCallback(name, async (prompt, progress, history, token) => { - const requestId = Math.random(); - this._pendingProgress.set(requestId, progress); - try { - return await this._proxy.$invokeAgent(handle, requestId, prompt, { history }, token); - } finally { - this._pendingProgress.delete(requestId); - } + $registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void { + const d = this._chatAgentService.registerAgent({ + id: name, + metadata: revive(metadata), + invoke: async (request, progress, history, token) => { + const requestId = Math.random(); + this._pendingProgress.set(requestId, progress); + try { + const result = await this._proxy.$invokeAgent(handle, requestId, request.message, { history }, token); + return { + followUp: result?.followUp ?? [], + }; + } finally { + this._pendingProgress.delete(requestId); + } + }, + async provideSlashCommands() { + return metadata.subCommands; + }, }); this._agents.set(handle, d); } async $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise { - this._pendingProgress.get(requestId)?.report(revive(chunk)); + // An extra step because TS really struggles with type inference in the Revived generic parameter? + const revived = revive(chunk); + if (typeof revived.content === 'string') { + this._pendingProgress.get(requestId)?.report({ content: revived.content }); + } else { + this._pendingProgress.get(requestId)?.report(revived.content); + } } $unregisterCommand(handle: number): void { diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts new file mode 100644 index 0000000000000..1e389f6ff7b57 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableMap, IDisposable } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { ExtHostChatAgentsShape2, ExtHostContext, IChatResponseProgressDto, IExtensionChatAgentMetadata, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; + + +type AgentData = { + dispose: () => void; + name: string; + hasSlashCommands?: boolean; + hasFollowups?: boolean; +}; + +@extHostNamedCustomer(MainContext.MainThreadChatAgents2) +export class MainThreadChatAgents implements MainThreadChatAgentsShape2, IDisposable { + + private readonly _agents = new DisposableMap; + private readonly _pendingProgress = new Map>(); + private readonly _proxy: ExtHostChatAgentsShape2; + + constructor( + extHostContext: IExtHostContext, + @IChatAgentService private readonly _chatAgentService: IChatAgentService + ) { + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatAgents2); + } + + $unregisterAgent(handle: number): void { + this._agents.deleteAndDispose(handle); + } + + dispose(): void { + this._agents.clearAndDisposeAll(); + } + + $registerAgent(handle: number, name: string, metadata: IExtensionChatAgentMetadata): void { + const d = this._chatAgentService.registerAgent({ + id: name, + metadata: revive(metadata), + invoke: async (request, progress, history, token) => { + const requestId = Math.random(); // Make this a guid + this._pendingProgress.set(requestId, progress); + try { + return await this._proxy.$invokeAgent(handle, requestId, request, { history }, token) ?? {}; + } finally { + this._pendingProgress.delete(requestId); + } + }, + provideSlashCommands: async (token) => { + if (!this._agents.get(handle)?.hasSlashCommands) { + return []; // safe an IPC call + } + return this._proxy.$provideSlashCommands(handle, token); + } + }); + this._agents.set(handle, { name, dispose: d.dispose, hasSlashCommands: metadata.hasSlashCommands }); + } + + $updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void { + const data = this._agents.get(handle); + if (!data) { + throw new Error(`No agent with handle ${handle} registered`); + } + data.hasSlashCommands = metadataUpdate.hasSlashCommands; + this._chatAgentService.updateAgent(data.name, revive(metadataUpdate)); + } + + async $handleProgressChunk(requestId: number, chunk: IChatResponseProgressDto): Promise { + // TODO copy/move $acceptResponseProgress from MainThreadChat + this._pendingProgress.get(requestId)?.report(revive(chunk) as any); + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 8840c9eb225bd..a894d5c01ce52 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -108,6 +108,7 @@ import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariabl import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation'; import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector'; import { ExtHostChatAgents } from 'vs/workbench/api/common/extHostChatAgents'; +import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -209,6 +210,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService)); const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService)); const extHostChatAgents = rpcProtocol.set(ExtHostContext.ExtHostChatAgents, new ExtHostChatAgents(rpcProtocol, extHostChatProvider, extHostLogService)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); @@ -1366,11 +1368,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'mappedEditsProvider'); return extHostLanguageFeatures.registerMappedEditsProvider(extension, selector, provider); }, + createChatAgent(name: string, handler: vscode.ChatAgentHandler) { + checkProposedApiEnabled(extension, 'chatAgents2'); + return extHostChatAgents2.createChatAgent(extension.identifier, name, handler); + }, registerAgent(name: string, agent: vscode.ChatAgent, metadata: vscode.ChatAgentMetadata) { checkProposedApiEnabled(extension, 'chatAgents'); return extHostChatAgents.registerAgent(extension.identifier, name, agent, metadata); } - }; return { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 710db4904443e..48d318c37956f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -50,7 +50,7 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks'; import { SaveReason } from 'vs/workbench/common/editor'; import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; -import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; @@ -1147,15 +1147,33 @@ export interface ExtHostChatProviderShape { } export interface MainThreadChatAgentsShape extends IDisposable { - $registerAgent(handle: number, name: string, metadata: IChatAgentMetadata): void; + $registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void; $unregisterAgent(handle: number): void; $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise; } +export interface IExtensionChatAgentMetadata extends Dto { + hasSlashCommands?: boolean; + hasFollowup?: boolean; +} + +export interface MainThreadChatAgentsShape2 extends IDisposable { + $registerAgent(handle: number, name: string, metadata: IExtensionChatAgentMetadata): void; + $updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void; + $unregisterAgent(handle: number): void; + $handleProgressChunk(requestId: number, chunk: IChatResponseProgressDto): Promise; +} + export interface ExtHostChatAgentsShape { $invokeAgent(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise; } +export interface ExtHostChatAgentsShape2 { + $invokeAgent(handle: number, requestId: number, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise; + $provideSlashCommands(handle: number, token: CancellationToken): Promise; + $provideFollowups(handle: number, requestId: number, token: CancellationToken): Promise; +} + export interface MainThreadChatVariablesShape extends IDisposable { $registerVariable(handle: number, data: IChatVariableData): void; $unregisterVariable(handle: number): void; @@ -2665,6 +2683,7 @@ export const MainContext = { MainThreadBulkEdits: createProxyIdentifier('MainThreadBulkEdits'), MainThreadChatProvider: createProxyIdentifier('MainThreadChatProvider'), MainThreadChatAgents: createProxyIdentifier('MainThreadChatAgents'), + MainThreadChatAgents2: createProxyIdentifier('MainThreadChatAgents2'), MainThreadChatVariables: createProxyIdentifier('MainThreadChatVariables'), MainThreadClipboard: createProxyIdentifier('MainThreadClipboard'), MainThreadCommands: createProxyIdentifier('MainThreadCommands'), @@ -2786,6 +2805,7 @@ export const ExtHostContext = { ExtHostInlineChat: createProxyIdentifier('ExtHostInlineChatShape'), ExtHostChat: createProxyIdentifier('ExtHostChat'), ExtHostChatAgents: createProxyIdentifier('ExtHostChatAgents'), + ExtHostChatAgents2: createProxyIdentifier('ExtHostChatAgents'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), ExtHostAiRelatedInformation: createProxyIdentifier('ExtHostAiRelatedInformation'), diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index efa6d30c49d9c..683cff67437bf 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -278,7 +278,7 @@ export class ExtHostChat implements ExtHostChatShape { } async $onDidPerformUserAction(event: IChatUserActionEvent): Promise { - this._onDidPerformUserAction.fire(event); + this._onDidPerformUserAction.fire(event as any); } //#endregion diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts new file mode 100644 index 0000000000000..365d4d0d91dfa --- /dev/null +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DeferredPromise, raceCancellation } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { assertType } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Progress } from 'vs/platform/progress/common/progress'; +import { ExtHostChatAgentsShape2, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; +import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; +import { IChatAgentCommand, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import type * as vscode from 'vscode'; + +export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { + + private static _idPool = 0; + + private readonly _agents = new Map(); + private readonly _proxy: MainThreadChatAgentsShape2; + + constructor( + mainContext: IMainContext, + private readonly _extHostChatProvider: ExtHostChatProvider, + private readonly _logService: ILogService, + ) { + this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); + } + + createChatAgent(extension: ExtensionIdentifier, name: string, handler: vscode.ChatAgentHandler): vscode.ChatAgent2 { + const handle = ExtHostChatAgents2._idPool++; + const agent = new ExtHostChatAgent(extension, name, this._proxy, handle, handler); + this._agents.set(handle, agent); + + this._proxy.$registerAgent(handle, name, {}); + return agent.apiAgent; + } + + async $invokeAgent(handle: number, requestId: number, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise { + const agent = this._agents.get(handle); + if (!agent) { + throw new Error(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`); + } + + 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(), 3 * 1000); + this._extHostChatProvider.allowListExtensionWhile(agent.extension, commandExecution.p); + + const slashCommand = request.command + ? await agent.validateSlashCommand(request.command) + : undefined; + + + try { + + const task = agent.invoke( + { prompt: request.message, variables: {}, slashCommand }, + { history: context.history.map(typeConvert.ChatMessage.to) }, + new Progress(p => { + throwIfDone(); + const convertedProgress = typeConvert.ChatResponseProgress.from(p); + this._proxy.$handleProgressChunk(requestId, convertedProgress); + }), + token + ); + + return await raceCancellation(Promise.resolve(task).then((result) => { + if (result) { + // An option would be to call provideFollowups here and send the result back to the renderer, rather than store the result + // and wait for the renderer to ask for followups + // agent.provideFollowups(result, token); + return { errorDetails: result.errorDetails }; // TODO timings here + } + + return undefined; + }), token); + + } catch (e) { + this._logService.error(e, agent.extension); + return { + errorDetails: { + message: toErrorMessage(e) + } + }; + + } finally { + done = true; + commandExecution.complete(); + } + } + + async $provideSlashCommands(handle: number, token: CancellationToken): Promise { + const agent = this._agents.get(handle); + if (!agent) { + // this is OK, the agent might have disposed while the request was in flight + return []; + } + return agent.provideSlashCommand(token); + } + + async $provideFollowups(handle: number, requestId: number, token: CancellationToken): Promise { + const agent = this._agents.get(handle); + if (!agent) { + // this is OK, the agent might have disposed while the request was in flight + return []; + } + + // TODO look up result object based on requestId + return agent.provideFollowups(null!, token); + } +} + +class ExtHostChatAgent { + + private _slashCommandProvider: vscode.ChatAgentSlashCommandProvider | undefined; + private _lastSlashCommands: vscode.ChatAgentSlashCommand[] | undefined; + private _followupProvider: vscode.FollowupProvider | undefined; + private _description: string | undefined; + private _fullName: string | undefined; + private _iconPath: URI | undefined; + + constructor( + public readonly extension: ExtensionIdentifier, + private readonly _id: string, + private readonly _proxy: MainThreadChatAgentsShape2, + private readonly _handle: number, + private readonly _callback: vscode.ChatAgentHandler, + ) { } + + + async validateSlashCommand(command: string) { + if (!this._lastSlashCommands) { + await this.provideSlashCommand(CancellationToken.None); + assertType(this._lastSlashCommands); + } + const result = this._lastSlashCommands.find(candidate => candidate.name === command); + if (!result) { + throw new Error(`Unknown slashCommand: ${command}`); + + } + return result; + } + + async provideSlashCommand(token: CancellationToken): Promise { + if (!this._slashCommandProvider) { + return []; + } + const result = await this._slashCommandProvider.provideSlashCommands(token); + if (!result) { + return []; + } + this._lastSlashCommands = result; + return result.map(c => ({ name: c.name, description: c.description })); + } + + async provideFollowups(result: vscode.ChatAgentResult2, token: CancellationToken): Promise { + if (!this._followupProvider) { + return []; + } + const followups = await this._followupProvider.provideFollowups(result, token); + if (!followups) { + return []; + } + return followups.map(f => typeConvert.ChatFollowup.from(f)); + } + + get apiAgent(): vscode.ChatAgent2 { + + let updateScheduled = false; + const updateMetadataSoon = () => { + if (updateScheduled) { + return; + } + updateScheduled = true; + queueMicrotask(() => { + this._proxy.$updateAgent(this._handle, { + description: this._description ?? '', + fullName: this._fullName, + icon: this._iconPath, + hasSlashCommands: this._slashCommandProvider !== undefined, + hasFollowup: this._followupProvider !== undefined, + }); + updateScheduled = false; + }); + }; + + const that = this; + return { + get name() { + return that._id; + }, + get description() { + return that._description ?? ''; + }, + set description(v) { + that._description = v; + updateMetadataSoon(); + }, + get fullName() { + return that._fullName ?? that.extension.value; + }, + set fullName(v) { + that._fullName = v; + updateMetadataSoon(); + }, + get iconPath() { + return that._iconPath; + }, + set iconPath(v) { + that._iconPath = v; + updateMetadataSoon(); + }, + // onDidPerformAction + get slashCommandProvider() { + return that._slashCommandProvider; + }, + set slashCommandProvider(v) { + that._slashCommandProvider = v; + updateMetadataSoon(); + }, + get followupProvider() { + return that._followupProvider; + }, + set followupProvider(v) { + that._followupProvider = v; + updateMetadataSoon(); + }, + dispose() { + that._proxy.$unregisterAgent(that._handle); + }, + } satisfies vscode.ChatAgent2; + } + + invoke(request: vscode.ChatAgentRequest, context: vscode.ChatAgentContext, progress: Progress, token: CancellationToken): vscode.ProviderResult { + return this._callback(request, context, progress, token); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatVariables.ts b/src/vs/workbench/contrib/chat/browser/chatVariables.ts index 09e53ac0b3a8a..90ed4ac45b378 100644 --- a/src/vs/workbench/contrib/chat/browser/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/chatVariables.ts @@ -43,16 +43,16 @@ export class ChatVariablesService implements IChatVariablesService { resolvedVariables[part.variableName] = value; parsedPrompt[i] = `[${part.text}](values:${part.variableName})`; } else { - parsedPrompt[i] = part.text; + parsedPrompt[i] = part.promptText; } }).catch(onUnexpectedExternalError)); } } else if (part instanceof ChatRequestDynamicReferencePart) { // Maybe the dynamic reference should include a full IChatRequestVariableValue[] at the time it is inserted? resolvedVariables[part.referenceText] = [{ level: 'full', value: part.data.toString() }]; - parsedPrompt[i] = `[${part.text}](values:${part.referenceText})`; + parsedPrompt[i] = part.promptText; } else { - parsedPrompt[i] = part.text; + parsedPrompt[i] = part.promptText; } }); @@ -60,7 +60,7 @@ export class ChatVariablesService implements IChatVariablesService { return { variables: resolvedVariables, - prompt: parsedPrompt.join('') + prompt: parsedPrompt.join('').trim() }; } diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 44ebbee7bf301..2880bd122a30a 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { raceCancellation } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -346,12 +347,17 @@ class AgentCompletions extends Disposable { this._register(this.languageFeaturesService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, { _debugDisplayName: 'chatAgentSubcommand', triggerCharacters: ['/'], - provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { + provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); if (!widget || !widget.viewModel) { return; } + const range = computeCompletionRanges(model, position, /\/\w*/g); + if (!range) { + return null; + } + const parsedRequest = (await this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(widget.viewModel.sessionId, model.getValue())).parts; const usedAgent = parsedRequest.find((p): p is ChatRequestAgentPart => p instanceof ChatRequestAgentPart); if (!usedAgent) { @@ -364,14 +370,16 @@ class AgentCompletions extends Disposable { return; } + const commands = await usedAgent.agent.provideSlashCommands(token); + return { - suggestions: usedAgent.agent.metadata.subCommands.map((c, i) => { + suggestions: commands.map((c, i) => { const withSlash = `/${c.name}`; return { label: withSlash, insertText: `${withSlash} `, detail: c.description, - range: new Range(1, position.column - 1, 1, position.column - 1), + range, kind: CompletionItemKind.Text, // The icons are disabled here anyway }; }) @@ -383,7 +391,7 @@ class AgentCompletions extends Disposable { this._register(this.languageFeaturesService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, { _debugDisplayName: 'chatAgentAndSubcommand', triggerCharacters: ['/'], - provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { + provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); if (!widget) { return; @@ -395,13 +403,21 @@ class AgentCompletions extends Disposable { } const agents = this.chatAgentService.getAgents(); + const all = agents.map(agent => agent.provideSlashCommands(token)); + const commands = await raceCancellation(Promise.all(all), token); + + if (!commands) { + return; + } + return { - suggestions: agents.flatMap(a => a.metadata.subCommands.map((c, i) => { + suggestions: agents.flatMap((agent, i) => commands[i].map((c, i) => { + const agentLabel = `@${agent.id}`; const withSlash = `/${c.name}`; return { - label: withSlash, - insertText: `@${a.id} ${withSlash} `, - detail: `(@${a.id}) ${c.description}`, + label: { label: withSlash, description: agentLabel }, + insertText: `${agentLabel} ${withSlash} `, + detail: `(${agentLabel}) ${c.description}`, range: new Range(1, 1, 1, 1), kind: CompletionItemKind.Text, // The icons are disabled here anyway }; diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index dcf7c2d4f9e58..e6a33f88f88b5 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -4,68 +4,23 @@ *--------------------------------------------------------------------------------------------*/ 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 { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { localize } from 'vs/nls'; 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 agentItem: IJSONSchema = { - type: 'object', - required: ['agent', 'detail'], - properties: { - agent: { - type: 'string', - markdownDescription: localize('agent', "The name of the agent which will be used as prefix.") - }, - detail: { - type: 'string', - markdownDescription: localize('details', "The details of the agent.") - }, - } -}; - -const agentItems: IJSONSchema = { - description: localize('vscode.extension.contributes.slashes', "Contributes agents to chat"), - oneOf: [ - agentItem, - { - type: 'array', - items: agentItem - } - ] -}; - -export const agentsExtPoint = ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'agents', - jsonSchema: agentItems -}); +import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; //#region agent service, commands etc -export interface IChatAgentData { +export interface IChatAgent { id: string; metadata: IChatAgentMetadata; -} - -function isAgentData(data: any): data is IChatAgentData { - return typeof data === 'object' && data && - typeof data.id === 'string' && - typeof data.detail === 'string'; - // (typeof data.sortText === 'undefined' || typeof data.sortText === 'string') && - // (typeof data.executeImmediately === 'undefined' || typeof data.executeImmediately === 'boolean'); + invoke(request: IChatAgentRequest, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise; + // provideFollowups?: IChatAgentFollowupProvider; + provideSlashCommands(token: CancellationToken): Promise; } export interface IChatAgentFragment { @@ -78,138 +33,112 @@ export interface IChatAgentCommand { } export interface IChatAgentMetadata { - description: string; - subCommands: IChatAgentCommand[]; + description?: string; + // subCommands: IChatAgentCommand[]; requireCommand?: boolean; // Do some agents not have a default action? isImplicit?: boolean; // Only @workspace. slash commands get promoted to the top-level and this agent is invoked when those are used fullName?: string; icon?: URI; } -export type IChatAgentCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; +export interface IChatAgentRequest { + requestId: string; + command?: string; + message: string; + variables: Record; +} + +export interface IChatAgentResult { + // delete, keep while people are still using the previous API + followUp?: IChatFollowup[]; + errorDetails?: IChatResponseErrorDetails; + timings?: { + firstProgress: number; + totalElapsed: number; + }; +} export const IChatAgentService = createDecorator('chatAgentService'); export interface IChatAgentService { _serviceBrand: undefined; readonly onDidChangeAgents: Event; - registerAgentData(data: IChatAgentData): IDisposable; - registerAgentCallback(id: string, callback: IChatAgentCallback): IDisposable; - registerAgent(data: IChatAgentData, callback: IChatAgentCallback): IDisposable; - invokeAgent(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; - getAgents(): Array; - getAgent(id: string): IChatAgentData | undefined; + registerAgent(agent: IChatAgent): IDisposable; + invokeAgent(id: string, request: IChatAgentRequest, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise; + getFollowups(id: string, requestId: string): IChatFollowup[]; + getAgents(): Array; + getAgent(id: string): IChatAgent | undefined; hasAgent(id: string): boolean; + updateAgent(id: string, updateMetadata: IChatAgentMetadata): void; } -type Tuple = { data: IChatAgentData; callback?: IChatAgentCallback }; - export class ChatAgentService extends Disposable implements IChatAgentService { public static readonly AGENT_LEADER = '@'; declare _serviceBrand: undefined; - private readonly _agents = new Map(); + private readonly _agents = new Map(); private readonly _onDidChangeAgents = this._register(new Emitter()); readonly onDidChangeAgents: Event = this._onDidChangeAgents.event; - constructor(@IExtensionService private readonly _extensionService: IExtensionService) { - super(); - } - override dispose(): void { super.dispose(); this._agents.clear(); } - registerAgentData(data: IChatAgentData): IDisposable { - if (this._agents.has(data.id)) { - throw new Error(`Already registered an agent with id ${data.id}}`); + registerAgent(agent: IChatAgent): IDisposable { + if (this._agents.has(agent.id)) { + throw new Error(`Already registered an agent with id ${agent.id}`); } - this._agents.set(data.id, { data }); + this._agents.set(agent.id, { agent }); this._onDidChangeAgents.fire(); return toDisposable(() => { - if (this._agents.delete(data.id)) { + if (this._agents.delete(agent.id)) { this._onDidChangeAgents.fire(); } }); } - registerAgentCallback(id: string, agentCallback: IChatAgentCallback): IDisposable { + updateAgent(id: string, updateMetadata: IChatAgentMetadata): void { const data = this._agents.get(id); if (!data) { throw new Error(`No agent with id ${id} registered`); } - data.callback = agentCallback; - return toDisposable(() => data.callback = undefined); - } - - registerAgent(data: IChatAgentData, callback: IChatAgentCallback): IDisposable { - return combinedDisposable( - this.registerAgentData(data), - this.registerAgentCallback(data.id, callback) - ); + data.agent.metadata = { ...data.agent.metadata, ...updateMetadata }; + this._onDidChangeAgents.fire(); } - getAgents(): Array { - return Array.from(this._agents.values(), v => v.data); + getAgents(): Array { + return Array.from(this._agents.values(), v => v.agent); } hasAgent(id: string): boolean { return this._agents.has(id); } - getAgent(id: string): IChatAgentData | undefined { + getAgent(id: string): IChatAgent | undefined { const data = this._agents.get(id); - return data?.data; + return data?.agent; } - async invokeAgent(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { + async invokeAgent(id: string, request: IChatAgentRequest, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise { const data = this._agents.get(id); if (!data) { - throw new Error('No agent with id ${id} NOT registered'); - } - if (!data.callback) { - await this._extensionService.activateByEvent(`onChatAgent:${id}`); - } - if (!data.callback) { - throw new Error(`No agent with id ${id} NOT resolved`); + throw new Error(`No agent with id ${id}`); } - return await data.callback(prompt, progress, history, token); + return await data.agent.invoke(request, progress, history, token); } -} - -class ChatAgentContribution implements IWorkbenchContribution { - constructor(@IChatAgentService chatAgentService: IChatAgentService) { - const contributions = new DisposableStore(); - - agentsExtPoint.setHandler(extensions => { - contributions.clear(); - - for (const entry of extensions) { - if (!isProposedApiEnabled(entry.description, 'chatAgents')) { - entry.collector.error(`The ${agentsExtPoint.name} is proposed API`); - continue; - } - - const { value } = entry; - for (const candidate of Iterable.wrap(value)) { - - if (!isAgentData(candidate)) { - entry.collector.error(localize('invalid', "Invalid {0}: {1}", agentsExtPoint.name, JSON.stringify(candidate))); - continue; - } + getFollowups(id: string, requestId: string): IChatFollowup[] { + const data = this._agents.get(id); + if (!data) { + throw new Error(`No agent with id ${id}`); + } - contributions.add(chatAgentService.registerAgentData({ ...candidate })); - } - } - }); + return []; } } - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ChatAgentContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 58e534489e364..bdcb7101521c6 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -13,7 +13,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ILogService } from 'vs/platform/log/common/log'; -import { IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChat, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; @@ -291,7 +291,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel constructor( _response: IMarkdownString | ReadonlyArray, public readonly session: ChatModel, - public readonly agent: IChatAgentData | undefined, + public readonly agent: IChatAgent | undefined, private _isComplete: boolean = false, private _isCanceled = false, private _vote?: InteractiveSessionVoteDirection, @@ -362,7 +362,7 @@ export interface ISerializableChatsData { export interface ISerializableChatAgentData { id: string; - description: string; + description?: string; fullName?: string; icon?: UriComponents; } @@ -644,7 +644,7 @@ export class ChatModel extends Disposable implements IChatModel { return this._requests; } - addRequest(message: IParsedChatRequest | IChatReplyFollowup, chatAgent?: IChatAgentData): ChatRequestModel { + addRequest(message: IParsedChatRequest | IChatReplyFollowup, chatAgent?: IChatAgent): ChatRequestModel { if (!this._session) { throw new Error('addRequest: No session'); } diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index e5df0780f4c72..0dea56c5e2220 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { IOffsetRange, OffsetRange } from 'vs/editor/common/core/offsetRange'; import { IRange } from 'vs/editor/common/core/range'; -import { IChatAgentData, IChatAgentCommand } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgent, IChatAgentCommand } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService'; // These are in a separate file to avoid circular dependencies with the dependencies of the parser @@ -21,12 +21,17 @@ export interface IParsedChatRequestPart { readonly range: IOffsetRange; readonly editorRange: IRange; readonly text: string; + readonly promptText: string; } export class ChatRequestTextPart implements IParsedChatRequestPart { static readonly Kind = 'text'; readonly kind = ChatRequestTextPart.Kind; constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly text: string) { } + + get promptText(): string { + return this.text; + } } export const chatVariableLeader = '#'; // warning, this also shows up in a regex in the parser @@ -43,6 +48,10 @@ export class ChatRequestVariablePart implements IParsedChatRequestPart { const argPart = this.variableArg ? `:${this.variableArg}` : ''; return `${chatVariableLeader}${this.variableName}${argPart}`; } + + get promptText(): string { + return this.text; + } } /** @@ -51,11 +60,15 @@ export class ChatRequestVariablePart implements IParsedChatRequestPart { export class ChatRequestAgentPart implements IParsedChatRequestPart { static readonly Kind = 'agent'; readonly kind = ChatRequestAgentPart.Kind; - constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly agent: IChatAgentData) { } + constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly agent: IChatAgent) { } get text(): string { return `@${this.agent.id}`; } + + get promptText(): string { + return ''; + } } /** @@ -69,6 +82,10 @@ export class ChatRequestAgentSubcommandPart implements IParsedChatRequestPart { get text(): string { return `/${this.command.name}`; } + + get promptText(): string { + return ''; + } } /** @@ -82,6 +99,10 @@ export class ChatRequestSlashCommandPart implements IParsedChatRequestPart { get text(): string { return `/${this.slashCommand.command}`; } + + get promptText(): string { + return ''; + } } /** @@ -99,6 +120,10 @@ export class ChatRequestDynamicReferencePart implements IParsedChatRequestPart { get text(): string { return `$${this.referenceText}`; } + + get promptText(): string { + return `[${this.text}](values:${this.referenceText})`; + } } export function reviveParsedChatRequest(serialized: IParsedChatRequest): IParsedChatRequest { diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index 64cb389fb15aa..6b322bdea63a4 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicReferencePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, IParsedChatRequest, IParsedChatRequestPart, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -79,6 +79,29 @@ export class ChatRequestParser { message.slice(lastPartEnd, message.length))); } + + // fix up parts: + // * only one agent at the beginning of the message + // * only one agent command after the agent or at the beginning of the message + let agentIndex = -1; + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (part instanceof ChatRequestAgentPart) { + if (i === 0) { + agentIndex = 0; + } else { + // agent not first -> make text part + parts[i] = new ChatRequestTextPart(part.range, part.editorRange, part.text); + } + } + if (part instanceof ChatRequestAgentSubcommandPart) { + if (!(i === 0 || agentIndex === 0 && i === 2 && /^\s+$/.test(parts[1].text))) { + // agent command not after agent nor first -> make text part + parts[i] = new ChatRequestTextPart(part.range, part.editorRange, part.text); + } + } + } + return { parts, text: message, @@ -95,7 +118,7 @@ export class ChatRequestParser { const varRange = new OffsetRange(offset, offset + full.length); const varEditorRange = new Range(position.lineNumber, position.column, position.lineNumber, position.column + full.length); - let agent: IChatAgentData | undefined; + let agent: IChatAgent | undefined; if ((agent = this.agentService.getAgent(name))) { if (parts.some(p => p instanceof ChatRequestAgentPart)) { // Only one agent allowed @@ -143,7 +166,8 @@ export class ChatRequestParser { const usedAgent = parts.find((p): p is ChatRequestAgentPart => p instanceof ChatRequestAgentPart); if (usedAgent) { - const subCommand = usedAgent.agent.metadata.subCommands.find(c => c.name === command); + const subCommands = await usedAgent.agent.provideSlashCommands(CancellationToken.None); + const subCommand = subCommands.find(c => c.name === command); if (subCommand) { // Valid agent subcommand return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand); diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 14ed59883499d..9586faeb1c373 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -12,6 +12,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { revive } from 'vs/base/common/marshalling'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -21,10 +22,10 @@ import { Progress } from 'vs/platform/progress/common/progress'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData, isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel'; -import { ChatRequestAgentPart, ChatRequestSlashCommandPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; +import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; @@ -442,6 +443,7 @@ export class ChatService extends Disposable implements IChatService { let request: ChatRequestModel; const agentPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentPart => r instanceof ChatRequestAgentPart); + const agentSlashCommandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestAgentSubcommandPart => r instanceof ChatRequestAgentSubcommandPart); const commandPart = 'kind' in parsedRequest ? undefined : parsedRequest.parts.find((r): r is ChatRequestSlashCommandPart => r instanceof ChatRequestSlashCommandPart); let gotProgress = false; @@ -502,7 +504,7 @@ export class ChatService extends Disposable implements IChatService { let slashCommandFollowups: IChatFollowup[] | void = []; if (typeof message === 'string' && agentPart) { - request = model.addRequest(parsedRequest); + request = model.addRequest(parsedRequest, agentPart.agent); const history: IChatMessage[] = []; for (const request of model.getRequests()) { if (!request.response) { @@ -512,13 +514,24 @@ export class ChatService extends Disposable implements IChatService { history.push({ role: ChatMessageRole.User, content: 'text' in request.message ? request.message.text : request.message.message }); history.push({ role: ChatMessageRole.Assistant, content: request.response.response.asString() }); } - const agentResult = await this.chatAgentService.invokeAgent(agentPart.agent.id, message.substring(agentPart.agent.id.length + 1).trimStart(), new Progress(p => { - const { content } = p; - const data = isCompleteInteractiveProgressTreeData(content) ? content : { content }; - progressCallback(data); + + const requestProps: IChatAgentRequest = { + requestId: generateUuid(), + message: message, + variables: {}, + command: agentSlashCommandPart?.command.name ?? '', + }; + if ('parts' in parsedRequest) { + const varResult = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); + requestProps.variables = varResult.variables; + requestProps.message = varResult.prompt; + } + + const agentResult = await this.chatAgentService.invokeAgent(agentPart.agent.id, requestProps, new Progress(p => { + progressCallback(p); }), history, token); slashCommandFollowups = agentResult?.followUp; - rawResponse = { session: model.session! }; + rawResponse = { session: model.session!, errorDetails: agentResult.errorDetails, timings: agentResult.timings }; } else if (commandPart && typeof message === 'string' && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) { request = model.addRequest(parsedRequest); // contributed slash commands diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_not_first.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_not_first.0.snap index a6a8d0d151669..0ac17204ee01c 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_not_first.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_not_first.0.snap @@ -25,14 +25,8 @@ endLineNumber: 1, endColumn: 17 }, - agent: { - id: "agent", - metadata: { - description: "", - subCommands: [ { name: "subCommand" } ] - } - }, - kind: "agent" + text: "@agent", + kind: "text" }, { range: { @@ -59,8 +53,8 @@ endLineNumber: 1, endColumn: 29 }, - command: { name: "subCommand" }, - kind: "subcommand" + text: "/subCommand", + kind: "text" }, { range: { diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_with_question_mark.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_with_question_mark.0.snap index 9c3a23726272c..65e2aa78ac033 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_with_question_mark.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_with_question_mark.0.snap @@ -3,51 +3,35 @@ { range: { start: 0, - endExclusive: 14 + endExclusive: 6 }, editorRange: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, - endColumn: 15 - }, - text: "Are you there ", - kind: "text" - }, - { - range: { - start: 14, - endExclusive: 20 - }, - editorRange: { - startLineNumber: 1, - startColumn: 15, - endLineNumber: 1, - endColumn: 21 + endColumn: 7 }, agent: { id: "agent", - metadata: { - description: "", - subCommands: [ { name: "subCommand" } ] - } + metadata: { description: "" }, + provideSlashCommands: [Function provideSlashCommands] }, kind: "agent" }, { range: { - start: 20, + start: 6, endExclusive: 21 }, editorRange: { startLineNumber: 1, - startColumn: 21, + startColumn: 7, endLineNumber: 1, endColumn: 22 }, - text: "?", + text: "? Are you there", kind: "text" } ], - text: "Are you there @agent?" + text: "@agent? Are you there" } \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents.0.snap index f89e75eabf3b9..8a83800323f0c 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents.0.snap @@ -13,10 +13,8 @@ }, agent: { id: "agent", - metadata: { - description: "", - subCommands: [ { name: "subCommand" } ] - } + metadata: { description: "" }, + provideSlashCommands: [Function provideSlashCommands] }, kind: "agent" }, @@ -45,8 +43,8 @@ endLineNumber: 1, endColumn: 29 }, - command: { name: "subCommand" }, - kind: "subcommand" + text: "/subCommand", + kind: "text" }, { range: { diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents__subCommand.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents__subCommand.0.snap new file mode 100644 index 0000000000000..ca9a0569fcd43 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents__subCommand.0.snap @@ -0,0 +1,68 @@ +{ + parts: [ + { + range: { + start: 0, + endExclusive: 6 + }, + editorRange: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 7 + }, + agent: { + id: "agent", + metadata: { description: "" }, + provideSlashCommands: [Function provideSlashCommands] + }, + kind: "agent" + }, + { + range: { + start: 6, + endExclusive: 7 + }, + editorRange: { + startLineNumber: 1, + startColumn: 7, + endLineNumber: 1, + endColumn: 8 + }, + text: " ", + kind: "text" + }, + { + range: { + start: 7, + endExclusive: 18 + }, + editorRange: { + startLineNumber: 1, + startColumn: 8, + endLineNumber: 1, + endColumn: 19 + }, + command: { + name: "subCommand", + description: "" + }, + kind: "subcommand" + }, + { + range: { + start: 18, + endExclusive: 35 + }, + editorRange: { + startLineNumber: 1, + startColumn: 19, + endLineNumber: 1, + endColumn: 36 + }, + text: " Please do thanks", + kind: "text" + } + ], + text: "@agent /subCommand Please do thanks" +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline.0.snap index 27a7c90ce8c81..750f1bc39f66c 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline.0.snap @@ -13,53 +13,54 @@ }, agent: { id: "agent", - metadata: { - description: "", - subCommands: [ { name: "subCommand" } ] - } + metadata: { description: "" }, + provideSlashCommands: [Function provideSlashCommands] }, kind: "agent" }, { range: { start: 6, - endExclusive: 18 + endExclusive: 7 }, editorRange: { startLineNumber: 1, startColumn: 7, - endLineNumber: 2, - endColumn: 4 + endLineNumber: 1, + endColumn: 8 }, - text: " Please \ndo ", + text: " ", kind: "text" }, { range: { - start: 18, - endExclusive: 29 + start: 7, + endExclusive: 18 }, editorRange: { - startLineNumber: 2, - startColumn: 4, - endLineNumber: 2, - endColumn: 15 + startLineNumber: 1, + startColumn: 8, + endLineNumber: 1, + endColumn: 19 + }, + command: { + name: "subCommand", + description: "" }, - command: { name: "subCommand" }, kind: "subcommand" }, { range: { - start: 29, + start: 18, endExclusive: 35 }, editorRange: { - startLineNumber: 2, - startColumn: 15, + startLineNumber: 1, + startColumn: 19, endLineNumber: 2, - endColumn: 21 + endColumn: 16 }, - text: " with ", + text: " \nPlease do with ", kind: "text" }, { @@ -69,9 +70,9 @@ }, editorRange: { startLineNumber: 2, - startColumn: 21, + startColumn: 16, endLineNumber: 2, - endColumn: 31 + endColumn: 26 }, variableName: "selection", variableArg: "", @@ -84,7 +85,7 @@ }, editorRange: { startLineNumber: 2, - startColumn: 31, + startColumn: 26, endLineNumber: 3, endColumn: 5 }, @@ -107,5 +108,5 @@ kind: "var" } ], - text: "@agent Please \ndo /subCommand with #selection\nand #debugConsole" + text: "@agent /subCommand \nPlease do with #selection\nand #debugConsole" } \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline__part2.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline__part2.0.snap new file mode 100644 index 0000000000000..3708cf785419d --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agents_and_variables_and_multiline__part2.0.snap @@ -0,0 +1,109 @@ +{ + parts: [ + { + range: { + start: 0, + endExclusive: 6 + }, + editorRange: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 7 + }, + agent: { + id: "agent", + metadata: { description: "" }, + provideSlashCommands: [Function provideSlashCommands] + }, + kind: "agent" + }, + { + range: { + start: 6, + endExclusive: 18 + }, + editorRange: { + startLineNumber: 1, + startColumn: 7, + endLineNumber: 2, + endColumn: 4 + }, + text: " Please \ndo ", + kind: "text" + }, + { + range: { + start: 18, + endExclusive: 29 + }, + editorRange: { + startLineNumber: 2, + startColumn: 4, + endLineNumber: 2, + endColumn: 15 + }, + text: "/subCommand", + kind: "text" + }, + { + range: { + start: 29, + endExclusive: 35 + }, + editorRange: { + startLineNumber: 2, + startColumn: 15, + endLineNumber: 2, + endColumn: 21 + }, + text: " with ", + kind: "text" + }, + { + range: { + start: 35, + endExclusive: 45 + }, + editorRange: { + startLineNumber: 2, + startColumn: 21, + endLineNumber: 2, + endColumn: 31 + }, + variableName: "selection", + variableArg: "", + kind: "var" + }, + { + range: { + start: 45, + endExclusive: 50 + }, + editorRange: { + startLineNumber: 2, + startColumn: 31, + endLineNumber: 3, + endColumn: 5 + }, + text: "\nand ", + kind: "text" + }, + { + range: { + start: 50, + endExclusive: 63 + }, + editorRange: { + startLineNumber: 3, + startColumn: 5, + endLineNumber: 3, + endColumn: 18 + }, + variableName: "debugConsole", + variableArg: "", + kind: "var" + } + ], + text: "@agent Please \ndo /subCommand with #selection\nand #debugConsole" +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts index 90b31b1ace978..78317a10496be 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts @@ -9,7 +9,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/uti import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ChatAgentService, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { ChatAgentService, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -111,7 +111,7 @@ suite('ChatRequestParser', () => { test('agents', async () => { const agentsService = mockObject()({}); - agentsService.getAgent.returns({ id: 'agent', metadata: { description: '', subCommands: [{ name: 'subCommand' }] } }); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -119,19 +119,29 @@ suite('ChatRequestParser', () => { await assertSnapshot(result); }); + test('agents, subCommand', async () => { + const agentsService = mockObject()({}); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); + instantiationService.stub(IChatAgentService, agentsService as any); + + parser = instantiationService.createInstance(ChatRequestParser); + const result = await parser.parseChatRequest('1', '@agent /subCommand Please do thanks'); + await assertSnapshot(result); + }); + test('agent with question mark', async () => { const agentsService = mockObject()({}); - agentsService.getAgent.returns({ id: 'agent', metadata: { description: '', subCommands: [{ name: 'subCommand' }] } }); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); - const result = await parser.parseChatRequest('1', 'Are you there @agent?'); + const result = await parser.parseChatRequest('1', '@agent? Are you there'); await assertSnapshot(result); }); test('agent not first', async () => { const agentsService = mockObject()({}); - agentsService.getAgent.returns({ id: 'agent', metadata: { description: '', subCommands: [{ name: 'subCommand' }] } }); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -141,7 +151,21 @@ suite('ChatRequestParser', () => { test('agents and variables and multiline', async () => { const agentsService = mockObject()({}); - agentsService.getAgent.returns({ id: 'agent', metadata: { description: '', subCommands: [{ name: 'subCommand' }] } }); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); + instantiationService.stub(IChatAgentService, agentsService as any); + + const variablesService = mockObject()({}); + variablesService.hasVariable.returns(true); + instantiationService.stub(IChatVariablesService, variablesService as any); + + parser = instantiationService.createInstance(ChatRequestParser); + const result = await parser.parseChatRequest('1', '@agent /subCommand \nPlease do with #selection\nand #debugConsole'); + await assertSnapshot(result); + }); + + test('agents and variables and multiline, part2', async () => { + const agentsService = mockObject()({}); + agentsService.getAgent.returns(>{ id: 'agent', metadata: { description: '' }, provideSlashCommands: async () => { return [{ name: 'subCommand', description: '' }]; } }); instantiationService.stub(IChatAgentService, agentsService as any); const variablesService = mockObject()({}); @@ -153,4 +177,3 @@ suite('ChatRequestParser', () => { await assertSnapshot(result); }); }); - diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 18a609800a8ce..ed45aadd56d03 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -12,6 +12,7 @@ export const allApiProposals = Object.freeze({ canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts', chat: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chat.d.ts', chatAgents: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents.d.ts', + chatAgents2: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents2.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', chatVariables: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariables.d.ts', diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts new file mode 100644 index 0000000000000..830cb37a67cad --- /dev/null +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * 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 ChatAgentContext { + /** + * All of the chat messages so far in the current chat session. + */ + history: ChatMessage[]; + } + + export interface ChatAgentErrorDetails { + message: string; + responseIsIncomplete?: boolean; + responseIsFiltered?: boolean; + } + + export interface ChatAgentResult2 { + errorDetails?: ChatAgentErrorDetails; + } + + export interface ChatAgentSlashCommand { + + /** + * A short name by which this command is referred to in the UI, e.g. `fix` or + * `explain` for commands that fix an issue or explain code. + */ + readonly name: string; + + /** + * Human-readable description explaining what this command does. + */ + readonly description: string; + } + + export interface ChatAgentSlashCommandProvider { + + /** + * Returns a list of slash commands that its agent is capable of handling. A slash command + * and be selected by the user and will then be passed to the {@link ChatAgentHandler handler} + * via the {@link ChatAgentRequest.slashCommand slashCommand} property. + * + * + * @param token A cancellation token. + * @returns A list of slash commands. The lack of a result can be signaled by returning `undefined`, `null`, or + * an empty array. + */ + provideSlashCommands(token: CancellationToken): ProviderResult; + } + + export interface ChatAgentCommandFollowup { + commandId: string; + args?: any[]; + title: string; // supports codicon strings + when?: string; + } + + export interface ChatAgentReplyFollowup { + message: string; + tooltip?: string; + title?: string; + } + + export type ChatAgentFollowup = ChatAgentCommandFollowup | ChatAgentReplyFollowup; + + export interface FollowupProvider { + provideFollowups(result: ChatAgentResult2, token: CancellationToken): ProviderResult; + } + + export interface ChatAgent2 { + + /** + * The short name by which this agent is referred to in the UI, e.g `workspace` + */ + readonly name: string; + + /** + * The full name of this agent + */ + fullName: string; + + /** + * A human-readable description explaining what this agent does. + */ + description: string; + + /** + * Icon for the agent shown in UI. + */ + iconPath?: Uri; + + slashCommandProvider?: ChatAgentSlashCommandProvider; + + followupProvider?: FollowupProvider; + + // TODO@API We need this- can't handle telemetry on the vscode side yet + // onDidPerformAction: Event<{ action: InteractiveSessionUserAction }>; + + + // TODO@API Something like prepareSession from the interactive chat provider might be needed.Probably nobody needs it right now. + // prepareSession(); + + /** + * TODO@API explain what happens wrt to history, in-flight requests etc... + * Dispose this agent and free resources + */ + dispose(): void; + } + + export interface ChatAgentRequest { + + /** + * The prompt entered by the user. The {@link ChatAgent2.name name} of the agent or the {@link ChatAgentSlashCommand.name slash command} + * are not part of the prompt. + * + * @see {@link ChatAgentRequest.slashCommand} + */ + prompt: string; + + /** + * The {@link ChatAgentSlashCommand slash command} that was selected for this request. It is guaranteed that the passed slash + * command is an instance that was previously returned from the {@link ChatAgentSlashCommandProvider.provideSlashCommands slash command provider}. + */ + slashCommand?: ChatAgentSlashCommand; + + variables: Record; + } + + // TODO@API InteractiveProgress is a lot to inline... + export type ChatAgentHandler = (request: ChatAgentRequest, context: ChatAgentContext, progress: Progress, token: CancellationToken) => ProviderResult; + + export namespace chat { + + /** + * Create a new {@link ChatAgent2 chat agent} instance. + * + * @param name Short name by which this agent is referred to in the UI + * @param handler The reply-handler of the agent. + * @returns A new chat agent + */ + export function createChatAgent(name: string, handler: ChatAgentHandler): ChatAgent2; + } +} diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index a3c2fd927bf49..30708bfda72e8 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -109,6 +109,8 @@ declare module 'vscode' { export interface InteractiveRequest { session: InteractiveSession; message: string | InteractiveSessionReplyFollowup; + // TODO@API move to agent + // slashCommand?: InteractiveSessionSlashCommand; } export interface InteractiveResponseErrorDetails { diff --git a/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts b/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts index 2e6bfe31f990b..e018235d60995 100644 --- a/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts +++ b/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts @@ -13,6 +13,7 @@ declare module 'vscode' { export interface InteractiveSessionVoteAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'vote'; + // sessionId: string; responseId: string; direction: InteractiveSessionVoteDirection; } @@ -26,6 +27,7 @@ declare module 'vscode' { export interface InteractiveSessionCopyAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'copy'; + // sessionId: string; responseId: string; codeBlockIndex: number; copyType: InteractiveSessionCopyKind; @@ -37,6 +39,7 @@ declare module 'vscode' { export interface InteractiveSessionInsertAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'insert'; + // sessionId: string; responseId: string; codeBlockIndex: number; totalCharacters: number; @@ -46,6 +49,7 @@ declare module 'vscode' { export interface InteractiveSessionTerminalAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'runInTerminal'; + // sessionId: string; responseId: string; codeBlockIndex: number; languageId?: string; From a936df41b768663d19f39f1f6a09544e1c598f96 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Oct 2023 06:13:12 +0200 Subject: [PATCH 11/11] towards using new proposed watch API --- extensions/typescript-language-features/package.json | 3 ++- .../src/typescriptServiceClient.ts | 7 ++++--- extensions/typescript-language-features/tsconfig.json | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 607bb3aabe0d8..55f685869ac6f 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -8,7 +8,8 @@ "license": "MIT", "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "enabledApiProposals": [ - "workspaceTrust" + "workspaceTrust", + "createFileSystemWatcher" ], "capabilities": { "virtualWorkspaces": { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 1557f4ac0a81d..dc791e81cdbb0 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -977,11 +977,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; case EventName.createDirectoryWatcher: - this.createFileSystemWatcher(event.body.id, new vscode.RelativePattern(vscode.Uri.file(event.body.path), event.body.recursive ? '**' : '*')); + this.createFileSystemWatcher(event.body.id, new vscode.RelativePattern(vscode.Uri.file(event.body.path), event.body.recursive ? '**' : '*'), [ /* TODO need to fill in excludes list */]); break; case EventName.createFileWatcher: - this.createFileSystemWatcher(event.body.id, event.body.path); + this.createFileSystemWatcher(event.body.id, event.body.path, []); break; case EventName.closeFileWatcher: @@ -993,8 +993,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType private createFileSystemWatcher( id: number, pattern: vscode.GlobPattern, + excludes: string[] ) { - const watcher = vscode.workspace.createFileSystemWatcher(pattern); + const watcher = typeof pattern === 'string' ? vscode.workspace.createFileSystemWatcher(pattern) : vscode.workspace.createFileSystemWatcher(pattern, { excludes }); watcher.onDidChange(changeFile => this.executeWithoutWaitingForResponse('watchChange', { id, path: changeFile.fsPath, eventType: 'update' }) ); diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 73957dde9321e..5ac0797334b2c 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -11,6 +11,7 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts" + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", + "../../src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts" ] }