mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
rename mode to agent (#272282)
* rename mode to agent * update * update * fix tests * update * update * update * update * update * update
This commit is contained in:
committed by
GitHub
parent
9d45ef57fa
commit
ab73e46a31
@@ -38,12 +38,14 @@
|
||||
"configuration": "./language-configuration.json"
|
||||
},
|
||||
{
|
||||
"id": "chatmode",
|
||||
"id": "agent",
|
||||
"aliases": [
|
||||
"Chat Mode",
|
||||
"chat mode"
|
||||
"Agent",
|
||||
"chat agent"
|
||||
],
|
||||
"extensions": [
|
||||
".agent.md",
|
||||
".vscode-agent.md",
|
||||
".chatmode.md"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
|
||||
@@ -517,7 +517,7 @@ export class PromptsSynchronizer extends AbstractSynchroniser implements IUserDa
|
||||
for (const entry of stat.children || []) {
|
||||
const resource = entry.resource;
|
||||
const path = resource.path;
|
||||
if (['.prompt.md', '.instructions.md', '.chatmode.md'].some(ext => path.endsWith(ext))) {
|
||||
if (['.prompt.md', '.instructions.md', '.chatmode.md', '.vscode-agent.md'].some(ext => path.endsWith(ext))) {
|
||||
const key = this.extUri.relativePath(this.promptsFolder, resource)!;
|
||||
const content = await this.fileService.readFile(resource);
|
||||
prompts[key] = content;
|
||||
|
||||
@@ -1788,7 +1788,7 @@ export async function handleCurrentEditingSession(currentEditingSession: IChatEd
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we can switch the chat mode, based on whether the user had to agree to clear the session, false to cancel.
|
||||
* Returns whether we can switch the agent, based on whether the user had to agree to clear the session, false to cancel.
|
||||
*/
|
||||
export async function handleModeSwitch(
|
||||
accessor: ServicesAccessor,
|
||||
@@ -1806,7 +1806,7 @@ export async function handleModeSwitch(
|
||||
const needToClearEdits = (!configurationService.getValue(ChatConfiguration.Edits2Enabled) && (fromMode === ChatModeKind.Edit || toMode === ChatModeKind.Edit)) && requestCount > 0;
|
||||
if (needToClearEdits) {
|
||||
// If not using edits2 and switching into or out of edit mode, ask to discard the session
|
||||
const phrase = localize('switchMode.confirmPhrase', "Switching chat modes will end your current edit session.");
|
||||
const phrase = localize('switchMode.confirmPhrase', "Switching agents will end your current edit session.");
|
||||
|
||||
const currentEdits = editingSession.entries.get();
|
||||
const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === ModifiedFileEntryState.Modified);
|
||||
@@ -1819,7 +1819,7 @@ export async function handleModeSwitch(
|
||||
} else {
|
||||
const confirmation = await dialogService.confirm({
|
||||
title: localize('agent.newSession', "Start new session?"),
|
||||
message: localize('agent.newSessionMessage', "Changing the chat mode will end your current edit session. Would you like to change the chat mode?"),
|
||||
message: localize('agent.newSessionMessage', "Changing the agent will end your current edit session. Would you like to change the agent?"),
|
||||
primaryButton: localize('agent.newSession.confirm', "Yes"),
|
||||
type: 'info'
|
||||
});
|
||||
|
||||
@@ -280,9 +280,9 @@ export interface IToggleChatModeArgs {
|
||||
|
||||
type ChatModeChangeClassification = {
|
||||
owner: 'digitarald';
|
||||
comment: 'Reporting when Chat mode is switched between different modes';
|
||||
fromMode?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The previous chat mode' };
|
||||
toMode?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The new chat mode' };
|
||||
comment: 'Reporting when agent is switched between different modes';
|
||||
fromMode?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The previous agent' };
|
||||
toMode?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The new agent' };
|
||||
requestCount?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of requests in the current chat session'; 'isMeasurement': true };
|
||||
};
|
||||
|
||||
@@ -299,7 +299,7 @@ class ToggleChatModeAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: ToggleChatModeAction.ID,
|
||||
title: localize2('interactive.toggleAgent.label', "Switch to Next Chat Mode"),
|
||||
title: localize2('interactive.toggleAgent.label', "Switch to Next Agent"),
|
||||
f1: true,
|
||||
category: CHAT_CATEGORY,
|
||||
precondition: ContextKeyExpr.and(
|
||||
|
||||
@@ -157,8 +157,8 @@ class ConfigureToolsAction extends Action2 {
|
||||
description = localize('chat.tools.description.session', "The selected tools were configured by a prompt command and only apply to this chat session.");
|
||||
break;
|
||||
case ToolsScope.Mode:
|
||||
placeholder = localize('chat.tools.placeholder.mode', "Select tools for this chat mode");
|
||||
description = localize('chat.tools.description.mode', "The selected tools are configured by the '{0}' chat mode. Changes to the tools will be applied to the mode file as well.", widget.input.currentModeObs.get().label);
|
||||
placeholder = localize('chat.tools.placeholder.mode', "Select tools for this agent");
|
||||
description = localize('chat.tools.description.mode', "The selected tools are configured by the '{0}' agent. Changes to the tools will be applied to the agent file as well.", widget.input.currentModeObs.get().label);
|
||||
break;
|
||||
case ToolsScope.Global:
|
||||
placeholder = localize('chat.tools.placeholder.global', "Select tools that are available to chat.");
|
||||
|
||||
@@ -51,9 +51,9 @@ import { ILanguageModelsService, LanguageModelsService } from '../common/languag
|
||||
import { ILanguageModelStatsService, LanguageModelStatsService } from '../common/languageModelStats.js';
|
||||
import { ILanguageModelToolsService } from '../common/languageModelToolsService.js';
|
||||
import { PromptsConfig } from '../common/promptSyntax/config/config.js';
|
||||
import { INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, INSTRUCTION_FILE_EXTENSION, MODE_DEFAULT_SOURCE_FOLDER, MODE_FILE_EXTENSION, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, INSTRUCTION_FILE_EXTENSION, LEGACY_MODE_DEFAULT_SOURCE_FOLDER, LEGACY_MODE_FILE_EXTENSION, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { PromptLanguageFeaturesProvider } from '../common/promptSyntax/promptFileContributions.js';
|
||||
import { INSTRUCTIONS_DOCUMENTATION_URL, MODE_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../common/promptSyntax/promptTypes.js';
|
||||
import { INSTRUCTIONS_DOCUMENTATION_URL, AGENT_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../common/promptSyntax/promptTypes.js';
|
||||
import { IPromptsService } from '../common/promptSyntax/service/promptsService.js';
|
||||
import { PromptsService } from '../common/promptSyntax/service/promptsServiceImpl.js';
|
||||
import { LanguageModelToolsExtensionPointHandler } from '../common/tools/languageModelToolsContribution.js';
|
||||
@@ -565,22 +565,23 @@ configurationRegistry.registerConfiguration({
|
||||
markdownDescription: nls.localize(
|
||||
'chat.mode.config.locations.description',
|
||||
"Specify location(s) of custom chat mode files (`*{0}`). [Learn More]({1}).\n\nRelative paths are resolved from the root folder(s) of your workspace.",
|
||||
MODE_FILE_EXTENSION,
|
||||
MODE_DOCUMENTATION_URL,
|
||||
LEGACY_MODE_FILE_EXTENSION,
|
||||
AGENT_DOCUMENTATION_URL,
|
||||
),
|
||||
default: {
|
||||
[MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
[LEGACY_MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
},
|
||||
deprecationMessage: nls.localize('chat.mode.config.locations.deprecated', "This setting is deprecated and will be removed in future releases. Chat modes are now called agents and are located in `.github/agents`"),
|
||||
additionalProperties: { type: 'boolean' },
|
||||
unevaluatedProperties: { type: 'boolean' },
|
||||
restricted: true,
|
||||
tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'],
|
||||
examples: [
|
||||
{
|
||||
[MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
[LEGACY_MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
},
|
||||
{
|
||||
[MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
[LEGACY_MODE_DEFAULT_SOURCE_FOLDER]: true,
|
||||
'/Users/vscode/repos/chatmodes': true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -785,14 +785,14 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
let modeLabel = '';
|
||||
switch (this.currentModeKind) {
|
||||
case ChatModeKind.Agent:
|
||||
modeLabel = localize('chatInput.mode.agent', "(Agent Mode), edit files in your workspace.");
|
||||
modeLabel = localize('chatInput.mode.agent', "(Agent), edit files in your workspace.");
|
||||
break;
|
||||
case ChatModeKind.Edit:
|
||||
modeLabel = localize('chatInput.mode.edit', "(Edit Mode), edit files in your workspace.");
|
||||
modeLabel = localize('chatInput.mode.edit', "(Edit), edit files in your workspace.");
|
||||
break;
|
||||
case ChatModeKind.Ask:
|
||||
default:
|
||||
modeLabel = localize('chatInput.mode.ask', "(Ask Mode), ask questions or type / for topics.");
|
||||
modeLabel = localize('chatInput.mode.ask', "(Ask), ask questions or type / for topics.");
|
||||
break;
|
||||
}
|
||||
if (verbose) {
|
||||
|
||||
@@ -1268,7 +1268,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
|
||||
const params = new URLSearchParams(url.query);
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'url', detail: params.get('referrer') ?? undefined });
|
||||
|
||||
const modeParam = params.get('mode');
|
||||
const modeParam = params.get('agent') ?? params.get('mode');
|
||||
let modeToUse: ChatModeKind | string | undefined;
|
||||
if (modeParam) {
|
||||
// check if the given param is a valid mode ID
|
||||
|
||||
@@ -2957,7 +2957,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||
this.agentInInput.set(!!currentAgent);
|
||||
}
|
||||
|
||||
private async _applyPromptMetadata({ mode, tools, model }: PromptHeader, requestInput: IChatRequestInputOptions): Promise<void> {
|
||||
private async _applyPromptMetadata({ agent: mode, tools, model }: PromptHeader, requestInput: IChatRequestInputOptions): Promise<void> {
|
||||
|
||||
const currentMode = this.input.currentModeObs.get();
|
||||
|
||||
@@ -2965,7 +2965,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||
mode = ChatModeKind.Agent;
|
||||
}
|
||||
|
||||
// switch to appropriate chat mode if needed
|
||||
// switch to appropriate agent if needed
|
||||
if (mode && mode !== currentMode.name) {
|
||||
// Find the mode object to get its kind
|
||||
const chatMode = this.chatModeService.findModeByName(mode);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { PromptsType } from '../../common/promptSyntax/promptTypes.js';
|
||||
import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
|
||||
import { ChatViewId } from '../chat.js';
|
||||
|
||||
abstract class ConfigModeActionImpl extends Action2 {
|
||||
abstract class ConfigAgentActionImpl extends Action2 {
|
||||
public override async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const openerService = accessor.get(IOpenerService);
|
||||
const instaService = accessor.get(IInstantiationService);
|
||||
@@ -25,26 +25,26 @@ abstract class ConfigModeActionImpl extends Action2 {
|
||||
const pickers = instaService.createInstance(PromptFilePickers);
|
||||
|
||||
const placeholder = localize(
|
||||
'commands.mode.select-dialog.placeholder',
|
||||
'Select the chat mode file to open'
|
||||
'commands.agent.select-dialog.placeholder',
|
||||
'Select the agent file to open'
|
||||
);
|
||||
|
||||
const result = await pickers.selectPromptFile({ placeholder, type: PromptsType.mode, optionEdit: false });
|
||||
const result = await pickers.selectPromptFile({ placeholder, type: PromptsType.agent, optionEdit: false });
|
||||
if (result !== undefined) {
|
||||
await openerService.open(result.promptFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Separate action `Configure Mode` link in the mode picker.
|
||||
// Separate action `Configure Agent` link in the agent picker.
|
||||
|
||||
const PICKER_CONFIGURE_MODES_ACTION_ID = 'workbench.action.chat.picker.configmode';
|
||||
const PICKER_CONFIGURE_AGENTS_ACTION_ID = 'workbench.action.chat.picker.configagents';
|
||||
|
||||
class PickerConfigModeAction extends ConfigModeActionImpl {
|
||||
class PickerConfigAgentAction extends ConfigAgentActionImpl {
|
||||
constructor() {
|
||||
super({
|
||||
id: PICKER_CONFIGURE_MODES_ACTION_ID,
|
||||
title: localize2('select-mode', "Configure Modes..."),
|
||||
id: PICKER_CONFIGURE_AGENTS_ACTION_ID,
|
||||
title: localize2('select-agent', "Configure Agents..."),
|
||||
category: CHAT_CATEGORY,
|
||||
f1: false,
|
||||
menu: {
|
||||
@@ -55,16 +55,16 @@ class PickerConfigModeAction extends ConfigModeActionImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* Action ID for the `Configure Custom Chat Mode` action.
|
||||
* Action ID for the `Configure Agent` action.
|
||||
*/
|
||||
const CONFIGURE_MODES_ACTION_ID = 'workbench.action.chat.manage.mode';
|
||||
const CONFIGURE_AGENTS_ACTION_ID = 'workbench.action.chat.manage.agents';
|
||||
|
||||
class ManageModeAction extends ConfigModeActionImpl {
|
||||
class ManageAgentsAction extends ConfigAgentActionImpl {
|
||||
constructor() {
|
||||
super({
|
||||
id: CONFIGURE_MODES_ACTION_ID,
|
||||
title: localize2('configure-modes', "Configure Chat Modes..."),
|
||||
shortTitle: localize('configure-modes.short', "Chat Modes"),
|
||||
id: CONFIGURE_AGENTS_ACTION_ID,
|
||||
title: localize2('configure-agents', "Configure Agents..."),
|
||||
shortTitle: localize('configure-agents.short', "Agents"),
|
||||
icon: Codicon.bookmark,
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
|
||||
@@ -85,7 +85,7 @@ class ManageModeAction extends ConfigModeActionImpl {
|
||||
/**
|
||||
* Helper to register all the `Run Current Prompt` actions.
|
||||
*/
|
||||
export function registerChatModeActions(): void {
|
||||
registerAction2(ManageModeAction);
|
||||
registerAction2(PickerConfigModeAction);
|
||||
export function registerAgentActions(): void {
|
||||
registerAction2(ManageAgentsAction);
|
||||
registerAction2(PickerConfigAgentAction);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class AbstractNewPromptFileAction extends Action2 {
|
||||
Severity.Info,
|
||||
localize(
|
||||
'workbench.command.prompts.create.user.enable-sync-notification',
|
||||
"Do you want to backup and sync your user prompt, instruction and mode files with Setting Sync?'",
|
||||
"Do you want to backup and sync your user prompt, instruction and agent files with Setting Sync?'",
|
||||
),
|
||||
[
|
||||
{
|
||||
@@ -141,13 +141,13 @@ class AbstractNewPromptFileAction extends Action2 {
|
||||
}
|
||||
|
||||
private getDefaultContentSnippet(promptType: PromptsType, chatModeService: IChatModeService): string {
|
||||
const modes = chatModeService.getModes();
|
||||
const modeNames = modes.builtin.map(mode => mode.name).join(',') + (modes.custom.length ? (',' + modes.custom.map(mode => mode.name).join(',')) : '');
|
||||
const agents = chatModeService.getModes();
|
||||
const agentNames = agents.builtin.map(agent => agent.name).join(',') + (agents.custom.length ? (',' + agents.custom.map(agent => agent.name).join(',')) : '');
|
||||
switch (promptType) {
|
||||
case PromptsType.prompt:
|
||||
return [
|
||||
`---`,
|
||||
`mode: \${1|${modeNames}|}`,
|
||||
`agent: \${1|${agentNames}|}`,
|
||||
`---`,
|
||||
`\${2:Define the task to achieve, including specific requirements, constraints, and success criteria.}`,
|
||||
].join('\n');
|
||||
@@ -158,13 +158,13 @@ class AbstractNewPromptFileAction extends Action2 {
|
||||
`---`,
|
||||
`\${2:Provide project context and coding guidelines that AI should follow when generating code, answering questions, or reviewing changes.}`,
|
||||
].join('\n');
|
||||
case PromptsType.mode:
|
||||
case PromptsType.agent:
|
||||
return [
|
||||
`---`,
|
||||
`description: '\${1:Description of the custom chat mode.}'`,
|
||||
`description: '\${1:Description of the agent.}'`,
|
||||
`tools: []`,
|
||||
`---`,
|
||||
`\${2:Define the purpose of this chat mode and how AI should behave: response style, available tools, focus areas, and any mode-specific instructions or constraints.}`,
|
||||
`\${2:Define the purpose of this agent and how AI should behave: response style, available tools, focus areas, and any agent-specific instructions or constraints.}`,
|
||||
].join('\n');
|
||||
default:
|
||||
throw new Error(`Unknown prompt type: ${promptType}`);
|
||||
@@ -176,7 +176,7 @@ class AbstractNewPromptFileAction extends Action2 {
|
||||
|
||||
export const NEW_PROMPT_COMMAND_ID = 'workbench.command.new.prompt';
|
||||
export const NEW_INSTRUCTIONS_COMMAND_ID = 'workbench.command.new.instructions';
|
||||
export const NEW_MODE_COMMAND_ID = 'workbench.command.new.mode';
|
||||
export const NEW_AGENT_COMMAND_ID = 'workbench.command.new.agent';
|
||||
|
||||
class NewPromptFileAction extends AbstractNewPromptFileAction {
|
||||
constructor() {
|
||||
@@ -190,9 +190,9 @@ class NewInstructionsFileAction extends AbstractNewPromptFileAction {
|
||||
}
|
||||
}
|
||||
|
||||
class NewModeFileAction extends AbstractNewPromptFileAction {
|
||||
class NewAgentFileAction extends AbstractNewPromptFileAction {
|
||||
constructor() {
|
||||
super(NEW_MODE_COMMAND_ID, localize('commands.new.mode.local.title', "New Mode File..."), PromptsType.mode);
|
||||
super(NEW_AGENT_COMMAND_ID, localize('commands.new.agent.local.title', "New Agent File..."), PromptsType.agent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +242,6 @@ class NewUntitledPromptFileAction extends Action2 {
|
||||
export function registerNewPromptFileActions(): void {
|
||||
registerAction2(NewPromptFileAction);
|
||||
registerAction2(NewInstructionsFileAction);
|
||||
registerAction2(NewModeFileAction);
|
||||
registerAction2(NewAgentFileAction);
|
||||
registerAction2(NewUntitledPromptFileAction);
|
||||
}
|
||||
|
||||
@@ -78,8 +78,8 @@ function getPlaceholderStringForNew(type: PromptsType): string {
|
||||
return localize('askForInstructionsFileName.placeholder', "Enter the name of the instructions file");
|
||||
case PromptsType.prompt:
|
||||
return localize('askForPromptFileName.placeholder', "Enter the name of the prompt file");
|
||||
case PromptsType.mode:
|
||||
return localize('askForModeFileName.placeholder', "Enter the name of the custom chat mode file");
|
||||
case PromptsType.agent:
|
||||
return localize('askForAgentFileName.placeholder', "Enter the name of the agent file");
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -91,8 +91,8 @@ function getPlaceholderStringForRename(type: PromptsType): string {
|
||||
return localize('askForRenamedInstructionsFileName.placeholder', "Enter a new name of the instructions file");
|
||||
case PromptsType.prompt:
|
||||
return localize('askForRenamedPromptFileName.placeholder', "Enter a new name of the prompt file");
|
||||
case PromptsType.mode:
|
||||
return localize('askForRenamedModeFileName.placeholder', "Enter a new name of the custom chat mode file");
|
||||
case PromptsType.agent:
|
||||
return localize('askForRenamedAgentFileName.placeholder', "Enter a new name of the agent file");
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
|
||||
@@ -109,8 +109,8 @@ function getPlaceholderStringforNew(type: PromptsType): string {
|
||||
return localize('workbench.command.instructions.create.location.placeholder', "Select a location to create the instructions file in...");
|
||||
case PromptsType.prompt:
|
||||
return localize('workbench.command.prompt.create.location.placeholder', "Select a location to create the prompt file in...");
|
||||
case PromptsType.mode:
|
||||
return localize('workbench.command.mode.create.location.placeholder', "Select a location to create the mode file in...");
|
||||
case PromptsType.agent:
|
||||
return localize('workbench.command.agent.create.location.placeholder', "Select a location to create the agent file in...");
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -123,8 +123,8 @@ function getPlaceholderStringforMove(type: PromptsType, isMove: boolean): string
|
||||
return localize('instructions.move.location.placeholder', "Select a location to move the instructions file to...");
|
||||
case PromptsType.prompt:
|
||||
return localize('prompt.move.location.placeholder', "Select a location to move the prompt file to...");
|
||||
case PromptsType.mode:
|
||||
return localize('mode.move.location.placeholder', "Select a location to move the mode file to...");
|
||||
case PromptsType.agent:
|
||||
return localize('agent.move.location.placeholder', "Select a location to move the agent file to...");
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -134,8 +134,8 @@ function getPlaceholderStringforMove(type: PromptsType, isMove: boolean): string
|
||||
return localize('instructions.copy.location.placeholder', "Select a location to copy the instructions file to...");
|
||||
case PromptsType.prompt:
|
||||
return localize('prompt.copy.location.placeholder', "Select a location to copy the prompt file to...");
|
||||
case PromptsType.mode:
|
||||
return localize('mode.copy.location.placeholder', "Select a location to copy the mode file to...");
|
||||
case PromptsType.agent:
|
||||
return localize('agent.copy.location.placeholder', "Select a location to copy the agent file to...");
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -177,8 +177,8 @@ function getLearnLabel(type: PromptsType): string {
|
||||
return localize('commands.prompts.create.ask-folder.empty.docs-label', 'Learn how to configure reusable prompts');
|
||||
case PromptsType.instructions:
|
||||
return localize('commands.instructions.create.ask-folder.empty.docs-label', 'Learn how to configure reusable instructions');
|
||||
case PromptsType.mode:
|
||||
return localize('commands.mode.create.ask-folder.empty.docs-label', 'Learn how to configure custom chat modes');
|
||||
case PromptsType.agent:
|
||||
return localize('commands.agent.create.ask-folder.empty.docs-label', 'Learn how to configure custom agents');
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -190,8 +190,8 @@ function getMissingSourceFolderString(type: PromptsType): string {
|
||||
return localize('commands.instructions.create.ask-folder.empty.placeholder', 'No instruction source folders found.');
|
||||
case PromptsType.prompt:
|
||||
return localize('commands.prompts.create.ask-folder.empty.placeholder', 'No prompt source folders found.');
|
||||
case PromptsType.mode:
|
||||
return localize('commands.mode.create.ask-folder.empty.placeholder', 'No custom chat mode source folders found.');
|
||||
case PromptsType.agent:
|
||||
return localize('commands.agent.create.ask-folder.empty.placeholder', 'No agent source folders found.');
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import { IOpenerService } from '../../../../../../platform/opener/common/opener.
|
||||
import { IDialogService } from '../../../../../../platform/dialogs/common/dialogs.js';
|
||||
import { ICommandService } from '../../../../../../platform/commands/common/commands.js';
|
||||
import { getCleanPromptName } from '../../../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { PromptsType, INSTRUCTIONS_DOCUMENTATION_URL, MODE_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../../../common/promptSyntax/promptTypes.js';
|
||||
import { NEW_PROMPT_COMMAND_ID, NEW_INSTRUCTIONS_COMMAND_ID, NEW_MODE_COMMAND_ID } from '../newPromptFileActions.js';
|
||||
import { PromptsType, INSTRUCTIONS_DOCUMENTATION_URL, AGENT_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../../../common/promptSyntax/promptTypes.js';
|
||||
import { NEW_PROMPT_COMMAND_ID, NEW_INSTRUCTIONS_COMMAND_ID, NEW_AGENT_COMMAND_ID } from '../newPromptFileActions.js';
|
||||
import { IKeyMods, IQuickInputButton, IQuickInputService, IQuickPick, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from '../../../../../../platform/quickinput/common/quickInput.js';
|
||||
import { askForPromptFileName } from './askForPromptName.js';
|
||||
import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
@@ -140,17 +140,17 @@ const UPDATE_INSTRUCTIONS_OPTION: IPromptPickerQuickPickItem = Object.freeze({
|
||||
/**
|
||||
* A quick pick item that starts the 'New Instructions File' command.
|
||||
*/
|
||||
const NEW_MODE_FILE_OPTION: IPromptPickerQuickPickItem = Object.freeze({
|
||||
const NEW_AGENT_FILE_OPTION: IPromptPickerQuickPickItem = Object.freeze({
|
||||
type: 'item',
|
||||
label: `$(plus) ${localize(
|
||||
'commands.new-modefile.select-dialog.label',
|
||||
'Create new custom chat mode file...',
|
||||
'commands.new-agentfile.select-dialog.label',
|
||||
'Create new agent file...',
|
||||
)}`,
|
||||
value: URI.parse(MODE_DOCUMENTATION_URL),
|
||||
value: URI.parse(AGENT_DOCUMENTATION_URL),
|
||||
pickable: false,
|
||||
alwaysShow: true,
|
||||
buttons: [HELP_BUTTON],
|
||||
commandId: NEW_MODE_COMMAND_ID,
|
||||
commandId: NEW_AGENT_COMMAND_ID,
|
||||
});
|
||||
|
||||
|
||||
@@ -350,8 +350,8 @@ export class PromptFilePickers {
|
||||
return [NEW_PROMPT_FILE_OPTION];
|
||||
case PromptsType.instructions:
|
||||
return [NEW_INSTRUCTIONS_FILE_OPTION, UPDATE_INSTRUCTIONS_OPTION];
|
||||
case PromptsType.mode:
|
||||
return [NEW_MODE_FILE_OPTION];
|
||||
case PromptsType.agent:
|
||||
return [NEW_AGENT_FILE_OPTION];
|
||||
default:
|
||||
throw new Error(`Unknown prompt type '${type}'.`);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { registerAttachPromptActions } from './attachInstructionsAction.js';
|
||||
import { registerChatModeActions } from './chatModeActions.js';
|
||||
import { registerAgentActions } from './chatModeActions.js';
|
||||
import { registerRunPromptActions } from './runPromptAction.js';
|
||||
import { registerSaveToPromptActions } from './saveToPromptAction.js';
|
||||
import { registerNewPromptFileActions } from './newPromptFileActions.js';
|
||||
@@ -17,6 +17,6 @@ export function registerPromptActions(): void {
|
||||
registerRunPromptActions();
|
||||
registerAttachPromptActions();
|
||||
registerSaveToPromptActions();
|
||||
registerChatModeActions();
|
||||
registerAgentActions();
|
||||
registerNewPromptFileActions();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ export class PromptUrlHandler extends Disposable implements IWorkbenchContributi
|
||||
promptType = PromptsType.instructions;
|
||||
break;
|
||||
case 'chat-mode/install':
|
||||
promptType = PromptsType.mode;
|
||||
case 'chat-agent/install':
|
||||
promptType = PromptsType.agent;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@@ -135,7 +136,7 @@ export class PromptUrlHandler extends Disposable implements IWorkbenchContributi
|
||||
message = localize('confirmInstallInstructions', "An external application wants to create an instructions file with content from a URL. Do you want to continue by selecting a destination folder and name?");
|
||||
break;
|
||||
default:
|
||||
message = localize('confirmInstallMode', "An external application wants to create a chat mode with content from a URL. Do you want to continue by selecting a destination folder and name?");
|
||||
message = localize('confirmInstallAgent', "An external application wants to create an agent with content from a URL. Do you want to continue by selecting a destination folder and name?");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ export namespace ChatContextKeys {
|
||||
export const inChatSession = new RawContextKey<boolean>('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") });
|
||||
export const inChatEditor = new RawContextKey<boolean>('inChatEditor', false, { type: 'boolean', description: localize('inChatEditor', "Whether focus is in a chat editor.") });
|
||||
export const hasPromptFile = new RawContextKey<boolean>('chatPromptFileAttached', false, { type: 'boolean', description: localize('chatPromptFileAttachedContextDescription', "True when the chat has a prompt file attached.") });
|
||||
export const chatModeKind = new RawContextKey<ChatModeKind>('chatMode', ChatModeKind.Ask, { type: 'string', description: localize('chatMode', "The 'kind' of the current chat mode- Agent for custom modes.") });
|
||||
export const chatToolCount = new RawContextKey<number>('chatToolCount', 0, { type: 'number', description: localize('chatToolCount', "The number of tools available in the current chat mode.") });
|
||||
export const chatModeKind = new RawContextKey<ChatModeKind>('chatAgentKind', ChatModeKind.Ask, { type: 'string', description: localize('agentKind', "The 'kind' of the current agent.") });
|
||||
export const chatToolCount = new RawContextKey<number>('chatToolCount', 0, { type: 'number', description: localize('chatToolCount', "The number of tools available in the current agent.") });
|
||||
export const chatToolGroupingThreshold = new RawContextKey<number>('chat.toolGroupingThreshold', 0, { type: 'number', description: localize('chatToolGroupingThreshold', "The number of tools at which we start doing virtual grouping.") });
|
||||
|
||||
export const supported = ContextKeyExpr.or(IsWebContext.negate(), RemoteNameContext.notEqualsTo(''), ContextKeyExpr.has('config.chat.experimental.serverlessWebEnabled'));
|
||||
@@ -84,7 +84,7 @@ export namespace ChatContextKeys {
|
||||
};
|
||||
|
||||
export const Modes = {
|
||||
hasCustomChatModes: new RawContextKey<boolean>('chatHasCustomChatModes', false, { type: 'boolean', description: localize('chatHasCustomChatModes', "True when the chat has custom chat modes available.") }),
|
||||
hasCustomChatModes: new RawContextKey<boolean>('chatHasCustomAgents', false, { type: 'boolean', description: localize('chatHasAgents', "True when the chat has custom agents available.") }),
|
||||
};
|
||||
|
||||
export const panelLocation = new RawContextKey<ViewContainerLocation>('chatPanelLocation', undefined, { type: 'number', description: localize('chatPanelLocation', "The location of the chat panel.") });
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IChatAgentService } from './chatAgents.js';
|
||||
import { ChatContextKeys } from './chatContextKeys.js';
|
||||
import { ChatModeKind } from './constants.js';
|
||||
import { IHandOff } from './promptSyntax/service/newPromptsParser.js';
|
||||
import { IChatModeSource, ICustomChatMode, IPromptsService, PromptsStorage } from './promptSyntax/service/promptsService.js';
|
||||
import { IAgentSource, ICustomAgent, IPromptsService, PromptsStorage } from './promptSyntax/service/promptsService.js';
|
||||
|
||||
export const IChatModeService = createDecorator<IChatModeService>('chatModeService');
|
||||
export interface IChatModeService {
|
||||
@@ -58,7 +58,7 @@ export class ChatModeService extends Disposable implements IChatModeService {
|
||||
this.loadCachedModes();
|
||||
|
||||
void this.refreshCustomPromptModes(true);
|
||||
this._register(this.promptsService.onDidChangeCustomChatModes(() => {
|
||||
this._register(this.promptsService.onDidChangeCustomAgents(() => {
|
||||
void this.refreshCustomPromptModes(true);
|
||||
}));
|
||||
this._register(this.storageService.onWillSaveState(() => this.saveCachedModes()));
|
||||
@@ -80,7 +80,7 @@ export class ChatModeService extends Disposable implements IChatModeService {
|
||||
this.deserializeCachedModes(cachedCustomModes);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error(error, 'Failed to load cached custom chat modes');
|
||||
this.logService.error(error, 'Failed to load cached custom agents');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,20 +94,20 @@ export class ChatModeService extends Disposable implements IChatModeService {
|
||||
if (isCachedChatModeData(cachedMode) && cachedMode.uri) {
|
||||
try {
|
||||
const uri = URI.revive(cachedMode.uri);
|
||||
const customChatMode: ICustomChatMode = {
|
||||
const customChatMode: ICustomAgent = {
|
||||
uri,
|
||||
name: cachedMode.name,
|
||||
description: cachedMode.description,
|
||||
tools: cachedMode.customTools,
|
||||
model: cachedMode.model,
|
||||
modeInstructions: cachedMode.modeInstructions ?? { content: cachedMode.body ?? '', toolReferences: [] },
|
||||
agentInstructions: cachedMode.modeInstructions ?? { content: cachedMode.body ?? '', toolReferences: [] },
|
||||
handOffs: cachedMode.handOffs,
|
||||
source: reviveChatModeSource(cachedMode.source) ?? { storage: PromptsStorage.local }
|
||||
};
|
||||
const instance = new CustomChatMode(customChatMode);
|
||||
this._customModeInstances.set(uri.toString(), instance);
|
||||
} catch (error) {
|
||||
this.logService.error(error, 'Failed to revive cached custom chat mode');
|
||||
this.logService.error(error, 'Failed to revive cached custom agent');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,13 +120,13 @@ export class ChatModeService extends Disposable implements IChatModeService {
|
||||
const modesToCache = Array.from(this._customModeInstances.values());
|
||||
this.storageService.store(ChatModeService.CUSTOM_MODES_STORAGE_KEY, modesToCache, StorageScope.WORKSPACE, StorageTarget.MACHINE);
|
||||
} catch (error) {
|
||||
this.logService.warn('Failed to save cached custom chat modes', error);
|
||||
this.logService.warn('Failed to save cached custom agents', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshCustomPromptModes(fireChangeEvent?: boolean): Promise<void> {
|
||||
try {
|
||||
const customModes = await this.promptsService.getCustomChatModes(CancellationToken.None);
|
||||
const customModes = await this.promptsService.getCustomAgents(CancellationToken.None);
|
||||
|
||||
// Create a new set of mode instances, reusing existing ones where possible
|
||||
const seenUris = new Set<string>();
|
||||
@@ -155,7 +155,7 @@ export class ChatModeService extends Disposable implements IChatModeService {
|
||||
|
||||
this.hasCustomModes.set(this._customModeInstances.size > 0);
|
||||
} catch (error) {
|
||||
this.logService.error(error, 'Failed to load custom chat modes');
|
||||
this.logService.error(error, 'Failed to load custom agents');
|
||||
this._customModeInstances.clear();
|
||||
this.hasCustomModes.set(false);
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export interface IChatMode {
|
||||
readonly model?: IObservable<string | undefined>;
|
||||
readonly modeInstructions?: IObservable<IChatModeInstructions>;
|
||||
readonly uri?: IObservable<URI>;
|
||||
readonly source?: IChatModeSource;
|
||||
readonly source?: IAgentSource;
|
||||
}
|
||||
|
||||
export interface IVariableReference {
|
||||
@@ -262,7 +262,7 @@ export class CustomChatMode implements IChatMode {
|
||||
private readonly _uriObservable: ISettableObservable<URI>;
|
||||
private readonly _modelObservable: ISettableObservable<string | undefined>;
|
||||
private readonly _handoffsObservable: ISettableObservable<readonly IHandOff[] | undefined>;
|
||||
private _source: IChatModeSource;
|
||||
private _source: IAgentSource;
|
||||
|
||||
public readonly id: string;
|
||||
public readonly name: string;
|
||||
@@ -299,14 +299,14 @@ export class CustomChatMode implements IChatMode {
|
||||
return this._handoffsObservable;
|
||||
}
|
||||
|
||||
get source(): IChatModeSource {
|
||||
get source(): IAgentSource {
|
||||
return this._source;
|
||||
}
|
||||
|
||||
public readonly kind = ChatModeKind.Agent;
|
||||
|
||||
constructor(
|
||||
customChatMode: ICustomChatMode
|
||||
customChatMode: ICustomAgent
|
||||
) {
|
||||
this.id = customChatMode.uri.toString();
|
||||
this.name = customChatMode.name;
|
||||
@@ -314,7 +314,7 @@ export class CustomChatMode implements IChatMode {
|
||||
this._customToolsObservable = observableValue('customTools', customChatMode.tools);
|
||||
this._modelObservable = observableValue('model', customChatMode.model);
|
||||
this._handoffsObservable = observableValue('handOffs', customChatMode.handOffs);
|
||||
this._modeInstructions = observableValue('_modeInstructions', customChatMode.modeInstructions);
|
||||
this._modeInstructions = observableValue('_modeInstructions', customChatMode.agentInstructions);
|
||||
this._uriObservable = observableValue('uri', customChatMode.uri);
|
||||
this._source = customChatMode.source;
|
||||
}
|
||||
@@ -322,14 +322,14 @@ export class CustomChatMode implements IChatMode {
|
||||
/**
|
||||
* Updates the underlying data and triggers observable changes
|
||||
*/
|
||||
updateData(newData: ICustomChatMode): void {
|
||||
updateData(newData: ICustomAgent): void {
|
||||
transaction(tx => {
|
||||
// Note- name is derived from ID, it can't change
|
||||
this._descriptionObservable.set(newData.description, tx);
|
||||
this._customToolsObservable.set(newData.tools, tx);
|
||||
this._modelObservable.set(newData.model, tx);
|
||||
this._handoffsObservable.set(newData.handOffs, tx);
|
||||
this._modeInstructions.set(newData.modeInstructions, tx);
|
||||
this._modeInstructions.set(newData.agentInstructions, tx);
|
||||
this._uriObservable.set(newData.uri, tx);
|
||||
this._source = newData.source;
|
||||
});
|
||||
@@ -366,7 +366,7 @@ function isChatModeSourceData(value: unknown): value is IChatModeSourceData {
|
||||
return data.storage === PromptsStorage.local || data.storage === PromptsStorage.user;
|
||||
}
|
||||
|
||||
function serializeChatModeSource(source: IChatModeSource | undefined): IChatModeSourceData | undefined {
|
||||
function serializeChatModeSource(source: IAgentSource | undefined): IChatModeSourceData | undefined {
|
||||
if (!source) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -376,7 +376,7 @@ function serializeChatModeSource(source: IChatModeSource | undefined): IChatMode
|
||||
return { storage: source.storage };
|
||||
}
|
||||
|
||||
function reviveChatModeSource(data: IChatModeSourceData | undefined): IChatModeSource | undefined {
|
||||
function reviveChatModeSource(data: IChatModeSourceData | undefined): IAgentSource | undefined {
|
||||
if (!data) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export enum ChatConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* The "kind" of the chat mode- "Agent" for custom modes.
|
||||
* The "kind" of agents for custom agents.
|
||||
*/
|
||||
export enum ChatModeKind {
|
||||
Ask = 'ask',
|
||||
|
||||
@@ -19,7 +19,7 @@ interface IRawChatFileContribution {
|
||||
readonly description?: string; // reserved for future use
|
||||
}
|
||||
|
||||
type ChatContributionPoint = 'chatPromptFiles' | 'chatInstructions' | 'chatModes';
|
||||
type ChatContributionPoint = 'chatPromptFiles' | 'chatInstructions' | 'chatAgents';
|
||||
|
||||
function registerChatFilesExtensionPoint(point: ChatContributionPoint) {
|
||||
return extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawChatFileContribution[]>({
|
||||
@@ -60,13 +60,13 @@ function registerChatFilesExtensionPoint(point: ChatContributionPoint) {
|
||||
|
||||
const epPrompt = registerChatFilesExtensionPoint('chatPromptFiles');
|
||||
const epInstructions = registerChatFilesExtensionPoint('chatInstructions');
|
||||
const epModes = registerChatFilesExtensionPoint('chatModes');
|
||||
const epAgents = registerChatFilesExtensionPoint('chatAgents');
|
||||
|
||||
function pointToType(contributionPoint: ChatContributionPoint): PromptsType {
|
||||
switch (contributionPoint) {
|
||||
case 'chatPromptFiles': return PromptsType.prompt;
|
||||
case 'chatInstructions': return PromptsType.instructions;
|
||||
case 'chatModes': return PromptsType.mode;
|
||||
case 'chatAgents': return PromptsType.agent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +84,13 @@ export class ChatPromptFilesExtensionPointHandler implements IWorkbenchContribut
|
||||
) {
|
||||
this.handle(epPrompt, 'chatPromptFiles');
|
||||
this.handle(epInstructions, 'chatInstructions');
|
||||
this.handle(epModes, 'chatModes');
|
||||
this.handle(epAgents, 'chatAgents');
|
||||
}
|
||||
|
||||
private handle(extensionPoint: extensionsRegistry.IExtensionPoint<IRawChatFileContribution[]>, contributionPoint: ChatContributionPoint) {
|
||||
extensionPoint.setHandler((_extensions, delta) => {
|
||||
for (const ext of delta.added) {
|
||||
if (contributionPoint === 'chatModes') {
|
||||
if (contributionPoint === 'chatAgents') {
|
||||
checkProposedApiEnabled(ext.description, 'chatParticipantPrivate');
|
||||
}
|
||||
const type = pointToType(contributionPoint);
|
||||
|
||||
@@ -227,7 +227,7 @@ export function getPromptFileLocationsConfigKey(type: PromptsType): string {
|
||||
return PromptsConfig.INSTRUCTIONS_LOCATION_KEY;
|
||||
case PromptsType.prompt:
|
||||
return PromptsConfig.PROMPT_LOCATIONS_KEY;
|
||||
case PromptsType.mode:
|
||||
case PromptsType.agent:
|
||||
return PromptsConfig.MODE_LOCATION_KEY;
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
|
||||
@@ -20,7 +20,12 @@ export const INSTRUCTION_FILE_EXTENSION = '.instructions.md';
|
||||
/**
|
||||
* File extension for the modes files.
|
||||
*/
|
||||
export const MODE_FILE_EXTENSION = '.chatmode.md';
|
||||
export const LEGACY_MODE_FILE_EXTENSION = '.chatmode.md';
|
||||
|
||||
/**
|
||||
* File extension for the agent files.
|
||||
*/
|
||||
export const AGENT_FILE_EXTENSION = '.vscode-agent.md';
|
||||
|
||||
/**
|
||||
* Copilot custom instructions file name.
|
||||
@@ -41,7 +46,12 @@ export const INSTRUCTIONS_DEFAULT_SOURCE_FOLDER = '.github/instructions';
|
||||
/**
|
||||
* Default modes source folder.
|
||||
*/
|
||||
export const MODE_DEFAULT_SOURCE_FOLDER = '.github/chatmodes';
|
||||
export const LEGACY_MODE_DEFAULT_SOURCE_FOLDER = '.github/chatmodes';
|
||||
|
||||
/**
|
||||
* Agents folder.
|
||||
*/
|
||||
export const AGENTS_SOURCE_FOLDER = '.github/agents';
|
||||
|
||||
/**
|
||||
* Gets the prompt file type from the provided path.
|
||||
@@ -57,8 +67,8 @@ export function getPromptFileType(fileUri: URI): PromptsType | undefined {
|
||||
return PromptsType.instructions;
|
||||
}
|
||||
|
||||
if (filename.endsWith(MODE_FILE_EXTENSION)) {
|
||||
return PromptsType.mode;
|
||||
if (filename.endsWith(LEGACY_MODE_FILE_EXTENSION) || filename.endsWith(AGENT_FILE_EXTENSION)) {
|
||||
return PromptsType.agent;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -77,8 +87,8 @@ export function getPromptFileExtension(type: PromptsType): string {
|
||||
return INSTRUCTION_FILE_EXTENSION;
|
||||
case PromptsType.prompt:
|
||||
return PROMPT_FILE_EXTENSION;
|
||||
case PromptsType.mode:
|
||||
return MODE_FILE_EXTENSION;
|
||||
case PromptsType.agent:
|
||||
return AGENT_FILE_EXTENSION;
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -90,8 +100,8 @@ export function getPromptFileDefaultLocation(type: PromptsType): string {
|
||||
return INSTRUCTIONS_DEFAULT_SOURCE_FOLDER;
|
||||
case PromptsType.prompt:
|
||||
return PROMPT_DEFAULT_SOURCE_FOLDER;
|
||||
case PromptsType.mode:
|
||||
return MODE_DEFAULT_SOURCE_FOLDER;
|
||||
case PromptsType.agent:
|
||||
return AGENTS_SOURCE_FOLDER;
|
||||
default:
|
||||
throw new Error('Unknown prompt type');
|
||||
}
|
||||
@@ -107,7 +117,8 @@ export function getCleanPromptName(fileUri: URI): string {
|
||||
const extensions = [
|
||||
PROMPT_FILE_EXTENSION,
|
||||
INSTRUCTION_FILE_EXTENSION,
|
||||
MODE_FILE_EXTENSION,
|
||||
LEGACY_MODE_FILE_EXTENSION,
|
||||
AGENT_FILE_EXTENSION,
|
||||
];
|
||||
|
||||
for (const ext of extensions) {
|
||||
|
||||
@@ -37,12 +37,12 @@ export class PromptHeaderDefinitionProvider implements DefinitionProvider {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const modeAttr = header.getAttribute('mode');
|
||||
if (modeAttr && modeAttr.value.type === 'string' && modeAttr.range.containsPosition(position)) {
|
||||
const mode = this.chatModeService.findModeByName(modeAttr.value.value);
|
||||
if (mode && mode.uri) {
|
||||
const agentAttr = header.getAttribute('agent') ?? header.getAttribute('mode');
|
||||
if (agentAttr && agentAttr.value.type === 'string' && agentAttr.range.containsPosition(position)) {
|
||||
const agent = this.chatModeService.findModeByName(agentAttr.value.value);
|
||||
if (agent && agent.uri) {
|
||||
return {
|
||||
uri: mode.uri.get(),
|
||||
uri: agent.uri.get(),
|
||||
range: new Range(1, 1, 1, 1)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class PromptBodyAutocompletion implements CompletionItemProvider {
|
||||
}
|
||||
} else if (reference.type === '') {
|
||||
const promptFileType = getPromptFileType(model.uri);
|
||||
if (promptFileType === PromptsType.mode || promptFileType === PromptsType.prompt) {
|
||||
if (promptFileType === PromptsType.agent || promptFileType === PromptsType.prompt) {
|
||||
await this.collectToolCompletions(model, position, reference.contentRange, suggestions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (promptType === PromptsType.prompt || promptType === PromptsType.mode) {
|
||||
if (promptType === PromptsType.prompt || promptType === PromptsType.agent) {
|
||||
// if the position is inside the tools metadata, we provide tool name completions
|
||||
const result = this.provideToolCompletions(model, position, header);
|
||||
if (result) {
|
||||
@@ -158,7 +158,7 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
|
||||
};
|
||||
suggestions.push(item);
|
||||
}
|
||||
if (property === 'handoffs' && (promptType === PromptsType.mode)) {
|
||||
if (property === 'handoffs' && (promptType === PromptsType.agent)) {
|
||||
const value = [
|
||||
'',
|
||||
' - label: Start Implementation',
|
||||
@@ -194,20 +194,20 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider {
|
||||
if (promptType === PromptsType.instructions && property === 'applyTo') {
|
||||
return [`'**'`, `'**/*.ts, **/*.js'`, `'**/*.php'`, `'**/*.py'`];
|
||||
}
|
||||
if (promptType === PromptsType.prompt && property === 'mode') {
|
||||
// Get all available modes (builtin + custom)
|
||||
const modes = this.chatModeService.getModes();
|
||||
if (promptType === PromptsType.prompt && (property === 'agent' || property === 'mode')) {
|
||||
// Get all available agents (builtin + custom)
|
||||
const agents = this.chatModeService.getModes();
|
||||
const suggestions: string[] = [];
|
||||
for (const mode of Iterable.concat(modes.builtin, modes.custom)) {
|
||||
suggestions.push(mode.name);
|
||||
for (const agent of Iterable.concat(agents.builtin, agents.custom)) {
|
||||
suggestions.push(agent.name);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
if (property === 'tools' && (promptType === PromptsType.prompt || promptType === PromptsType.mode)) {
|
||||
if (property === 'tools' && (promptType === PromptsType.prompt || promptType === PromptsType.agent)) {
|
||||
return ['[]', `['search', 'edit', 'fetch']`];
|
||||
}
|
||||
if (property === 'model' && (promptType === PromptsType.prompt || promptType === PromptsType.mode)) {
|
||||
return this.getModelNames(promptType === PromptsType.mode);
|
||||
if (property === 'model' && (promptType === PromptsType.prompt || promptType === PromptsType.agent)) {
|
||||
return this.getModelNames(promptType === PromptsType.agent);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
@@ -77,23 +77,23 @@ export class PromptHoverProvider implements HoverProvider {
|
||||
return this.createHover(localize('promptHeader.instructions.applyToRange', 'One or more glob pattern (separated by comma) that describe for which files the instructions apply to. Based on these patterns, the file is automatically included in the prompt, when the context contains a file that matches one or more of these patterns. Use `**` when you want this file to always be added.\nExample: `**/*.ts`, `**/*.js`, `client/**`'), applyToRange);
|
||||
}
|
||||
|
||||
} else if (promptType === PromptsType.mode) {
|
||||
} else if (promptType === PromptsType.agent) {
|
||||
const descriptionRange = header.getAttribute('description')?.range;
|
||||
if (descriptionRange?.containsPosition(position)) {
|
||||
return this.createHover(localize('promptHeader.mode.description', 'The description of the mode file. It can be used to provide additional context or information about the mode to the mode author.'), descriptionRange);
|
||||
return this.createHover(localize('promptHeader.agent.description', 'The description of the agent file. It is a short description of what the agent does.'), descriptionRange);
|
||||
}
|
||||
const model = header.getAttribute('model');
|
||||
if (model?.range.containsPosition(position)) {
|
||||
return this.getModelHover(model, model.range, localize('promptHeader.mode.model', 'The model to use in this mode.'));
|
||||
return this.getModelHover(model, model.range, localize('promptHeader.agent.model', 'The model to use in this agent.'));
|
||||
}
|
||||
const tools = header.getAttribute('tools');
|
||||
if (tools?.range.containsPosition(position)) {
|
||||
return this.getToolHover(tools, position, localize('promptHeader.mode.tools', 'The tools to use in this mode.'));
|
||||
return this.getToolHover(tools, position, localize('promptHeader.agent.tools', 'The tools to use in this agent.'));
|
||||
}
|
||||
} else {
|
||||
const descriptionRange = header.getAttribute('description')?.range;
|
||||
if (descriptionRange?.containsPosition(position)) {
|
||||
return this.createHover(localize('promptHeader.prompt.description', 'The description of the prompt file. It can be used to provide additional context or information about the prompt to the prompt author.'), descriptionRange);
|
||||
return this.createHover(localize('promptHeader.prompt.description', 'The description of the prompt file. It is a short description of what the prompt does.'), descriptionRange);
|
||||
}
|
||||
const model = header.getAttribute('model');
|
||||
if (model?.range.containsPosition(position)) {
|
||||
@@ -103,9 +103,9 @@ export class PromptHoverProvider implements HoverProvider {
|
||||
if (tools?.range.containsPosition(position)) {
|
||||
return this.getToolHover(tools, position, localize('promptHeader.prompt.tools', 'The tools to use in this prompt.'));
|
||||
}
|
||||
const mode = header.getAttribute('mode');
|
||||
if (mode?.range.containsPosition(position)) {
|
||||
return this.getModeHover(mode, position, localize('promptHeader.prompt.mode', 'The mode to use in this prompt.'));
|
||||
const agent = header.getAttribute('agent') ?? header.getAttribute('mode');
|
||||
if (agent?.range.containsPosition(position)) {
|
||||
return this.getAgentHover(agent, position, localize('promptHeader.prompt.agent', 'The agent to use in this prompt.'));
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -166,37 +166,37 @@ export class PromptHoverProvider implements HoverProvider {
|
||||
return this.createHover(baseMessage, range);
|
||||
}
|
||||
|
||||
private getModeHover(mode: IHeaderAttribute, position: Position, baseMessage: string): Hover | undefined {
|
||||
private getAgentHover(agentAttribute: IHeaderAttribute, position: Position, baseMessage: string): Hover | undefined {
|
||||
const lines: string[] = [];
|
||||
const value = mode.value;
|
||||
const value = agentAttribute.value;
|
||||
if (value.type === 'string' && value.range.containsPosition(position)) {
|
||||
const mode = this.chatModeService.findModeByName(value.value);
|
||||
if (mode) {
|
||||
const description = mode.description.get() || (isBuiltinChatMode(mode) ? localize('promptHeader.prompt.mode.builtInDesc', 'Built-in chat mode') : localize('promptHeader.prompt.mode.customDesc', 'Custom chat mode'));
|
||||
lines.push(`\`${mode.name}\`: ${description}`);
|
||||
const agent = this.chatModeService.findModeByName(value.value);
|
||||
if (agent) {
|
||||
const description = agent.description.get() || (isBuiltinChatMode(agent) ? localize('promptHeader.prompt.agent.builtInDesc', 'Built-in agent') : localize('promptHeader.prompt.agent.customDesc', 'Custom agent'));
|
||||
lines.push(`\`${agent.name}\`: ${description}`);
|
||||
}
|
||||
} else {
|
||||
const modes = this.chatModeService.getModes();
|
||||
lines.push(localize('promptHeader.prompt.mode.description', 'The chat mode to use when running this prompt.'));
|
||||
const agents = this.chatModeService.getModes();
|
||||
lines.push(localize('promptHeader.prompt.agent.description', 'The agent to use when running this prompt.'));
|
||||
lines.push('');
|
||||
|
||||
// Built-in modes
|
||||
lines.push(localize('promptHeader.prompt.mode.builtin', '**Built-in modes:**'));
|
||||
for (const mode of modes.builtin) {
|
||||
lines.push(`- \`${mode.name}\`: ${mode.description.get() || mode.label}`);
|
||||
// Built-in agents
|
||||
lines.push(localize('promptHeader.prompt.agent.builtin', '**Built-in agents:**'));
|
||||
for (const agent of agents.builtin) {
|
||||
lines.push(`- \`${agent.name}\`: ${agent.description.get() || agent.label}`);
|
||||
}
|
||||
|
||||
// Custom modes
|
||||
if (modes.custom.length > 0) {
|
||||
// Custom agents
|
||||
if (agents.custom.length > 0) {
|
||||
lines.push('');
|
||||
lines.push(localize('promptHeader.prompt.mode.custom', '**Custom modes:**'));
|
||||
for (const mode of modes.custom) {
|
||||
const description = mode.description.get();
|
||||
lines.push(`- \`${mode.name}\`: ${description || localize('promptHeader.prompt.mode.customDesc', 'Custom chat mode')}`);
|
||||
lines.push(localize('promptHeader.prompt.agent.custom', '**Custom agents:**'));
|
||||
for (const agent of agents.custom) {
|
||||
const description = agent.description.get();
|
||||
lines.push(`- \`${agent.name}\`: ${description || localize('promptHeader.prompt.agent.customDesc', 'Custom agent')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.createHover(lines.join('\n'), mode.range);
|
||||
return this.createHover(lines.join('\n'), agentAttribute.range);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,8 +115,8 @@ export class PromptValidator {
|
||||
case PromptsType.prompt:
|
||||
report(toMarker(localize('promptValidator.unknownAttribute.prompt', "Attribute '{0}' is not supported in prompt files. Supported: {1}.", attribute.key, supportedNames), attribute.range, MarkerSeverity.Warning));
|
||||
break;
|
||||
case PromptsType.mode:
|
||||
report(toMarker(localize('promptValidator.unknownAttribute.mode', "Attribute '{0}' is not supported in mode files. Supported: {1}.", attribute.key, supportedNames), attribute.range, MarkerSeverity.Warning));
|
||||
case PromptsType.agent:
|
||||
report(toMarker(localize('promptValidator.unknownAttribute.agent', "Attribute '{0}' is not supported in agent files. Supported: {1}.", attribute.key, supportedNames), attribute.range, MarkerSeverity.Warning));
|
||||
break;
|
||||
case PromptsType.instructions:
|
||||
report(toMarker(localize('promptValidator.unknownAttribute.instructions', "Attribute '{0}' is not supported in instructions files. Supported: {1}.", attribute.key, supportedNames), attribute.range, MarkerSeverity.Warning));
|
||||
@@ -127,9 +127,9 @@ export class PromptValidator {
|
||||
this.validateDescription(attributes, report);
|
||||
switch (promptType) {
|
||||
case PromptsType.prompt: {
|
||||
const mode = this.validateMode(attributes, report);
|
||||
this.validateTools(attributes, mode?.kind ?? ChatModeKind.Agent, report);
|
||||
this.validateModel(attributes, mode?.kind ?? ChatModeKind.Agent, report);
|
||||
const agent = this.validateAgent(attributes, report);
|
||||
this.validateTools(attributes, agent?.kind ?? ChatModeKind.Agent, report);
|
||||
this.validateModel(attributes, agent?.kind ?? ChatModeKind.Agent, report);
|
||||
break;
|
||||
}
|
||||
case PromptsType.instructions:
|
||||
@@ -137,7 +137,7 @@ export class PromptValidator {
|
||||
this.validateExcludeAgent(attributes, report);
|
||||
break;
|
||||
|
||||
case PromptsType.mode:
|
||||
case PromptsType.agent:
|
||||
this.validateTools(attributes, ChatModeKind.Agent, report);
|
||||
this.validateModel(attributes, ChatModeKind.Agent, report);
|
||||
this.validateHandoffs(attributes, report);
|
||||
@@ -162,7 +162,7 @@ export class PromptValidator {
|
||||
}
|
||||
|
||||
|
||||
private validateModel(attributes: IHeaderAttribute[], modeKind: ChatModeKind, report: (markers: IMarkerData) => void): void {
|
||||
private validateModel(attributes: IHeaderAttribute[], agentKind: ChatModeKind, report: (markers: IMarkerData) => void): void {
|
||||
const attribute = attributes.find(attr => attr.key === 'model');
|
||||
if (!attribute) {
|
||||
return;
|
||||
@@ -186,7 +186,7 @@ export class PromptValidator {
|
||||
if (!modelMetadata) {
|
||||
report(toMarker(localize('promptValidator.modelNotFound', "Unknown model '{0}'.", modelName), attribute.value.range, MarkerSeverity.Warning));
|
||||
|
||||
} else if (modeKind === ChatModeKind.Agent && !ILanguageModelChatMetadata.suitableForAgentMode(modelMetadata)) {
|
||||
} else if (agentKind === ChatModeKind.Agent && !ILanguageModelChatMetadata.suitableForAgentMode(modelMetadata)) {
|
||||
report(toMarker(localize('promptValidator.modelNotSuited', "Model '{0}' is not suited for agent mode.", modelName), attribute.value.range, MarkerSeverity.Warning));
|
||||
}
|
||||
}
|
||||
@@ -201,43 +201,53 @@ export class PromptValidator {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private validateMode(attributes: IHeaderAttribute[], report: (markers: IMarkerData) => void): IChatMode | undefined {
|
||||
const attribute = attributes.find(attr => attr.key === 'mode');
|
||||
private validateAgent(attributes: IHeaderAttribute[], report: (markers: IMarkerData) => void): IChatMode | undefined {
|
||||
const agentAttribute = attributes.find(attr => attr.key === 'agent');
|
||||
const modeAttribute = attributes.find(attr => attr.key === 'mode');
|
||||
if (modeAttribute) {
|
||||
if (agentAttribute) {
|
||||
report(toMarker(localize('promptValidator.modeDeprecated', "The 'mode' attribute has been deprecated. The 'agent' attribute is used instead."), modeAttribute.range, MarkerSeverity.Warning));
|
||||
} else {
|
||||
report(toMarker(localize('promptValidator.modeDeprecated.useAgent', "The 'mode' attribute has been deprecated. Please rename it to 'agent'."), modeAttribute.range, MarkerSeverity.Error));
|
||||
}
|
||||
}
|
||||
|
||||
const attribute = attributes.find(attr => attr.key === 'agent') ?? modeAttribute;
|
||||
if (!attribute) {
|
||||
return undefined; // default mode for prompts is Agent
|
||||
return undefined; // default agent for prompts is Agent
|
||||
}
|
||||
if (attribute.value.type !== 'string') {
|
||||
report(toMarker(localize('promptValidator.modeMustBeString', "The 'mode' attribute must be a string."), attribute.value.range, MarkerSeverity.Error));
|
||||
report(toMarker(localize('promptValidator.attributeMustBeString', "The '{0}' attribute must be a string.", attribute.key), attribute.value.range, MarkerSeverity.Error));
|
||||
return undefined;
|
||||
}
|
||||
const modeValue = attribute.value.value;
|
||||
if (modeValue.trim().length === 0) {
|
||||
report(toMarker(localize('promptValidator.modeMustBeNonEmpty', "The 'mode' attribute must be a non-empty string."), attribute.value.range, MarkerSeverity.Error));
|
||||
const agentValue = attribute.value.value;
|
||||
if (agentValue.trim().length === 0) {
|
||||
report(toMarker(localize('promptValidator.attributeMustBeNonEmpty', "The '{0}' attribute must be a non-empty string.", attribute.key), attribute.value.range, MarkerSeverity.Error));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const modes = this.chatModeService.getModes();
|
||||
const availableModes = [];
|
||||
const agents = this.chatModeService.getModes();
|
||||
const availableAgents = [];
|
||||
|
||||
// Check if mode exists in builtin or custom modes
|
||||
for (const mode of Iterable.concat(modes.builtin, modes.custom)) {
|
||||
if (mode.name === modeValue) {
|
||||
return mode;
|
||||
// Check if agent exists in builtin or custom agents
|
||||
for (const agent of Iterable.concat(agents.builtin, agents.custom)) {
|
||||
if (agent.name === agentValue) {
|
||||
return agent;
|
||||
}
|
||||
availableModes.push(mode.name); // collect all available mode names
|
||||
availableAgents.push(agent.name); // collect all available agent names
|
||||
}
|
||||
|
||||
const errorMessage = localize('promptValidator.modeNotFound', "Unknown mode '{0}'. Available modes: {1}.", modeValue, availableModes.join(', '));
|
||||
const errorMessage = localize('promptValidator.agentNotFound', "Unknown agent '{0}'. Available agents: {1}.", agentValue, availableAgents.join(', '));
|
||||
report(toMarker(errorMessage, attribute.value.range, MarkerSeverity.Warning));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private validateTools(attributes: IHeaderAttribute[], modeKind: ChatModeKind, report: (markers: IMarkerData) => void): undefined {
|
||||
private validateTools(attributes: IHeaderAttribute[], agentKind: ChatModeKind, report: (markers: IMarkerData) => void): undefined {
|
||||
const attribute = attributes.find(attr => attr.key === 'tools');
|
||||
if (!attribute) {
|
||||
return;
|
||||
}
|
||||
if (modeKind !== ChatModeKind.Agent) {
|
||||
if (agentKind !== ChatModeKind.Agent) {
|
||||
report(toMarker(localize('promptValidator.toolsOnlyInAgent', "The 'tools' attribute is only supported in agent mode. Attribute will be ignored."), attribute.range, MarkerSeverity.Warning));
|
||||
}
|
||||
|
||||
@@ -357,23 +367,23 @@ export class PromptValidator {
|
||||
}
|
||||
}
|
||||
|
||||
const validAttributeNames = {
|
||||
[PromptsType.prompt]: ['description', 'model', 'tools', 'mode'],
|
||||
const allAttributeNames = {
|
||||
[PromptsType.prompt]: ['description', 'model', 'tools', 'mode', 'agent'],
|
||||
[PromptsType.instructions]: ['description', 'applyTo', 'excludeAgent'],
|
||||
[PromptsType.mode]: ['description', 'model', 'tools', 'advancedOptions', 'handoffs']
|
||||
[PromptsType.agent]: ['description', 'model', 'tools', 'advancedOptions', 'handoffs']
|
||||
};
|
||||
const validAttributeNamesNoExperimental = {
|
||||
[PromptsType.prompt]: validAttributeNames[PromptsType.prompt].filter(name => !isExperimentalAttribute(name)),
|
||||
[PromptsType.instructions]: validAttributeNames[PromptsType.instructions].filter(name => !isExperimentalAttribute(name)),
|
||||
[PromptsType.mode]: validAttributeNames[PromptsType.mode].filter(name => !isExperimentalAttribute(name))
|
||||
const recommendedAttributeNames = {
|
||||
[PromptsType.prompt]: allAttributeNames[PromptsType.prompt].filter(name => !isNonRecommendedAttribute(name)),
|
||||
[PromptsType.instructions]: allAttributeNames[PromptsType.instructions].filter(name => !isNonRecommendedAttribute(name)),
|
||||
[PromptsType.agent]: allAttributeNames[PromptsType.agent].filter(name => !isNonRecommendedAttribute(name))
|
||||
};
|
||||
|
||||
export function getValidAttributeNames(promptType: PromptsType, includeExperimental: boolean): string[] {
|
||||
return includeExperimental ? validAttributeNames[promptType] : validAttributeNamesNoExperimental[promptType];
|
||||
export function getValidAttributeNames(promptType: PromptsType, includeNonRecommended: boolean): string[] {
|
||||
return includeNonRecommended ? allAttributeNames[promptType] : recommendedAttributeNames[promptType];
|
||||
}
|
||||
|
||||
export function isExperimentalAttribute(attributeName: string): boolean {
|
||||
return attributeName === 'advancedOptions' || attributeName === 'excludeAgent';
|
||||
export function isNonRecommendedAttribute(attributeName: string): boolean {
|
||||
return attributeName === 'advancedOptions' || attributeName === 'excludeAgent' || attributeName === 'mode';
|
||||
}
|
||||
|
||||
function toMarker(message: string, range: Range, severity = MarkerSeverity.Error): IMarkerData {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { LanguageSelector } from '../../../../../editor/common/languageSelector.
|
||||
*/
|
||||
export const PROMPT_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets';
|
||||
export const INSTRUCTIONS_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-custom-instructions';
|
||||
export const MODE_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-custom-chat-modes'; // todo
|
||||
export const AGENT_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-custom-chat-modes'; // todo
|
||||
|
||||
/**
|
||||
* Language ID for the reusable prompt syntax.
|
||||
@@ -23,14 +23,14 @@ export const PROMPT_LANGUAGE_ID = 'prompt';
|
||||
export const INSTRUCTIONS_LANGUAGE_ID = 'instructions';
|
||||
|
||||
/**
|
||||
* Language ID for modes syntax.
|
||||
* Language ID for agent syntax.
|
||||
*/
|
||||
export const MODE_LANGUAGE_ID = 'chatmode';
|
||||
export const AGENT_LANGUAGE_ID = 'agent';
|
||||
|
||||
/**
|
||||
* Prompt and instructions files language selector.
|
||||
*/
|
||||
export const ALL_PROMPTS_LANGUAGE_SELECTOR: LanguageSelector = [PROMPT_LANGUAGE_ID, INSTRUCTIONS_LANGUAGE_ID, MODE_LANGUAGE_ID];
|
||||
export const ALL_PROMPTS_LANGUAGE_SELECTOR: LanguageSelector = [PROMPT_LANGUAGE_ID, INSTRUCTIONS_LANGUAGE_ID, AGENT_LANGUAGE_ID];
|
||||
|
||||
/**
|
||||
* The language id for for a prompts type.
|
||||
@@ -41,8 +41,8 @@ export function getLanguageIdForPromptsType(type: PromptsType): string {
|
||||
return PROMPT_LANGUAGE_ID;
|
||||
case PromptsType.instructions:
|
||||
return INSTRUCTIONS_LANGUAGE_ID;
|
||||
case PromptsType.mode:
|
||||
return MODE_LANGUAGE_ID;
|
||||
case PromptsType.agent:
|
||||
return AGENT_LANGUAGE_ID;
|
||||
default:
|
||||
throw new Error(`Unknown prompt type: ${type}`);
|
||||
}
|
||||
@@ -54,8 +54,8 @@ export function getPromptsTypeForLanguageId(languageId: string): PromptsType | u
|
||||
return PromptsType.prompt;
|
||||
case INSTRUCTIONS_LANGUAGE_ID:
|
||||
return PromptsType.instructions;
|
||||
case MODE_LANGUAGE_ID:
|
||||
return PromptsType.mode;
|
||||
case AGENT_LANGUAGE_ID:
|
||||
return PromptsType.agent;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export function getPromptsTypeForLanguageId(languageId: string): PromptsType | u
|
||||
export enum PromptsType {
|
||||
instructions = 'instructions',
|
||||
prompt = 'prompt',
|
||||
mode = 'mode'
|
||||
agent = 'agent'
|
||||
}
|
||||
export function isValidPromptType(type: string): type is PromptsType {
|
||||
return Object.values(PromptsType).includes(type as PromptsType);
|
||||
|
||||
@@ -140,8 +140,8 @@ export class PromptHeader {
|
||||
return this.getStringAttribute('description');
|
||||
}
|
||||
|
||||
public get mode(): string | undefined {
|
||||
return this.getStringAttribute('mode');
|
||||
public get agent(): string | undefined {
|
||||
return this.getStringAttribute('agent') ?? this.getStringAttribute('mode');
|
||||
}
|
||||
|
||||
public get model(): string | undefined {
|
||||
|
||||
@@ -10,8 +10,7 @@ import { URI } from '../../../../../../base/common/uri.js';
|
||||
import { ITextModel } from '../../../../../../editor/common/model.js';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from '../../../../../../platform/extensions/common/extensions.js';
|
||||
import { createDecorator } from '../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IChatModeInstructions } from '../../chatModes.js';
|
||||
import { ChatModeKind } from '../../constants.js';
|
||||
import { IChatModeInstructions, IVariableReference } from '../../chatModes.js';
|
||||
import { PromptsType } from '../promptTypes.js';
|
||||
import { IHandOff, ParsedPromptFile } from './newPromptsParser.js';
|
||||
|
||||
@@ -75,39 +74,26 @@ export interface IUserPromptPath extends IPromptPathBase {
|
||||
readonly storage: PromptsStorage.user;
|
||||
}
|
||||
|
||||
export type IChatModeSource = {
|
||||
export type IAgentSource = {
|
||||
readonly storage: PromptsStorage.extension;
|
||||
readonly extensionId: ExtensionIdentifier;
|
||||
} | {
|
||||
readonly storage: PromptsStorage.local | PromptsStorage.user;
|
||||
};
|
||||
|
||||
export function promptPathToChatModeSource(promptPath: IPromptPath): IChatModeSource {
|
||||
if (promptPath.storage === PromptsStorage.extension) {
|
||||
return {
|
||||
storage: PromptsStorage.extension,
|
||||
extensionId: promptPath.extension.identifier
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
storage: promptPath.storage
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICustomChatMode {
|
||||
export interface ICustomAgent {
|
||||
/**
|
||||
* URI of a custom chat mode file.
|
||||
* URI of a custom agent file.
|
||||
*/
|
||||
readonly uri: URI;
|
||||
|
||||
/**
|
||||
* Name of the custom chat mode as used in prompt files or contexts
|
||||
* Name of the custom agent as used in prompt files or contexts
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Description of the mode
|
||||
* Description of the agent
|
||||
*/
|
||||
readonly description?: string;
|
||||
|
||||
@@ -122,62 +108,27 @@ export interface ICustomChatMode {
|
||||
readonly model?: string;
|
||||
|
||||
/**
|
||||
* Contents of the custom chat mode file body and other mode instructions.
|
||||
* Contents of the custom agent file body and other agent instructions.
|
||||
*/
|
||||
readonly modeInstructions: IChatModeInstructions;
|
||||
readonly agentInstructions: IChatModeInstructions;
|
||||
|
||||
/**
|
||||
* Hand-offs defined in the custom chat mode file.
|
||||
* Hand-offs defined in the custom agent file.
|
||||
*/
|
||||
readonly handOffs?: readonly IHandOff[];
|
||||
|
||||
/**
|
||||
* Where the mode was loaded from.
|
||||
* Where the agent was loaded from.
|
||||
*/
|
||||
readonly source: IChatModeSource;
|
||||
readonly source: IAgentSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of combined tools metadata for the case
|
||||
* when the prompt is in the agent mode.
|
||||
*/
|
||||
interface ICombinedAgentToolsMetadata {
|
||||
/**
|
||||
* List of combined tools metadata for
|
||||
* the entire tree of prompt references.
|
||||
*/
|
||||
readonly tools: readonly string[] | undefined;
|
||||
|
||||
/**
|
||||
* Resulting chat mode of a prompt, based on modes
|
||||
* used in the entire tree of prompt references.
|
||||
*/
|
||||
readonly mode: ChatModeKind.Agent;
|
||||
export interface IAgentInstructions {
|
||||
readonly content: string;
|
||||
readonly toolReferences: readonly IVariableReference[];
|
||||
readonly metadata?: Record<string, boolean | string | number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of combined tools metadata for the case
|
||||
* when the prompt is in non-agent mode.
|
||||
*/
|
||||
interface ICombinedNonAgentToolsMetadata {
|
||||
/**
|
||||
* List of combined tools metadata is empty
|
||||
* when the prompt is in non-agent mode.
|
||||
*/
|
||||
readonly tools: undefined;
|
||||
|
||||
/**
|
||||
* Resulting chat mode of a prompt, based on modes
|
||||
* used in the entire tree of prompt references.
|
||||
*/
|
||||
readonly mode?: ChatModeKind.Ask | ChatModeKind.Edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* General type of the combined tools metadata.
|
||||
*/
|
||||
export type TCombinedToolsMetadata = ICombinedAgentToolsMetadata | ICombinedNonAgentToolsMetadata;
|
||||
|
||||
/**
|
||||
* Provides prompt services.
|
||||
*/
|
||||
@@ -227,14 +178,14 @@ export interface IPromptsService extends IDisposable {
|
||||
getPromptCommandName(uri: URI): Promise<string>;
|
||||
|
||||
/**
|
||||
* Event that is triggered when the list of custom chat modes changes.
|
||||
* Event that is triggered when the list of custom agents changes.
|
||||
*/
|
||||
readonly onDidChangeCustomChatModes: Event<void>;
|
||||
readonly onDidChangeCustomAgents: Event<void>;
|
||||
|
||||
/**
|
||||
* Finds all available custom chat modes
|
||||
* Finds all available custom agents
|
||||
*/
|
||||
getCustomChatModes(token: CancellationToken): Promise<readonly ICustomChatMode[]>;
|
||||
getCustomAgents(token: CancellationToken): Promise<readonly ICustomAgent[]>;
|
||||
|
||||
/**
|
||||
* Parses the provided URI
|
||||
|
||||
@@ -25,13 +25,13 @@ import { ILabelService } from '../../../../../../platform/label/common/label.js'
|
||||
import { ILogService } from '../../../../../../platform/log/common/log.js';
|
||||
import { IFilesConfigurationService } from '../../../../../services/filesConfiguration/common/filesConfigurationService.js';
|
||||
import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js';
|
||||
import { IChatModeInstructions, IVariableReference } from '../../chatModes.js';
|
||||
import { IVariableReference } from '../../chatModes.js';
|
||||
import { PromptsConfig } from '../config/config.js';
|
||||
import { getCleanPromptName, PROMPT_FILE_EXTENSION } from '../config/promptFileLocations.js';
|
||||
import { getPromptsTypeForLanguageId, MODE_LANGUAGE_ID, PROMPT_LANGUAGE_ID, PromptsType } from '../promptTypes.js';
|
||||
import { getPromptsTypeForLanguageId, AGENT_LANGUAGE_ID, PROMPT_LANGUAGE_ID, PromptsType } from '../promptTypes.js';
|
||||
import { PromptFilesLocator } from '../utils/promptFilesLocator.js';
|
||||
import { NewPromptsParser, ParsedPromptFile } from './newPromptsParser.js';
|
||||
import { IChatModeSource, IChatPromptSlashCommand, ICustomChatMode, IExtensionPromptPath, ILocalPromptPath, IPromptPath, IPromptsService, IUserPromptPath, promptPathToChatModeSource, PromptsStorage } from './promptsService.js';
|
||||
import { IAgentInstructions, IAgentSource, IChatPromptSlashCommand, ICustomAgent, IExtensionPromptPath, ILocalPromptPath, IPromptPath, IPromptsService, IUserPromptPath, PromptsStorage } from './promptsService.js';
|
||||
|
||||
/**
|
||||
* Provides prompt services.
|
||||
@@ -45,9 +45,9 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
private readonly fileLocator: PromptFilesLocator;
|
||||
|
||||
/**
|
||||
* Cached custom modes. Caching only happens if the `onDidChangeCustomChatModes` event is used.
|
||||
* Cached custom agents. Caching only happens if the `onDidChangeCustomAgents` event is used.
|
||||
*/
|
||||
private cachedCustomChatModes: Promise<readonly ICustomChatMode[]> | undefined;
|
||||
private cachedCustomAgents: Promise<readonly ICustomAgent[]> | undefined;
|
||||
|
||||
|
||||
private parsedPromptFileCache = new ResourceMap<[number, ParsedPromptFile]>();
|
||||
@@ -58,13 +58,13 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
private readonly contributedFiles = {
|
||||
[PromptsType.prompt]: new ResourceMap<Promise<IExtensionPromptPath>>(),
|
||||
[PromptsType.instructions]: new ResourceMap<Promise<IExtensionPromptPath>>(),
|
||||
[PromptsType.mode]: new ResourceMap<Promise<IExtensionPromptPath>>(),
|
||||
[PromptsType.agent]: new ResourceMap<Promise<IExtensionPromptPath>>(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Lazily created event that is fired when the custom chat modes change.
|
||||
* Lazily created event that is fired when the custom agents change.
|
||||
*/
|
||||
private onDidChangeCustomChatModesEmitter: Emitter<void> | undefined;
|
||||
private onDidChangeCustomAgentsEmitter: Emitter<void> | undefined;
|
||||
|
||||
constructor(
|
||||
@ILogService public readonly logger: ILogService,
|
||||
@@ -87,18 +87,18 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitter for the custom chat modes change event.
|
||||
* Emitter for the custom agents change event.
|
||||
*/
|
||||
public get onDidChangeCustomChatModes(): Event<void> {
|
||||
if (!this.onDidChangeCustomChatModesEmitter) {
|
||||
const emitter = this.onDidChangeCustomChatModesEmitter = this._register(new Emitter<void>());
|
||||
const chatModelTracker = this._register(new ChatModeUpdateTracker(this.fileLocator, this.modelService));
|
||||
this._register(chatModelTracker.onDidChangeContent(() => {
|
||||
this.cachedCustomChatModes = undefined; // reset cached custom chat modes
|
||||
public get onDidChangeCustomAgents(): Event<void> {
|
||||
if (!this.onDidChangeCustomAgentsEmitter) {
|
||||
const emitter = this.onDidChangeCustomAgentsEmitter = this._register(new Emitter<void>());
|
||||
const updateTracker = this._register(new UpdateTracker(this.fileLocator, PromptsType.agent, this.modelService));
|
||||
this._register(updateTracker.onDidChangeContent(() => {
|
||||
this.cachedCustomAgents = undefined; // reset cached custom agents
|
||||
emitter.fire();
|
||||
}));
|
||||
}
|
||||
return this.onDidChangeCustomChatModesEmitter.event;
|
||||
return this.onDidChangeCustomAgentsEmitter.event;
|
||||
}
|
||||
|
||||
public getPromptFileType(uri: URI): PromptsType | undefined {
|
||||
@@ -162,9 +162,17 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
|
||||
const result: IPromptPath[] = [];
|
||||
|
||||
for (const uri of this.fileLocator.getConfigBasedSourceFolders(type)) {
|
||||
result.push({ uri, storage: PromptsStorage.local, type });
|
||||
if (type === PromptsType.agent) {
|
||||
const folders = this.fileLocator.getAgentSourceFolder();
|
||||
for (const uri of folders) {
|
||||
result.push({ uri, storage: PromptsStorage.local, type });
|
||||
}
|
||||
} else {
|
||||
for (const uri of this.fileLocator.getConfigBasedSourceFolders(type)) {
|
||||
result.push({ uri, storage: PromptsStorage.local, type });
|
||||
}
|
||||
}
|
||||
|
||||
const userHome = this.userDataService.currentProfile.promptsHome;
|
||||
result.push({ uri: userHome, storage: PromptsStorage.user, type });
|
||||
|
||||
@@ -231,23 +239,23 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
});
|
||||
}
|
||||
|
||||
public async getCustomChatModes(token: CancellationToken): Promise<readonly ICustomChatMode[]> {
|
||||
if (!this.cachedCustomChatModes) {
|
||||
const customChatModes = this.computeCustomChatModes(token);
|
||||
if (!this.onDidChangeCustomChatModesEmitter) {
|
||||
return customChatModes;
|
||||
public async getCustomAgents(token: CancellationToken): Promise<readonly ICustomAgent[]> {
|
||||
if (!this.cachedCustomAgents) {
|
||||
const customAgents = this.computeCustomAgents(token);
|
||||
if (!this.onDidChangeCustomAgentsEmitter) {
|
||||
return customAgents;
|
||||
}
|
||||
this.cachedCustomChatModes = customChatModes;
|
||||
this.cachedCustomAgents = customAgents;
|
||||
}
|
||||
return this.cachedCustomChatModes;
|
||||
return this.cachedCustomAgents;
|
||||
}
|
||||
|
||||
private async computeCustomChatModes(token: CancellationToken): Promise<readonly ICustomChatMode[]> {
|
||||
const modeFiles = await this.listPromptFiles(PromptsType.mode, token);
|
||||
private async computeCustomAgents(token: CancellationToken): Promise<readonly ICustomAgent[]> {
|
||||
const agentFiles = await this.listPromptFiles(PromptsType.agent, token);
|
||||
|
||||
const customChatModes = await Promise.all(
|
||||
modeFiles.map(async (promptPath): Promise<ICustomChatMode> => {
|
||||
const { uri, name: modeName } = promptPath;
|
||||
const customAgents = await Promise.all(
|
||||
agentFiles.map(async (promptPath): Promise<ICustomAgent> => {
|
||||
const { uri, name: agentName } = promptPath;
|
||||
const ast = await this.parseNew(uri, token);
|
||||
|
||||
let metadata: any | undefined;
|
||||
@@ -273,24 +281,24 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
}
|
||||
}
|
||||
|
||||
const modeInstructions = {
|
||||
const agentInstructions = {
|
||||
content: ast.body?.getContent() ?? '',
|
||||
toolReferences,
|
||||
metadata,
|
||||
} satisfies IChatModeInstructions;
|
||||
} satisfies IAgentInstructions;
|
||||
|
||||
const name = modeName ?? getCleanPromptName(uri);
|
||||
const name = agentName ?? getCleanPromptName(uri);
|
||||
|
||||
const source: IChatModeSource = promptPathToChatModeSource(promptPath);
|
||||
const source: IAgentSource = IAgentSource.fromPromptPath(promptPath);
|
||||
if (!ast.header) {
|
||||
return { uri, name, modeInstructions, source };
|
||||
return { uri, name, agentInstructions, source };
|
||||
}
|
||||
const { description, model, tools, handOffs } = ast.header;
|
||||
return { uri, name, description, model, tools, handOffs, modeInstructions, source };
|
||||
return { uri, name, description, model, tools, handOffs, agentInstructions, source };
|
||||
|
||||
})
|
||||
);
|
||||
return customChatModes;
|
||||
return customAgents;
|
||||
}
|
||||
|
||||
public async parseNew(uri: URI, token: CancellationToken): Promise<ParsedPromptFile> {
|
||||
@@ -322,17 +330,17 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
})();
|
||||
bucket.set(uri, entryPromise);
|
||||
|
||||
const updateModesIfRequired = () => {
|
||||
if (type === PromptsType.mode) {
|
||||
this.cachedCustomChatModes = undefined;
|
||||
this.onDidChangeCustomChatModesEmitter?.fire();
|
||||
const updateAgentsIfRequired = () => {
|
||||
if (type === PromptsType.agent) {
|
||||
this.cachedCustomAgents = undefined;
|
||||
this.onDidChangeCustomAgentsEmitter?.fire();
|
||||
}
|
||||
};
|
||||
updateModesIfRequired();
|
||||
updateAgentsIfRequired();
|
||||
return {
|
||||
dispose: () => {
|
||||
bucket.delete(uri);
|
||||
updateModesIfRequired();
|
||||
updateAgentsIfRequired();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -381,36 +389,37 @@ function getCommandNameFromURI(uri: URI): string {
|
||||
return basename(uri.fsPath, PROMPT_FILE_EXTENSION);
|
||||
}
|
||||
|
||||
export class ChatModeUpdateTracker extends Disposable {
|
||||
export class UpdateTracker extends Disposable {
|
||||
|
||||
private static readonly CHAT_MODE_UPDATE_DELAY_MS = 200;
|
||||
private static readonly CHAT_AGENT_UPDATE_DELAY_MS = 200;
|
||||
|
||||
private readonly listeners = new ResourceMap<IDisposable>();
|
||||
private readonly onDidChatModeModelChange: Emitter<void>;
|
||||
private readonly onDidChangeContentEmitter: Emitter<void>;
|
||||
|
||||
public get onDidChangeContent(): Event<void> {
|
||||
return this.onDidChatModeModelChange.event;
|
||||
return this.onDidChangeContentEmitter.event;
|
||||
}
|
||||
|
||||
constructor(
|
||||
fileLocator: PromptFilesLocator,
|
||||
promptTypes: PromptsType,
|
||||
@IModelService modelService: IModelService,
|
||||
) {
|
||||
super();
|
||||
this.onDidChatModeModelChange = this._register(new Emitter<void>());
|
||||
const delayer = this._register(new Delayer<void>(ChatModeUpdateTracker.CHAT_MODE_UPDATE_DELAY_MS));
|
||||
const trigger = () => delayer.trigger(() => this.onDidChatModeModelChange.fire());
|
||||
this.onDidChangeContentEmitter = this._register(new Emitter<void>());
|
||||
const delayer = this._register(new Delayer<void>(UpdateTracker.CHAT_AGENT_UPDATE_DELAY_MS));
|
||||
const trigger = () => delayer.trigger(() => this.onDidChangeContentEmitter.fire());
|
||||
|
||||
const filesUpdatedEventRegistration = this._register(fileLocator.createFilesUpdatedEvent(PromptsType.mode));
|
||||
const filesUpdatedEventRegistration = this._register(fileLocator.createFilesUpdatedEvent(promptTypes));
|
||||
this._register(filesUpdatedEventRegistration.event(() => trigger()));
|
||||
|
||||
const onAdd = (model: ITextModel) => {
|
||||
if (model.getLanguageId() === MODE_LANGUAGE_ID) {
|
||||
if (model.getLanguageId() === AGENT_LANGUAGE_ID) {
|
||||
this.listeners.set(model.uri, model.onDidChangeContent(() => trigger()));
|
||||
}
|
||||
};
|
||||
const onRemove = (languageId: string, uri: URI) => {
|
||||
if (languageId === MODE_LANGUAGE_ID) {
|
||||
if (languageId === AGENT_LANGUAGE_ID) {
|
||||
this.listeners.get(uri)?.dispose();
|
||||
this.listeners.delete(uri);
|
||||
trigger();
|
||||
@@ -431,3 +440,19 @@ export class ChatModeUpdateTracker extends Disposable {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace IAgentSource {
|
||||
export function fromPromptPath(promptPath: IPromptPath): IAgentSource {
|
||||
if (promptPath.storage === PromptsStorage.extension) {
|
||||
return {
|
||||
storage: PromptsStorage.extension,
|
||||
extensionId: promptPath.extension.identifier
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
storage: promptPath.storage
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getPromptFileLocationsConfigKey, PromptsConfig } from '../config/config
|
||||
import { basename, dirname, joinPath } from '../../../../../../base/common/resources.js';
|
||||
import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js';
|
||||
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js';
|
||||
import { COPILOT_CUSTOM_INSTRUCTIONS_FILENAME, getPromptFileExtension, getPromptFileType } from '../config/promptFileLocations.js';
|
||||
import { COPILOT_CUSTOM_INSTRUCTIONS_FILENAME, AGENTS_SOURCE_FOLDER, getPromptFileExtension, getPromptFileType } from '../config/promptFileLocations.js';
|
||||
import { PromptsType } from '../promptTypes.js';
|
||||
import { IWorkbenchEnvironmentService } from '../../../../../services/environment/common/environmentService.js';
|
||||
import { Schemas } from '../../../../../../base/common/network.js';
|
||||
@@ -103,6 +103,10 @@ export class PromptFilesLocator extends Disposable {
|
||||
return { event: eventEmitter.event, dispose: () => disposables.dispose() };
|
||||
}
|
||||
|
||||
public getAgentSourceFolder(): readonly URI[] {
|
||||
return this.toAbsoluteLocations([AGENTS_SOURCE_FOLDER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all possible unambiguous prompt file source folders based on
|
||||
* the current workspace folder structure.
|
||||
@@ -183,6 +187,9 @@ export class PromptFilesLocator extends Disposable {
|
||||
|
||||
private getLocalParentFolders(type: PromptsType): readonly { parent: URI; filePattern?: string }[] {
|
||||
const configuredLocations = PromptsConfig.promptSourceFolders(this.configService, type);
|
||||
if (type === PromptsType.agent) {
|
||||
configuredLocations.push(AGENTS_SOURCE_FOLDER);
|
||||
}
|
||||
const absoluteLocations = this.toAbsoluteLocations(configuredLocations);
|
||||
return absoluteLocations.map(firstNonGlobParentAndPattern);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ suite('PromptValidator', () => {
|
||||
const customChatMode = new CustomChatMode({
|
||||
uri: URI.parse('myFs://test/test/chatmode.md'),
|
||||
name: 'BeastMode',
|
||||
modeInstructions: { content: 'Beast mode instructions', toolReferences: [] },
|
||||
agentInstructions: { content: 'Beast mode instructions', toolReferences: [] },
|
||||
source: { storage: PromptsStorage.local }
|
||||
});
|
||||
instaService.stub(IChatModeService, new MockChatModeService({ builtin: [ChatMode.Agent, ChatMode.Ask, ChatMode.Edit], custom: [customChatMode] }));
|
||||
@@ -102,23 +102,23 @@ suite('PromptValidator', () => {
|
||||
await validator.validate(result, promptType, m => markers.push(m));
|
||||
return markers;
|
||||
}
|
||||
suite('modes', () => {
|
||||
suite('agents', () => {
|
||||
|
||||
test('correct mode', async () => {
|
||||
test('correct agent', async () => {
|
||||
const content = [
|
||||
/* 01 */'---',
|
||||
/* 02 */`description: "Agent mode test"`,
|
||||
/* 03 */'model: MAE 4.1',
|
||||
/* 04 */`tools: ['tool1', 'tool2']`,
|
||||
/* 05 */'---',
|
||||
/* 06 */'This is a chat mode test.',
|
||||
/* 06 */'This is a chat agent test.',
|
||||
/* 07 */'Here is a #tool1 variable and a #file:./reference1.md as well as a [reference](./reference2.md).',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.deepStrictEqual(markers, []);
|
||||
});
|
||||
|
||||
test('mode with errors (empty description, unknown tool & model)', async () => {
|
||||
test('agent with errors (empty description, unknown tool & model)', async () => {
|
||||
const content = [
|
||||
/* 01 */'---',
|
||||
/* 02 */`description: ""`, // empty description -> error
|
||||
@@ -127,7 +127,7 @@ suite('PromptValidator', () => {
|
||||
/* 05 */'---',
|
||||
/* 06 */'Body',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.deepStrictEqual(
|
||||
markers.map(m => ({ severity: m.severity, message: m.message })),
|
||||
[
|
||||
@@ -145,7 +145,7 @@ suite('PromptValidator', () => {
|
||||
`tools: 'tool1'`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`The 'tools' attribute must be an array.`]);
|
||||
});
|
||||
@@ -157,7 +157,7 @@ suite('PromptValidator', () => {
|
||||
`tools: ['tool1', 2]`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.deepStrictEqual(
|
||||
markers.map(m => ({ severity: m.severity, message: m.message })),
|
||||
[
|
||||
@@ -173,7 +173,7 @@ suite('PromptValidator', () => {
|
||||
`tools: ['tool1', 'tool3']`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.deepStrictEqual(
|
||||
markers.map(m => ({ severity: m.severity, message: m.message })),
|
||||
[
|
||||
@@ -182,17 +182,17 @@ suite('PromptValidator', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('unknown attribute in mode file', async () => {
|
||||
test('unknown attribute in agent file', async () => {
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Test"',
|
||||
`applyTo: '*.ts'`, // not allowed in mode file
|
||||
`applyTo: '*.ts'`, // not allowed in agent file
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.strictEqual(markers[0].severity, MarkerSeverity.Warning);
|
||||
assert.ok(markers[0].message.startsWith(`Attribute 'applyTo' is not supported in mode files.`));
|
||||
assert.ok(markers[0].message.startsWith(`Attribute 'applyTo' is not supported in agent files.`));
|
||||
});
|
||||
|
||||
test('tools with invalid handoffs', async () => {
|
||||
@@ -203,7 +203,7 @@ suite('PromptValidator', () => {
|
||||
`handoffs: next`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`The 'handoffs' attribute must be an array.`]);
|
||||
}
|
||||
@@ -215,7 +215,7 @@ suite('PromptValidator', () => {
|
||||
` - label: '123'`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`Missing required properties 'agent', 'prompt' in handoff object.`]);
|
||||
}
|
||||
@@ -230,16 +230,16 @@ suite('PromptValidator', () => {
|
||||
` send: true`,
|
||||
'---',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`The 'agent' property in a handoff must be a non-empty string.`]);
|
||||
}
|
||||
});
|
||||
|
||||
test('mode with handoffs attribute', async () => {
|
||||
test('agent with handoffs attribute', async () => {
|
||||
const content = [
|
||||
'---',
|
||||
'description: \"Test mode with handoffs\"',
|
||||
'description: \"Test agent with handoffs\"',
|
||||
`handoffs:`,
|
||||
' - label: Test Prompt',
|
||||
' agent: Default',
|
||||
@@ -250,7 +250,7 @@ suite('PromptValidator', () => {
|
||||
'---',
|
||||
'Body',
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.mode);
|
||||
const markers = await validate(content, PromptsType.agent);
|
||||
assert.deepStrictEqual(markers, [], 'Expected no validation issues for handoffs attribute');
|
||||
});
|
||||
});
|
||||
@@ -326,7 +326,7 @@ suite('PromptValidator', () => {
|
||||
});
|
||||
|
||||
test('prompt model not suited for agent mode', async () => {
|
||||
// MAE 3.5 Turbo lacks agentMode capability -> warning when used in agent (default) mode
|
||||
// MAE 3.5 Turbo lacks agentMode capability -> warning when used in agent (default)
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Prompt with unsuitable model"',
|
||||
@@ -340,6 +340,20 @@ suite('PromptValidator', () => {
|
||||
assert.strictEqual(markers[0].message, `Model 'MAE 3.5 Turbo' is not suited for agent mode.`);
|
||||
});
|
||||
|
||||
test('prompt with custom agent BeastMode and tools', async () => {
|
||||
// Explicit custom agent should be recognized; BeastMode kind comes from setup; ensure tools accepted
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Prompt custom mode"',
|
||||
'agent: BeastMode',
|
||||
`tools: ['tool1']`,
|
||||
'---',
|
||||
'Body'
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.prompt);
|
||||
assert.deepStrictEqual(markers, []);
|
||||
});
|
||||
|
||||
test('prompt with custom mode BeastMode and tools', async () => {
|
||||
// Explicit custom mode should be recognized; BeastMode kind comes from setup; ensure tools accepted
|
||||
const content = [
|
||||
@@ -351,14 +365,32 @@ suite('PromptValidator', () => {
|
||||
'Body'
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.prompt);
|
||||
assert.deepStrictEqual(markers, []);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`The 'mode' attribute has been deprecated. Please rename it to 'agent'.`]);
|
||||
|
||||
});
|
||||
|
||||
test('prompt with unknown mode Ask', async () => {
|
||||
test('prompt with custom mode an agent', async () => {
|
||||
// Explicit custom mode should be recognized; BeastMode kind comes from setup; ensure tools accepted
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Prompt unknown mode Ask"',
|
||||
'mode: Ask',
|
||||
'description: "Prompt custom mode"',
|
||||
'mode: BeastMode',
|
||||
`agent: agent`,
|
||||
'---',
|
||||
'Body'
|
||||
].join('\n');
|
||||
const markers = await validate(content, PromptsType.prompt);
|
||||
assert.strictEqual(markers.length, 1);
|
||||
assert.deepStrictEqual(markers.map(m => m.message), [`The 'mode' attribute has been deprecated. The 'agent' attribute is used instead.`]);
|
||||
|
||||
});
|
||||
|
||||
test('prompt with unknown agent Ask', async () => {
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Prompt unknown agent Ask"',
|
||||
'agent: Ask',
|
||||
`tools: ['tool1','tool2']`,
|
||||
'---',
|
||||
'Body'
|
||||
@@ -366,14 +398,14 @@ suite('PromptValidator', () => {
|
||||
const markers = await validate(content, PromptsType.prompt);
|
||||
assert.strictEqual(markers.length, 1, 'Expected one warning about tools in non-agent mode');
|
||||
assert.strictEqual(markers[0].severity, MarkerSeverity.Warning);
|
||||
assert.strictEqual(markers[0].message, `Unknown mode 'Ask'. Available modes: agent, ask, edit, BeastMode.`);
|
||||
assert.strictEqual(markers[0].message, `Unknown agent 'Ask'. Available agents: agent, ask, edit, BeastMode.`);
|
||||
});
|
||||
|
||||
test('prompt with mode edit', async () => {
|
||||
test('prompt with agent edit', async () => {
|
||||
const content = [
|
||||
'---',
|
||||
'description: "Prompt edit mode with tool"',
|
||||
'mode: edit',
|
||||
'agent: edit',
|
||||
`tools: ['tool1']`,
|
||||
'---',
|
||||
'Body'
|
||||
|
||||
@@ -17,7 +17,7 @@ import { TestStorageService } from '../../../../test/common/workbenchTestService
|
||||
import { IChatAgentService } from '../../common/chatAgents.js';
|
||||
import { ChatMode, ChatModeService } from '../../common/chatModes.js';
|
||||
import { ChatModeKind } from '../../common/constants.js';
|
||||
import { IChatModeSource, ICustomChatMode, IPromptsService, PromptsStorage } from '../../common/promptSyntax/service/promptsService.js';
|
||||
import { IAgentSource, ICustomAgent, IPromptsService, PromptsStorage } from '../../common/promptSyntax/service/promptsService.js';
|
||||
import { MockPromptsService } from './mockPromptsService.js';
|
||||
|
||||
class TestChatAgentService implements Partial<IChatAgentService> {
|
||||
@@ -41,7 +41,7 @@ class TestChatAgentService implements Partial<IChatAgentService> {
|
||||
suite('ChatModeService', () => {
|
||||
const testDisposables = ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
const workspaceSource: IChatModeSource = { storage: PromptsStorage.local };
|
||||
const workspaceSource: IAgentSource = { storage: PromptsStorage.local };
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let promptsService: MockPromptsService;
|
||||
@@ -81,17 +81,17 @@ suite('ChatModeService', () => {
|
||||
test('should adjust builtin modes based on tools agent availability', () => {
|
||||
// With tools agent
|
||||
chatAgentService.setHasToolsAgent(true);
|
||||
let modes = chatModeService.getModes();
|
||||
assert.ok(modes.builtin.find(mode => mode.id === ChatModeKind.Agent));
|
||||
let agents = chatModeService.getModes();
|
||||
assert.ok(agents.builtin.find(agent => agent.id === ChatModeKind.Agent));
|
||||
|
||||
// Without tools agent - Agent mode should not be present
|
||||
chatAgentService.setHasToolsAgent(false);
|
||||
modes = chatModeService.getModes();
|
||||
assert.strictEqual(modes.builtin.find(mode => mode.id === ChatModeKind.Agent), undefined);
|
||||
agents = chatModeService.getModes();
|
||||
assert.strictEqual(agents.builtin.find(agent => agent.id === ChatModeKind.Agent), undefined);
|
||||
|
||||
// But Ask and Edit modes should always be present
|
||||
assert.ok(modes.builtin.find(mode => mode.id === ChatModeKind.Ask));
|
||||
assert.ok(modes.builtin.find(mode => mode.id === ChatModeKind.Edit));
|
||||
assert.ok(agents.builtin.find(agent => agent.id === ChatModeKind.Ask));
|
||||
assert.ok(agents.builtin.find(agent => agent.id === ChatModeKind.Edit));
|
||||
});
|
||||
|
||||
test('should find builtin modes by id', () => {
|
||||
@@ -107,12 +107,12 @@ suite('ChatModeService', () => {
|
||||
});
|
||||
|
||||
test('should handle custom modes from prompts service', async () => {
|
||||
const customMode: ICustomChatMode = {
|
||||
const customMode: ICustomAgent = {
|
||||
uri: URI.parse('file:///test/custom-mode.md'),
|
||||
name: 'Test Mode',
|
||||
description: 'A test custom mode',
|
||||
tools: ['tool1', 'tool2'],
|
||||
modeInstructions: { content: 'Custom mode body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Custom mode body', toolReferences: [] },
|
||||
source: workspaceSource
|
||||
};
|
||||
|
||||
@@ -131,7 +131,7 @@ suite('ChatModeService', () => {
|
||||
assert.strictEqual(testMode.description.get(), customMode.description);
|
||||
assert.strictEqual(testMode.kind, ChatModeKind.Agent);
|
||||
assert.deepStrictEqual(testMode.customTools?.get(), customMode.tools);
|
||||
assert.deepStrictEqual(testMode.modeInstructions?.get(), customMode.modeInstructions);
|
||||
assert.deepStrictEqual(testMode.modeInstructions?.get(), customMode.agentInstructions);
|
||||
assert.deepStrictEqual(testMode.handOffs?.get(), customMode.handOffs);
|
||||
assert.strictEqual(testMode.uri?.get().toString(), customMode.uri.toString());
|
||||
assert.deepStrictEqual(testMode.source, workspaceSource);
|
||||
@@ -143,12 +143,12 @@ suite('ChatModeService', () => {
|
||||
eventFired = true;
|
||||
}));
|
||||
|
||||
const customMode: ICustomChatMode = {
|
||||
const customMode: ICustomAgent = {
|
||||
uri: URI.parse('file:///test/custom-mode.md'),
|
||||
name: 'Test Mode',
|
||||
description: 'A test custom mode',
|
||||
tools: [],
|
||||
modeInstructions: { content: 'Custom mode body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Custom mode body', toolReferences: [] },
|
||||
source: workspaceSource,
|
||||
};
|
||||
|
||||
@@ -161,12 +161,12 @@ suite('ChatModeService', () => {
|
||||
});
|
||||
|
||||
test('should find custom modes by id', async () => {
|
||||
const customMode: ICustomChatMode = {
|
||||
const customMode: ICustomAgent = {
|
||||
uri: URI.parse('file:///test/findable-mode.md'),
|
||||
name: 'Findable Mode',
|
||||
description: 'A findable custom mode',
|
||||
tools: [],
|
||||
modeInstructions: { content: 'Findable mode body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Findable mode body', toolReferences: [] },
|
||||
source: workspaceSource,
|
||||
};
|
||||
|
||||
@@ -184,12 +184,12 @@ suite('ChatModeService', () => {
|
||||
|
||||
test('should update existing custom mode instances when data changes', async () => {
|
||||
const uri = URI.parse('file:///test/updateable-mode.md');
|
||||
const initialMode: ICustomChatMode = {
|
||||
const initialMode: ICustomAgent = {
|
||||
uri,
|
||||
name: 'Initial Mode',
|
||||
description: 'Initial description',
|
||||
tools: ['tool1'],
|
||||
modeInstructions: { content: 'Initial body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Initial body', toolReferences: [] },
|
||||
model: 'gpt-4',
|
||||
source: workspaceSource,
|
||||
};
|
||||
@@ -202,11 +202,11 @@ suite('ChatModeService', () => {
|
||||
assert.strictEqual(initialCustomMode.description.get(), 'Initial description');
|
||||
|
||||
// Update the mode data
|
||||
const updatedMode: ICustomChatMode = {
|
||||
const updatedMode: ICustomAgent = {
|
||||
...initialMode,
|
||||
description: 'Updated description',
|
||||
tools: ['tool1', 'tool2'],
|
||||
modeInstructions: { content: 'Updated body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Updated body', toolReferences: [] },
|
||||
model: 'Updated model'
|
||||
};
|
||||
|
||||
@@ -228,21 +228,21 @@ suite('ChatModeService', () => {
|
||||
});
|
||||
|
||||
test('should remove custom modes that no longer exist', async () => {
|
||||
const mode1: ICustomChatMode = {
|
||||
const mode1: ICustomAgent = {
|
||||
uri: URI.parse('file:///test/mode1.md'),
|
||||
name: 'Mode 1',
|
||||
description: 'First mode',
|
||||
tools: [],
|
||||
modeInstructions: { content: 'Mode 1 body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Mode 1 body', toolReferences: [] },
|
||||
source: workspaceSource,
|
||||
};
|
||||
|
||||
const mode2: ICustomChatMode = {
|
||||
const mode2: ICustomAgent = {
|
||||
uri: URI.parse('file:///test/mode2.md'),
|
||||
name: 'Mode 2',
|
||||
description: 'Second mode',
|
||||
tools: [],
|
||||
modeInstructions: { content: 'Mode 2 body', toolReferences: [] },
|
||||
agentInstructions: { content: 'Mode 2 body', toolReferences: [] },
|
||||
source: workspaceSource,
|
||||
};
|
||||
|
||||
|
||||
@@ -11,22 +11,22 @@ import { ITextModel } from '../../../../../editor/common/model.js';
|
||||
import { IExtensionDescription } from '../../../../../platform/extensions/common/extensions.js';
|
||||
import { PromptsType } from '../../common/promptSyntax/promptTypes.js';
|
||||
import { ParsedPromptFile } from '../../common/promptSyntax/service/newPromptsParser.js';
|
||||
import { ICustomChatMode, IPromptPath, IPromptsService, PromptsStorage } from '../../common/promptSyntax/service/promptsService.js';
|
||||
import { ICustomAgent, IPromptPath, IPromptsService, PromptsStorage } from '../../common/promptSyntax/service/promptsService.js';
|
||||
|
||||
export class MockPromptsService implements IPromptsService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeCustomChatModes = new Emitter<void>();
|
||||
readonly onDidChangeCustomChatModes = this._onDidChangeCustomChatModes.event;
|
||||
readonly onDidChangeCustomAgents = this._onDidChangeCustomChatModes.event;
|
||||
|
||||
private _customModes: ICustomChatMode[] = [];
|
||||
private _customModes: ICustomAgent[] = [];
|
||||
|
||||
setCustomModes(modes: ICustomChatMode[]): void {
|
||||
setCustomModes(modes: ICustomAgent[]): void {
|
||||
this._customModes = modes;
|
||||
this._onDidChangeCustomChatModes.fire();
|
||||
}
|
||||
|
||||
async getCustomChatModes(token: CancellationToken): Promise<readonly ICustomChatMode[]> {
|
||||
async getCustomAgents(token: CancellationToken): Promise<readonly ICustomAgent[]> {
|
||||
return this._customModes;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ class TestPromptFileReference extends Disposable {
|
||||
const result: any = {};
|
||||
result.promptType = getPromptFileType(this.rootFileUri);
|
||||
if (ast.header) {
|
||||
for (const key of ['tools', 'model', 'mode', 'applyTo', 'description'] as const) {
|
||||
for (const key of ['tools', 'model', 'agent', 'applyTo', 'description'] as const) {
|
||||
if (ast.header[key]) {
|
||||
result[key] = ast.header[key];
|
||||
}
|
||||
@@ -308,7 +308,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'description: \'Root prompt description.\'',
|
||||
'tools: [\'my-tool1\']',
|
||||
'mode: "agent" ',
|
||||
'agent: "agent" ',
|
||||
'---',
|
||||
'## Files',
|
||||
'\t- this file #file:folder1/file3.prompt.md ',
|
||||
@@ -340,7 +340,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , ]',
|
||||
'something: true',
|
||||
'mode: \'ask\'\t',
|
||||
'agent: \'ask\'\t',
|
||||
'---',
|
||||
'this file has a non-existing #file:./some-non-existing/file.prompt.md\t\treference',
|
||||
'',
|
||||
@@ -406,7 +406,7 @@ suite('PromptFileReference', function () {
|
||||
metadata,
|
||||
{
|
||||
promptType: PromptsType.prompt,
|
||||
mode: 'agent',
|
||||
agent: 'agent',
|
||||
description: 'Root prompt description.',
|
||||
tools: ['my-tool1'],
|
||||
},
|
||||
@@ -471,7 +471,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , \'my-tool3\' , ]',
|
||||
'something: true',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -577,7 +577,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , \'my-tool3\' , ]',
|
||||
'something: true',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -627,8 +627,8 @@ suite('PromptFileReference', function () {
|
||||
});
|
||||
});
|
||||
|
||||
suite('tools and mode compatibility', () => {
|
||||
test('ask mode', async function () {
|
||||
suite('tools and agent compatibility', () => {
|
||||
test('ask agent', async function () {
|
||||
const rootFolderName = 'resolves-nested-file-references';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootUri = toUri(rootFolder);
|
||||
@@ -653,7 +653,7 @@ suite('PromptFileReference', function () {
|
||||
contents: [
|
||||
'---',
|
||||
'description: \'Description of my prompt.\'',
|
||||
'mode: "ask" ',
|
||||
'agent: "ask" ',
|
||||
'---',
|
||||
'## Files',
|
||||
'\t- this file #file:folder1/file3.prompt.md ',
|
||||
@@ -669,7 +669,7 @@ suite('PromptFileReference', function () {
|
||||
contents: [
|
||||
'---',
|
||||
'tools: [ false, \'my-tool1\' , ]',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
' some more\t content',
|
||||
],
|
||||
@@ -683,7 +683,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , ]',
|
||||
'something: true',
|
||||
'mode: \'ask\'\t',
|
||||
'agent: \'ask\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -724,14 +724,14 @@ suite('PromptFileReference', function () {
|
||||
metadata,
|
||||
{
|
||||
promptType: PromptsType.prompt,
|
||||
mode: ChatModeKind.Ask,
|
||||
agent: ChatModeKind.Ask,
|
||||
description: 'Description of my prompt.',
|
||||
},
|
||||
'Must have correct metadata.',
|
||||
);
|
||||
});
|
||||
|
||||
test('edit mode', async function () {
|
||||
test('edit agent', async function () {
|
||||
const rootFolderName = 'resolves-nested-file-references';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootUri = toUri(rootFolder);
|
||||
@@ -756,7 +756,7 @@ suite('PromptFileReference', function () {
|
||||
contents: [
|
||||
'---',
|
||||
'description: \'Description of my prompt.\'',
|
||||
'mode:\t\t"edit"\t\t',
|
||||
'agent:\t\t"edit"\t\t',
|
||||
'---',
|
||||
'## Files',
|
||||
'\t- this file #file:folder1/file3.prompt.md ',
|
||||
@@ -785,7 +785,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , ]',
|
||||
'something: true',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -826,7 +826,7 @@ suite('PromptFileReference', function () {
|
||||
metadata,
|
||||
{
|
||||
promptType: PromptsType.prompt,
|
||||
mode: ChatModeKind.Edit,
|
||||
agent: ChatModeKind.Edit,
|
||||
description: 'Description of my prompt.',
|
||||
},
|
||||
'Must have correct metadata.',
|
||||
@@ -834,7 +834,7 @@ suite('PromptFileReference', function () {
|
||||
|
||||
});
|
||||
|
||||
test('agent mode', async function () {
|
||||
test('agent', async function () {
|
||||
const rootFolderName = 'resolves-nested-file-references';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootUri = toUri(rootFolder);
|
||||
@@ -859,7 +859,7 @@ suite('PromptFileReference', function () {
|
||||
contents: [
|
||||
'---',
|
||||
'description: \'Description of my prompt.\'',
|
||||
'mode: \t\t "agent" \t\t ',
|
||||
'agent: \t\t "agent" \t\t ',
|
||||
'---',
|
||||
'## Files',
|
||||
'\t- this file #file:folder1/file3.prompt.md ',
|
||||
@@ -888,7 +888,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , \'my-tool3\' , ]',
|
||||
'something: true',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
@@ -929,7 +929,7 @@ suite('PromptFileReference', function () {
|
||||
metadata,
|
||||
{
|
||||
promptType: PromptsType.prompt,
|
||||
mode: ChatModeKind.Agent,
|
||||
agent: ChatModeKind.Agent,
|
||||
description: 'Description of my prompt.',
|
||||
},
|
||||
'Must have correct metadata.',
|
||||
@@ -937,7 +937,7 @@ suite('PromptFileReference', function () {
|
||||
|
||||
});
|
||||
|
||||
test('no mode', async function () {
|
||||
test('no agent', async function () {
|
||||
const rootFolderName = 'resolves-nested-file-references';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootUri = toUri(rootFolder);
|
||||
@@ -991,7 +991,7 @@ suite('PromptFileReference', function () {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , \'my-tool3\' , ]',
|
||||
'something: true',
|
||||
'mode: \'agent\'\t',
|
||||
'agent: \'agent\'\t',
|
||||
'---',
|
||||
'',
|
||||
'',
|
||||
|
||||
@@ -13,15 +13,15 @@ import { NewPromptsParser } from '../../../../common/promptSyntax/service/newPro
|
||||
suite('NewPromptsParser', () => {
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('mode', async () => {
|
||||
const uri = URI.parse('file:///test/chatmode.md');
|
||||
test('agent', async () => {
|
||||
const uri = URI.parse('file:///test/test.vscode-agent.md');
|
||||
const content = [
|
||||
/* 01 */'---',
|
||||
/* 02 */`description: "Agent mode test"`,
|
||||
/* 02 */`description: "Agent test"`,
|
||||
/* 03 */'model: GPT 4.1',
|
||||
/* 04 */`tools: ['tool1', 'tool2']`,
|
||||
/* 05 */'---',
|
||||
/* 06 */'This is a chat mode test.',
|
||||
/* 06 */'This is an agent test.',
|
||||
/* 07 */'Here is a #tool1 variable and a #file:./reference1.md as well as a [reference](./reference2.md).',
|
||||
].join('\n');
|
||||
const result = new NewPromptsParser().parse(uri, content);
|
||||
@@ -30,7 +30,7 @@ suite('NewPromptsParser', () => {
|
||||
assert.ok(result.body);
|
||||
assert.deepEqual(result.header.range, { startLineNumber: 2, startColumn: 1, endLineNumber: 5, endColumn: 1 });
|
||||
assert.deepEqual(result.header.attributes, [
|
||||
{ key: 'description', range: new Range(2, 1, 2, 31), value: { type: 'string', value: 'Agent mode test', range: new Range(2, 14, 2, 31) } },
|
||||
{ key: 'description', range: new Range(2, 1, 2, 26), value: { type: 'string', value: 'Agent test', range: new Range(2, 14, 2, 26) } },
|
||||
{ key: 'model', range: new Range(3, 1, 3, 15), value: { type: 'string', value: 'GPT 4.1', range: new Range(3, 8, 3, 15) } },
|
||||
{
|
||||
key: 'tools', range: new Range(4, 1, 4, 26), value: {
|
||||
@@ -41,24 +41,24 @@ suite('NewPromptsParser', () => {
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(result.body.range, { startLineNumber: 6, startColumn: 1, endLineNumber: 8, endColumn: 1 });
|
||||
assert.equal(result.body.offset, 80);
|
||||
assert.equal(result.body.getContent(), 'This is a chat mode test.\nHere is a #tool1 variable and a #file:./reference1.md as well as a [reference](./reference2.md).');
|
||||
assert.equal(result.body.offset, 75);
|
||||
assert.equal(result.body.getContent(), 'This is an agent test.\nHere is a #tool1 variable and a #file:./reference1.md as well as a [reference](./reference2.md).');
|
||||
|
||||
assert.deepEqual(result.body.fileReferences, [
|
||||
{ range: new Range(7, 39, 7, 54), content: './reference1.md', isMarkdownLink: false },
|
||||
{ range: new Range(7, 80, 7, 95), content: './reference2.md', isMarkdownLink: true }
|
||||
]);
|
||||
assert.deepEqual(result.body.variableReferences, [
|
||||
{ range: new Range(7, 12, 7, 17), name: 'tool1', offset: 116 }
|
||||
{ range: new Range(7, 12, 7, 17), name: 'tool1', offset: 108 }
|
||||
]);
|
||||
assert.deepEqual(result.header.description, 'Agent mode test');
|
||||
assert.deepEqual(result.header.description, 'Agent test');
|
||||
assert.deepEqual(result.header.model, 'GPT 4.1');
|
||||
assert.ok(result.header.tools);
|
||||
assert.deepEqual(result.header.tools, ['tool1', 'tool2']);
|
||||
});
|
||||
|
||||
test('mode with handoff', async () => {
|
||||
const uri = URI.parse('file:///test/chatmode.md');
|
||||
const uri = URI.parse('file:///test/test.vscode-agent.md');
|
||||
const content = [
|
||||
/* 01 */'---',
|
||||
/* 02 */`description: "Agent test"`,
|
||||
@@ -152,7 +152,7 @@ suite('NewPromptsParser', () => {
|
||||
const content = [
|
||||
/* 01 */'---',
|
||||
/* 02 */`description: "General purpose coding assistant"`,
|
||||
/* 03 */'mode: agent',
|
||||
/* 03 */'agent: agent',
|
||||
/* 04 */'model: GPT 4.1',
|
||||
/* 05 */`tools: ['search', 'terminal']`,
|
||||
/* 06 */'---',
|
||||
@@ -165,7 +165,7 @@ suite('NewPromptsParser', () => {
|
||||
assert.deepEqual(result.header.range, { startLineNumber: 2, startColumn: 1, endLineNumber: 6, endColumn: 1 });
|
||||
assert.deepEqual(result.header.attributes, [
|
||||
{ key: 'description', range: new Range(2, 1, 2, 48), value: { type: 'string', value: 'General purpose coding assistant', range: new Range(2, 14, 2, 48) } },
|
||||
{ key: 'mode', range: new Range(3, 1, 3, 12), value: { type: 'string', value: 'agent', range: new Range(3, 7, 3, 12) } },
|
||||
{ key: 'agent', range: new Range(3, 1, 3, 13), value: { type: 'string', value: 'agent', range: new Range(3, 8, 3, 13) } },
|
||||
{ key: 'model', range: new Range(4, 1, 4, 15), value: { type: 'string', value: 'GPT 4.1', range: new Range(4, 8, 4, 15) } },
|
||||
{
|
||||
key: 'tools', range: new Range(5, 1, 5, 30), value: {
|
||||
@@ -176,16 +176,16 @@ suite('NewPromptsParser', () => {
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(result.body.range, { startLineNumber: 7, startColumn: 1, endLineNumber: 8, endColumn: 1 });
|
||||
assert.equal(result.body.offset, 113);
|
||||
assert.equal(result.body.offset, 114);
|
||||
assert.equal(result.body.getContent(), 'This is a prompt file body referencing #search and [docs](https://example.com/docs).');
|
||||
assert.deepEqual(result.body.fileReferences, [
|
||||
{ range: new Range(7, 59, 7, 83), content: 'https://example.com/docs', isMarkdownLink: true },
|
||||
]);
|
||||
assert.deepEqual(result.body.variableReferences, [
|
||||
{ range: new Range(7, 41, 7, 47), name: 'search', offset: 152 }
|
||||
{ range: new Range(7, 41, 7, 47), name: 'search', offset: 153 }
|
||||
]);
|
||||
assert.deepEqual(result.header.description, 'General purpose coding assistant');
|
||||
assert.deepEqual(result.header.mode, 'agent');
|
||||
assert.deepEqual(result.header.agent, 'agent');
|
||||
assert.deepEqual(result.header.model, 'GPT 4.1');
|
||||
assert.ok(result.header.tools);
|
||||
assert.deepEqual(result.header.tools, ['search', 'terminal']);
|
||||
@@ -255,7 +255,7 @@ suite('NewPromptsParser', () => {
|
||||
}
|
||||
]);
|
||||
assert.deepEqual(result.header.description, undefined);
|
||||
assert.deepEqual(result.header.mode, undefined);
|
||||
assert.deepEqual(result.header.agent, undefined);
|
||||
assert.deepEqual(result.header.model, undefined);
|
||||
assert.ok(result.header.tools);
|
||||
assert.deepEqual(result.header.tools, ['built-in', 'browser-click', 'openPullRequest', 'copilotCodingAgent']);
|
||||
|
||||
@@ -34,9 +34,9 @@ import { TestContextService, TestUserDataProfileService } from '../../../../../.
|
||||
import { ChatRequestVariableSet, isPromptFileVariableEntry, toFileVariableEntry } from '../../../../common/chatVariableEntries.js';
|
||||
import { ComputeAutomaticInstructions, newInstructionsCollectionEvent } from '../../../../common/promptSyntax/computeAutomaticInstructions.js';
|
||||
import { PromptsConfig } from '../../../../common/promptSyntax/config/config.js';
|
||||
import { INSTRUCTION_FILE_EXTENSION, INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, MODE_DEFAULT_SOURCE_FOLDER, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../../../../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { INSTRUCTION_FILE_EXTENSION, INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, LEGACY_MODE_DEFAULT_SOURCE_FOLDER, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../../../../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { INSTRUCTIONS_LANGUAGE_ID, PROMPT_LANGUAGE_ID, PromptsType } from '../../../../common/promptSyntax/promptTypes.js';
|
||||
import { ICustomChatMode, IPromptsService, PromptsStorage } from '../../../../common/promptSyntax/service/promptsService.js';
|
||||
import { ICustomAgent, IPromptsService, PromptsStorage } from '../../../../common/promptSyntax/service/promptsService.js';
|
||||
import { PromptsService } from '../../../../common/promptSyntax/service/promptsServiceImpl.js';
|
||||
import { MockFilesystem } from '../testUtils/mockFilesystem.js';
|
||||
|
||||
@@ -61,7 +61,7 @@ suite('PromptsService', () => {
|
||||
testConfigService.setUserConfiguration(PromptsConfig.USE_NESTED_AGENT_MD, false);
|
||||
testConfigService.setUserConfiguration(PromptsConfig.INSTRUCTIONS_LOCATION_KEY, { [INSTRUCTIONS_DEFAULT_SOURCE_FOLDER]: true });
|
||||
testConfigService.setUserConfiguration(PromptsConfig.PROMPT_LOCATIONS_KEY, { [PROMPT_DEFAULT_SOURCE_FOLDER]: true });
|
||||
testConfigService.setUserConfiguration(PromptsConfig.MODE_LOCATION_KEY, { [MODE_DEFAULT_SOURCE_FOLDER]: true });
|
||||
testConfigService.setUserConfiguration(PromptsConfig.MODE_LOCATION_KEY, { [LEGACY_MODE_DEFAULT_SOURCE_FOLDER]: true });
|
||||
|
||||
instaService.stub(IConfigurationService, testConfigService);
|
||||
instaService.stub(IWorkbenchEnvironmentService, {});
|
||||
@@ -127,7 +127,7 @@ suite('PromptsService', () => {
|
||||
'---',
|
||||
'description: \'Root prompt description.\'',
|
||||
'tools: [\'my-tool1\', , true]',
|
||||
'mode: "agent" ',
|
||||
'agent: "agent" ',
|
||||
'---',
|
||||
'## Files',
|
||||
'\t- this file #file:folder1/file3.prompt.md ',
|
||||
@@ -146,7 +146,7 @@ suite('PromptsService', () => {
|
||||
contents: [
|
||||
'---',
|
||||
'tools: [ false, \'my-tool1\' , ]',
|
||||
'mode: \'edit\'',
|
||||
'agent: \'edit\'',
|
||||
'---',
|
||||
'',
|
||||
'[](./some-other-folder/non-existing-folder)',
|
||||
@@ -163,7 +163,7 @@ suite('PromptsService', () => {
|
||||
'---',
|
||||
'tools: [\'my-tool1\', "my-tool2", true, , ]',
|
||||
'something: true',
|
||||
'mode: \'ask\'\t',
|
||||
'agent: \'ask\'\t',
|
||||
'description: "File 4 splendid description."',
|
||||
'---',
|
||||
'this file has a non-existing #file:./some-non-existing/file.prompt.md\t\treference',
|
||||
@@ -222,7 +222,7 @@ suite('PromptsService', () => {
|
||||
assert.deepEqual(result1.uri, rootFileUri);
|
||||
assert.deepEqual(result1.header?.description, 'Root prompt description.');
|
||||
assert.deepEqual(result1.header?.tools, ['my-tool1']);
|
||||
assert.deepEqual(result1.header?.mode, 'agent');
|
||||
assert.deepEqual(result1.header?.agent, 'agent');
|
||||
assert.ok(result1.body);
|
||||
assert.deepEqual(
|
||||
result1.body.fileReferences.map(r => result1.body?.resolveFilePath(r.content)),
|
||||
@@ -231,14 +231,14 @@ suite('PromptsService', () => {
|
||||
assert.deepEqual(
|
||||
result1.body.variableReferences,
|
||||
[
|
||||
{ name: 'my-tool', range: new Range(10, 5, 10, 12), offset: 239 },
|
||||
{ name: 'my-other-tool', range: new Range(11, 5, 11, 18), offset: 251 },
|
||||
{ name: 'my-tool', range: new Range(10, 5, 10, 12), offset: 240 },
|
||||
{ name: 'my-other-tool', range: new Range(11, 5, 11, 18), offset: 252 },
|
||||
]
|
||||
);
|
||||
|
||||
const result2 = await service.parseNew(file3, CancellationToken.None);
|
||||
assert.deepEqual(result2.uri, file3);
|
||||
assert.deepEqual(result2.header?.mode, 'edit');
|
||||
assert.deepEqual(result2.header?.agent, 'edit');
|
||||
assert.ok(result2.body);
|
||||
assert.deepEqual(
|
||||
result2.body.fileReferences.map(r => result2.body?.resolveFilePath(r.content)),
|
||||
@@ -737,14 +737,14 @@ suite('PromptsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
suite('getCustomChatModes', () => {
|
||||
suite('getCustomAgents', () => {
|
||||
teardown(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
|
||||
test('header with handOffs', async () => {
|
||||
const rootFolderName = 'custom-modes-with-handoffs';
|
||||
const rootFolderName = 'custom-agents-with-handoffs';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootFolderUri = URI.file(rootFolder);
|
||||
|
||||
@@ -755,13 +755,13 @@ suite('PromptsService', () => {
|
||||
name: rootFolderName,
|
||||
children: [
|
||||
{
|
||||
name: '.github/chatmodes',
|
||||
name: '.github/agents',
|
||||
children: [
|
||||
{
|
||||
name: 'mode1.chatmode.md',
|
||||
name: 'agent1.vscode-agent.md',
|
||||
contents: [
|
||||
'---',
|
||||
'description: \'Mode file 1.\'',
|
||||
'description: \'Agent file 1.\'',
|
||||
'handoffs: [ { agent: "Edit", label: "Do it", prompt: "Do it now" } ]',
|
||||
'---',
|
||||
],
|
||||
@@ -772,20 +772,20 @@ suite('PromptsService', () => {
|
||||
],
|
||||
}])).mock();
|
||||
|
||||
const result = (await service.getCustomChatModes(CancellationToken.None)).map(mode => ({ ...mode, uri: URI.from(mode.uri) }));
|
||||
const expected: ICustomChatMode[] = [
|
||||
const result = (await service.getCustomAgents(CancellationToken.None)).map(agent => ({ ...agent, uri: URI.from(agent.uri) }));
|
||||
const expected: ICustomAgent[] = [
|
||||
{
|
||||
name: 'mode1',
|
||||
description: 'Mode file 1.',
|
||||
name: 'agent1',
|
||||
description: 'Agent file 1.',
|
||||
handOffs: [{ agent: 'Edit', label: 'Do it', prompt: 'Do it now', send: undefined }],
|
||||
modeInstructions: {
|
||||
agentInstructions: {
|
||||
content: '',
|
||||
toolReferences: [],
|
||||
metadata: undefined
|
||||
},
|
||||
model: undefined,
|
||||
tools: undefined,
|
||||
uri: URI.joinPath(rootFolderUri, '.github/chatmodes/mode1.chatmode.md'),
|
||||
uri: URI.joinPath(rootFolderUri, '.github/agents/agent1.vscode-agent.md'),
|
||||
source: { storage: PromptsStorage.local }
|
||||
},
|
||||
];
|
||||
@@ -793,12 +793,12 @@ suite('PromptsService', () => {
|
||||
assert.deepEqual(
|
||||
result,
|
||||
expected,
|
||||
'Must get custom chat modes.',
|
||||
'Must get custom agents.',
|
||||
);
|
||||
});
|
||||
|
||||
test('body with tool references', async () => {
|
||||
const rootFolderName = 'custom-modes';
|
||||
const rootFolderName = 'custom-agents';
|
||||
const rootFolder = `/${rootFolderName}`;
|
||||
const rootFolderUri = URI.file(rootFolder);
|
||||
|
||||
@@ -810,20 +810,20 @@ suite('PromptsService', () => {
|
||||
name: rootFolderName,
|
||||
children: [
|
||||
{
|
||||
name: '.github/chatmodes',
|
||||
name: '.github/agents',
|
||||
children: [
|
||||
{
|
||||
name: 'mode1.chatmode.md',
|
||||
name: 'agent1.vscode-agent.md',
|
||||
contents: [
|
||||
'---',
|
||||
'description: \'Mode file 1.\'',
|
||||
'description: \'Agent file 1.\'',
|
||||
'tools: [ tool1, tool2 ]',
|
||||
'---',
|
||||
'Do it with #tool1',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'mode2.chatmode.md',
|
||||
name: 'agent2.vscode-agent.md',
|
||||
contents: [
|
||||
'First use #tool2\nThen use #tool1',
|
||||
],
|
||||
@@ -834,25 +834,25 @@ suite('PromptsService', () => {
|
||||
],
|
||||
}])).mock();
|
||||
|
||||
const result = (await service.getCustomChatModes(CancellationToken.None)).map(mode => ({ ...mode, uri: URI.from(mode.uri) }));
|
||||
const expected: ICustomChatMode[] = [
|
||||
const result = (await service.getCustomAgents(CancellationToken.None)).map(agent => ({ ...agent, uri: URI.from(agent.uri) }));
|
||||
const expected: ICustomAgent[] = [
|
||||
{
|
||||
name: 'mode1',
|
||||
description: 'Mode file 1.',
|
||||
name: 'agent1',
|
||||
description: 'Agent file 1.',
|
||||
tools: ['tool1', 'tool2'],
|
||||
modeInstructions: {
|
||||
agentInstructions: {
|
||||
content: 'Do it with #tool1',
|
||||
toolReferences: [{ name: 'tool1', range: { start: 11, endExclusive: 17 } }],
|
||||
metadata: undefined
|
||||
},
|
||||
handOffs: undefined,
|
||||
model: undefined,
|
||||
uri: URI.joinPath(rootFolderUri, '.github/chatmodes/mode1.chatmode.md'),
|
||||
uri: URI.joinPath(rootFolderUri, '.github/agents/agent1.vscode-agent.md'),
|
||||
source: { storage: PromptsStorage.local },
|
||||
},
|
||||
{
|
||||
name: 'mode2',
|
||||
modeInstructions: {
|
||||
name: 'agent2',
|
||||
agentInstructions: {
|
||||
content: 'First use #tool2\nThen use #tool1',
|
||||
toolReferences: [
|
||||
{ name: 'tool1', range: { start: 26, endExclusive: 32 } },
|
||||
@@ -860,7 +860,7 @@ suite('PromptsService', () => {
|
||||
],
|
||||
metadata: undefined
|
||||
},
|
||||
uri: URI.joinPath(rootFolderUri, '.github/chatmodes/mode2.chatmode.md'),
|
||||
uri: URI.joinPath(rootFolderUri, '.github/agents/agent2.vscode-agent.md'),
|
||||
source: { storage: PromptsStorage.local },
|
||||
}
|
||||
];
|
||||
@@ -868,9 +868,12 @@ suite('PromptsService', () => {
|
||||
assert.deepEqual(
|
||||
result,
|
||||
expected,
|
||||
'Must get custom chat modes.',
|
||||
'Must get custom agents.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('listPromptFiles - extensions', () => {
|
||||
|
||||
test('Contributed prompt file', async () => {
|
||||
const uri = URI.parse('file://extensions/my-extension/textMate.instructions.md');
|
||||
|
||||
@@ -35,7 +35,7 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind
|
||||
import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js';
|
||||
import { IChatAgentService } from '../../chat/common/chatAgents.js';
|
||||
import { ChatAgentLocation } from '../../chat/common/constants.js';
|
||||
import { MODE_FILE_EXTENSION } from '../../chat/common/promptSyntax/config/promptFileLocations.js';
|
||||
import { AGENT_FILE_EXTENSION, LEGACY_MODE_FILE_EXTENSION } from '../../chat/common/promptSyntax/config/promptFileLocations.js';
|
||||
import { INSTRUCTIONS_LANGUAGE_ID, PROMPT_LANGUAGE_ID } from '../../chat/common/promptSyntax/promptTypes.js';
|
||||
import { ACTION_START, CTX_INLINE_CHAT_V1_ENABLED, CTX_INLINE_CHAT_VISIBLE, InlineChatConfigKeys } from '../common/inlineChat.js';
|
||||
import { AbstractInline1ChatAction } from './inlineChatActions.js';
|
||||
@@ -51,7 +51,8 @@ const IGNORED_LANGUAGE_IDS = new Set([
|
||||
'search-result',
|
||||
INSTRUCTIONS_LANGUAGE_ID,
|
||||
PROMPT_LANGUAGE_ID,
|
||||
MODE_FILE_EXTENSION
|
||||
LEGACY_MODE_FILE_EXTENSION,
|
||||
AGENT_FILE_EXTENSION
|
||||
]);
|
||||
|
||||
export const CTX_INLINE_CHAT_SHOWING_HINT = new RawContextKey<boolean>('inlineChatShowingHint', false, localize('inlineChatShowingHint', "Whether inline chat shows a contextual hint"));
|
||||
|
||||
Reference in New Issue
Block a user