diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 440d4953a28..6f4ef625267 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -75,6 +75,9 @@ "name": "hello", "description": "Hello" } + ], + "modes": [ + "agent", "ask", "edit" ] }, { diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 5e205a0772d..6c231b0f15f 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -208,8 +208,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA metadata: revive(metadata), slashCommands: [], disambiguation: [], - locations: [ChatAgentLocation.Panel], // TODO all dynamic participants are panel only? - modes: [ChatModeKind.Ask] + locations: [ChatAgentLocation.Panel], + modes: [ChatModeKind.Ask, ChatModeKind.Agent, ChatModeKind.Edit], }, impl); } else { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 4dbc2d245a1..c7e13596d35 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -155,10 +155,7 @@ abstract class OpenChatGlobalAction extends Action2 { } const switchToModeInput = opts?.mode ?? this.mode; - let switchToMode = switchToModeInput && (chatModeService.findModeById(switchToModeInput) ?? chatModeService.findModeByName(switchToModeInput)); - if (!switchToMode) { - switchToMode = opts?.query?.startsWith('@') ? ChatMode.Ask : undefined; - } + const switchToMode = switchToModeInput && (chatModeService.findModeById(switchToModeInput) ?? chatModeService.findModeByName(switchToModeInput)); if (switchToMode) { await this.handleSwitchToMode(switchToMode, chatWidget, instaService, commandService); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index 15ba64b61be..48f4b5f0e76 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -71,7 +71,7 @@ export function registerNewChatActions() { title: localize2('chat.newEdits.label', "New Chat"), category: CHAT_CATEGORY, icon: Codicon.plus, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), + precondition: ContextKeyExpr.and(ChatContextKeys.enabled), f1: true, menu: [{ id: MenuId.ChatContext, @@ -142,7 +142,7 @@ export function registerNewChatActions() { title: localize2('chat.undoEdit.label', "Undo Last Request"), category: CHAT_CATEGORY, icon: Codicon.discard, - precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), + precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled), f1: true, menu: [{ id: MenuId.ViewTitle, @@ -166,7 +166,7 @@ export function registerNewChatActions() { title: localize2('chat.redoEdit.label', "Redo Checkpoint Restore"), category: CHAT_CATEGORY, icon: Codicon.redo, - precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), + precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled), f1: true, menu: [ { @@ -191,7 +191,7 @@ export function registerNewChatActions() { id: 'workbench.action.chat.redoEdit2', title: localize2('chat.redoEdit.label2', "Redo Checkpoint Restore"), category: CHAT_CATEGORY, - precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered), + precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled), f1: true, menu: [{ id: MenuId.ChatMessageRestoreCheckpoint, diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 3f0ba278602..1e84def11c5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -247,6 +247,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { continue; } + if (providerDescriptor.isDefault && !providerDescriptor.modes?.length) { + extension.collector.error(`Extension '${extension.description.identifier.value}' CANNOT register default participant without modes.`); + continue; + } + if (providerDescriptor.locations && !isProposedApiEnabled(extension.description, 'chatParticipantAdditions')) { extension.collector.error(`Extension '${extension.description.identifier.value}' CANNOT use API proposal: chatParticipantAdditions.`); continue; @@ -291,7 +296,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { locations: isNonEmptyArray(providerDescriptor.locations) ? providerDescriptor.locations.map(ChatAgentLocation.fromRaw) : [ChatAgentLocation.Panel], - modes: providerDescriptor.modes ?? [ChatModeKind.Ask], + modes: providerDescriptor.isDefault ? providerDescriptor.modes! : [ChatModeKind.Agent, ChatModeKind.Ask, ChatModeKind.Edit], slashCommands: providerDescriptor.commands ?? [], disambiguation: coalesce(participantsDisambiguation.flat()), } satisfies IChatAgentData)); diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index dc74c196260..d7f7f40bd00 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -59,6 +59,7 @@ export interface IChatAgentData { metadata: IChatAgentMetadata; slashCommands: IChatAgentCommand[]; locations: ChatAgentLocation[]; + /** This is only relevant for isDefault agents. Others should have all modes available. */ modes: ChatModeKind[]; disambiguation: { category: string; description: string; examples: string[] }[]; } @@ -232,7 +233,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService { private readonly _hasDefaultAgent: IContextKey; private readonly _extensionAgentRegistered: IContextKey; private readonly _defaultAgentRegistered: IContextKey; - private readonly _editingAgentRegistered: IContextKey; private _hasToolsAgent = false; private _chatParticipantDetectionProviders = new Map(); @@ -245,7 +245,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService { this._hasDefaultAgent = ChatContextKeys.enabled.bindTo(this.contextKeyService); this._extensionAgentRegistered = ChatContextKeys.extensionParticipantRegistered.bindTo(this.contextKeyService); this._defaultAgentRegistered = ChatContextKeys.panelParticipantRegistered.bindTo(this.contextKeyService); - this._editingAgentRegistered = ChatContextKeys.editingParticipantRegistered.bindTo(this.contextKeyService); this._register(contextKeyService.onDidChangeContext((e) => { if (e.affectsSome(this._agentsContextKeys)) { this._updateContextKeys(); @@ -295,7 +294,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService { } private _updateContextKeys(): void { - let editingAgentRegistered = false; let extensionAgentRegistered = false; let defaultAgentRegistered = false; let toolsAgentRegistered = false; @@ -304,16 +302,14 @@ export class ChatAgentService extends Disposable implements IChatAgentService { if (!agent.isCore) { extensionAgentRegistered = true; } - if (agent.modes.includes(ChatModeKind.Agent)) { + if (agent.id === 'chat.setup' || agent.id === 'github.copilot.editsAgent') { + // TODO@roblourens firing the event below probably isn't necessary but leave it alone for now toolsAgentRegistered = true; - } else if (agent.modes.includes(ChatModeKind.Edit)) { - editingAgentRegistered = true; } else { defaultAgentRegistered = true; } } } - this._editingAgentRegistered.set(editingAgentRegistered); this._defaultAgentRegistered.set(defaultAgentRegistered); this._extensionAgentRegistered.set(extensionAgentRegistered); if (toolsAgentRegistered !== this._hasToolsAgent) { diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index 764ad70e395..78e322ddadb 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -42,7 +42,6 @@ export namespace ChatContextKeys { export const extensionParticipantRegistered = new RawContextKey('chatPanelExtensionParticipantRegistered', false, { type: 'boolean', description: localize('chatPanelExtensionParticipantRegistered', "True when a default chat participant is registered for the panel from an extension.") }); export const panelParticipantRegistered = new RawContextKey('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") }); - export const editingParticipantRegistered = new RawContextKey('chatEditingParticipantRegistered', false, { type: 'boolean', description: localize('chatEditingParticipantRegistered', "True when a default chat participant is registered for editing.") }); export const chatEditingCanUndo = new RawContextKey('chatEditingCanUndo', false, { type: 'boolean', description: localize('chatEditingCanUndo', "True when it is possible to undo an interaction in the editing panel.") }); export const chatEditingCanRedo = new RawContextKey('chatEditingCanRedo', false, { type: 'boolean', description: localize('chatEditingCanRedo', "True when it is possible to redo an interaction in the editing panel.") }); export const extensionInvalid = new RawContextKey('chatExtensionInvalid', false, { type: 'boolean', description: localize('chatExtensionInvalid', "True when the installed chat extension is invalid and needs to be updated.") });