mirror of https://github.com/microsoft/vscode.git
Support chat participants in agent mode (#256113)
* Allow participants in any mode Fix #255921 * Get rid of unneeded context keys * Preserve modes for default agents * Fix tests * Fix test
This commit is contained in:
parent
9abb71c178
commit
d8af289890
|
@ -75,6 +75,9 @@
|
||||||
"name": "hello",
|
"name": "hello",
|
||||||
"description": "Hello"
|
"description": "Hello"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"modes": [
|
||||||
|
"agent", "ask", "edit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -208,8 +208,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
|
||||||
metadata: revive(metadata),
|
metadata: revive(metadata),
|
||||||
slashCommands: [],
|
slashCommands: [],
|
||||||
disambiguation: [],
|
disambiguation: [],
|
||||||
locations: [ChatAgentLocation.Panel], // TODO all dynamic participants are panel only?
|
locations: [ChatAgentLocation.Panel],
|
||||||
modes: [ChatModeKind.Ask]
|
modes: [ChatModeKind.Ask, ChatModeKind.Agent, ChatModeKind.Edit],
|
||||||
},
|
},
|
||||||
impl);
|
impl);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -155,10 +155,7 @@ abstract class OpenChatGlobalAction extends Action2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
const switchToModeInput = opts?.mode ?? this.mode;
|
const switchToModeInput = opts?.mode ?? this.mode;
|
||||||
let switchToMode = switchToModeInput && (chatModeService.findModeById(switchToModeInput) ?? chatModeService.findModeByName(switchToModeInput));
|
const switchToMode = switchToModeInput && (chatModeService.findModeById(switchToModeInput) ?? chatModeService.findModeByName(switchToModeInput));
|
||||||
if (!switchToMode) {
|
|
||||||
switchToMode = opts?.query?.startsWith('@') ? ChatMode.Ask : undefined;
|
|
||||||
}
|
|
||||||
if (switchToMode) {
|
if (switchToMode) {
|
||||||
await this.handleSwitchToMode(switchToMode, chatWidget, instaService, commandService);
|
await this.handleSwitchToMode(switchToMode, chatWidget, instaService, commandService);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ export function registerNewChatActions() {
|
||||||
title: localize2('chat.newEdits.label', "New Chat"),
|
title: localize2('chat.newEdits.label', "New Chat"),
|
||||||
category: CHAT_CATEGORY,
|
category: CHAT_CATEGORY,
|
||||||
icon: Codicon.plus,
|
icon: Codicon.plus,
|
||||||
precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered),
|
precondition: ContextKeyExpr.and(ChatContextKeys.enabled),
|
||||||
f1: true,
|
f1: true,
|
||||||
menu: [{
|
menu: [{
|
||||||
id: MenuId.ChatContext,
|
id: MenuId.ChatContext,
|
||||||
|
@ -142,7 +142,7 @@ export function registerNewChatActions() {
|
||||||
title: localize2('chat.undoEdit.label', "Undo Last Request"),
|
title: localize2('chat.undoEdit.label', "Undo Last Request"),
|
||||||
category: CHAT_CATEGORY,
|
category: CHAT_CATEGORY,
|
||||||
icon: Codicon.discard,
|
icon: Codicon.discard,
|
||||||
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered),
|
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled),
|
||||||
f1: true,
|
f1: true,
|
||||||
menu: [{
|
menu: [{
|
||||||
id: MenuId.ViewTitle,
|
id: MenuId.ViewTitle,
|
||||||
|
@ -166,7 +166,7 @@ export function registerNewChatActions() {
|
||||||
title: localize2('chat.redoEdit.label', "Redo Checkpoint Restore"),
|
title: localize2('chat.redoEdit.label', "Redo Checkpoint Restore"),
|
||||||
category: CHAT_CATEGORY,
|
category: CHAT_CATEGORY,
|
||||||
icon: Codicon.redo,
|
icon: Codicon.redo,
|
||||||
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered),
|
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled),
|
||||||
f1: true,
|
f1: true,
|
||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
|
@ -191,7 +191,7 @@ export function registerNewChatActions() {
|
||||||
id: 'workbench.action.chat.redoEdit2',
|
id: 'workbench.action.chat.redoEdit2',
|
||||||
title: localize2('chat.redoEdit.label2', "Redo Checkpoint Restore"),
|
title: localize2('chat.redoEdit.label2', "Redo Checkpoint Restore"),
|
||||||
category: CHAT_CATEGORY,
|
category: CHAT_CATEGORY,
|
||||||
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered),
|
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled),
|
||||||
f1: true,
|
f1: true,
|
||||||
menu: [{
|
menu: [{
|
||||||
id: MenuId.ChatMessageRestoreCheckpoint,
|
id: MenuId.ChatMessageRestoreCheckpoint,
|
||||||
|
|
|
@ -247,6 +247,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
continue;
|
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')) {
|
if (providerDescriptor.locations && !isProposedApiEnabled(extension.description, 'chatParticipantAdditions')) {
|
||||||
extension.collector.error(`Extension '${extension.description.identifier.value}' CANNOT use API proposal: chatParticipantAdditions.`);
|
extension.collector.error(`Extension '${extension.description.identifier.value}' CANNOT use API proposal: chatParticipantAdditions.`);
|
||||||
continue;
|
continue;
|
||||||
|
@ -291,7 +296,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
|
||||||
locations: isNonEmptyArray(providerDescriptor.locations) ?
|
locations: isNonEmptyArray(providerDescriptor.locations) ?
|
||||||
providerDescriptor.locations.map(ChatAgentLocation.fromRaw) :
|
providerDescriptor.locations.map(ChatAgentLocation.fromRaw) :
|
||||||
[ChatAgentLocation.Panel],
|
[ChatAgentLocation.Panel],
|
||||||
modes: providerDescriptor.modes ?? [ChatModeKind.Ask],
|
modes: providerDescriptor.isDefault ? providerDescriptor.modes! : [ChatModeKind.Agent, ChatModeKind.Ask, ChatModeKind.Edit],
|
||||||
slashCommands: providerDescriptor.commands ?? [],
|
slashCommands: providerDescriptor.commands ?? [],
|
||||||
disambiguation: coalesce(participantsDisambiguation.flat()),
|
disambiguation: coalesce(participantsDisambiguation.flat()),
|
||||||
} satisfies IChatAgentData));
|
} satisfies IChatAgentData));
|
||||||
|
|
|
@ -59,6 +59,7 @@ export interface IChatAgentData {
|
||||||
metadata: IChatAgentMetadata;
|
metadata: IChatAgentMetadata;
|
||||||
slashCommands: IChatAgentCommand[];
|
slashCommands: IChatAgentCommand[];
|
||||||
locations: ChatAgentLocation[];
|
locations: ChatAgentLocation[];
|
||||||
|
/** This is only relevant for isDefault agents. Others should have all modes available. */
|
||||||
modes: ChatModeKind[];
|
modes: ChatModeKind[];
|
||||||
disambiguation: { category: string; description: string; examples: string[] }[];
|
disambiguation: { category: string; description: string; examples: string[] }[];
|
||||||
}
|
}
|
||||||
|
@ -232,7 +233,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
||||||
private readonly _hasDefaultAgent: IContextKey<boolean>;
|
private readonly _hasDefaultAgent: IContextKey<boolean>;
|
||||||
private readonly _extensionAgentRegistered: IContextKey<boolean>;
|
private readonly _extensionAgentRegistered: IContextKey<boolean>;
|
||||||
private readonly _defaultAgentRegistered: IContextKey<boolean>;
|
private readonly _defaultAgentRegistered: IContextKey<boolean>;
|
||||||
private readonly _editingAgentRegistered: IContextKey<boolean>;
|
|
||||||
private _hasToolsAgent = false;
|
private _hasToolsAgent = false;
|
||||||
|
|
||||||
private _chatParticipantDetectionProviders = new Map<number, IChatParticipantDetectionProvider>();
|
private _chatParticipantDetectionProviders = new Map<number, IChatParticipantDetectionProvider>();
|
||||||
|
@ -245,7 +245,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
||||||
this._hasDefaultAgent = ChatContextKeys.enabled.bindTo(this.contextKeyService);
|
this._hasDefaultAgent = ChatContextKeys.enabled.bindTo(this.contextKeyService);
|
||||||
this._extensionAgentRegistered = ChatContextKeys.extensionParticipantRegistered.bindTo(this.contextKeyService);
|
this._extensionAgentRegistered = ChatContextKeys.extensionParticipantRegistered.bindTo(this.contextKeyService);
|
||||||
this._defaultAgentRegistered = ChatContextKeys.panelParticipantRegistered.bindTo(this.contextKeyService);
|
this._defaultAgentRegistered = ChatContextKeys.panelParticipantRegistered.bindTo(this.contextKeyService);
|
||||||
this._editingAgentRegistered = ChatContextKeys.editingParticipantRegistered.bindTo(this.contextKeyService);
|
|
||||||
this._register(contextKeyService.onDidChangeContext((e) => {
|
this._register(contextKeyService.onDidChangeContext((e) => {
|
||||||
if (e.affectsSome(this._agentsContextKeys)) {
|
if (e.affectsSome(this._agentsContextKeys)) {
|
||||||
this._updateContextKeys();
|
this._updateContextKeys();
|
||||||
|
@ -295,7 +294,6 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateContextKeys(): void {
|
private _updateContextKeys(): void {
|
||||||
let editingAgentRegistered = false;
|
|
||||||
let extensionAgentRegistered = false;
|
let extensionAgentRegistered = false;
|
||||||
let defaultAgentRegistered = false;
|
let defaultAgentRegistered = false;
|
||||||
let toolsAgentRegistered = false;
|
let toolsAgentRegistered = false;
|
||||||
|
@ -304,16 +302,14 @@ export class ChatAgentService extends Disposable implements IChatAgentService {
|
||||||
if (!agent.isCore) {
|
if (!agent.isCore) {
|
||||||
extensionAgentRegistered = true;
|
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;
|
toolsAgentRegistered = true;
|
||||||
} else if (agent.modes.includes(ChatModeKind.Edit)) {
|
|
||||||
editingAgentRegistered = true;
|
|
||||||
} else {
|
} else {
|
||||||
defaultAgentRegistered = true;
|
defaultAgentRegistered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._editingAgentRegistered.set(editingAgentRegistered);
|
|
||||||
this._defaultAgentRegistered.set(defaultAgentRegistered);
|
this._defaultAgentRegistered.set(defaultAgentRegistered);
|
||||||
this._extensionAgentRegistered.set(extensionAgentRegistered);
|
this._extensionAgentRegistered.set(extensionAgentRegistered);
|
||||||
if (toolsAgentRegistered !== this._hasToolsAgent) {
|
if (toolsAgentRegistered !== this._hasToolsAgent) {
|
||||||
|
|
|
@ -42,7 +42,6 @@ export namespace ChatContextKeys {
|
||||||
|
|
||||||
export const extensionParticipantRegistered = new RawContextKey<boolean>('chatPanelExtensionParticipantRegistered', false, { type: 'boolean', description: localize('chatPanelExtensionParticipantRegistered', "True when a default chat participant is registered for the panel from an extension.") });
|
export const extensionParticipantRegistered = new RawContextKey<boolean>('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<boolean>('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") });
|
export const panelParticipantRegistered = new RawContextKey<boolean>('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") });
|
||||||
export const editingParticipantRegistered = new RawContextKey<boolean>('chatEditingParticipantRegistered', false, { type: 'boolean', description: localize('chatEditingParticipantRegistered', "True when a default chat participant is registered for editing.") });
|
|
||||||
export const chatEditingCanUndo = new RawContextKey<boolean>('chatEditingCanUndo', false, { type: 'boolean', description: localize('chatEditingCanUndo', "True when it is possible to undo an interaction in the editing panel.") });
|
export const chatEditingCanUndo = new RawContextKey<boolean>('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<boolean>('chatEditingCanRedo', false, { type: 'boolean', description: localize('chatEditingCanRedo', "True when it is possible to redo an interaction in the editing panel.") });
|
export const chatEditingCanRedo = new RawContextKey<boolean>('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<boolean>('chatExtensionInvalid', false, { type: 'boolean', description: localize('chatExtensionInvalid', "True when the installed chat extension is invalid and needs to be updated.") });
|
export const extensionInvalid = new RawContextKey<boolean>('chatExtensionInvalid', false, { type: 'boolean', description: localize('chatExtensionInvalid', "True when the installed chat extension is invalid and needs to be updated.") });
|
||||||
|
|
Loading…
Reference in New Issue