diff --git a/package.json b/package.json
index 71c8fb63fb..d696777fa7 100644
--- a/package.json
+++ b/package.json
@@ -3947,6 +3947,15 @@
"experimental"
]
},
+ "github.copilot.chat.planAgent.mermaid.enabled": {
+ "type": "boolean",
+ "default": false,
+ "scope": "resource",
+ "markdownDescription": "%github.copilot.config.planAgent.mermaid.enabled%",
+ "tags": [
+ "experimental"
+ ]
+ },
"github.copilot.chat.implementAgent.model": {
"type": "string",
"default": "",
diff --git a/package.nls.json b/package.nls.json
index fca78b7144..d276faf420 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -326,6 +326,7 @@
"github.copilot.config.organizationCustomAgents.enabled": "When enabled, Copilot will load custom agents defined by your GitHub Organization.",
"github.copilot.config.organizationInstructions.enabled": "When enabled, Copilot will load custom instructions defined by your GitHub Organization.",
"github.copilot.config.planAgent.additionalTools": "Additional tools to enable for the Plan agent, on top of built-in tools. Use fully-qualified tool names (e.g., `github/issue_read`, `mcp_server/tool_name`).",
+ "github.copilot.config.planAgent.mermaid.enabled": "Allow the Plan agent to include Mermaid diagrams in plans when they materially clarify architecture, sequencing, or dependencies. When enabled, Plan mode may persist concise fenced `mermaid` blocks in the saved plan.",
"github.copilot.config.implementAgent.model": "Override the language model used when starting implementation from the Plan agent's handoff. Use the format `Model Name (vendor)` (e.g., `GPT-5 (copilot)`). Leave empty to use the default model.",
"github.copilot.config.askAgent.additionalTools": "Additional tools to enable for the Ask agent, on top of built-in read-only tools. Use fully-qualified tool names (e.g., `github/issue_read`, `mcp_server/tool_name`).",
"github.copilot.config.askAgent.model": "Override the language model used by the Ask agent. Leave empty to use the default model.",
diff --git a/src/extension/agents/vscode-node/planAgentProvider.ts b/src/extension/agents/vscode-node/planAgentProvider.ts
index 4ef9f2fb01..f03ea0cfd5 100644
--- a/src/extension/agents/vscode-node/planAgentProvider.ts
+++ b/src/extension/agents/vscode-node/planAgentProvider.ts
@@ -12,6 +12,8 @@ import { ILogService } from '../../../platform/log/common/logService';
import { Disposable } from '../../../util/vs/base/common/lifecycle';
import { AgentConfig, AgentHandoff, buildAgentMarkdown, DEFAULT_READ_TOOLS } from './agentTypes';
+const MERMAID_RENDER_TOOL = 'vscode.mermaid-chat-features/renderMermaidDiagram';
+
/**
* Base Plan agent configuration - embedded from Plan.agent.md
* This avoids runtime file loading and YAML parsing dependencies.
@@ -61,6 +63,7 @@ export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAg
// these capture the model at render time.
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(ConfigKey.PlanAgentAdditionalTools.fullyQualifiedId) ||
+ e.affectsConfiguration(ConfigKey.PlanAgentMermaidEnabled.fullyQualifiedId) ||
e.affectsConfiguration(ConfigKey.Deprecated.PlanAgentModel.fullyQualifiedId) ||
e.affectsConfiguration('chat.planAgent.defaultModel') ||
e.affectsConfiguration(ConfigKey.ImplementAgentModel.fullyQualifiedId)) {
@@ -103,12 +106,26 @@ export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAg
return fileUri;
}
- static buildAgentBody(): string {
+ static buildAgentBody(planAgentMermaidEnabled = false): string {
const discoverySection = `## 1. Discovery
Run the *Explore* subagent to gather context, analogous existing features to use as implementation templates, and potential blockers or ambiguities. When the task spans multiple independent areas (e.g., frontend + backend, different features, separate repos), launch **2-3 *Explore* subagents in parallel** — one per area — to speed up discovery.
Update the plan with your findings.`;
+ const baseRules = [
+ '- STOP if you consider running file editing tools — plans are for others to execute. The only write tool you have is #tool:vscode/memory for persisting plans.',
+ '- Use #tool:vscode/askQuestions freely to clarify requirements — don\'t make large assumptions',
+ '- Present a well-researched plan with loose ends tied BEFORE implementation'
+ ];
+ const mermaidRules = [
+ `- When a Mermaid diagram would materially improve the plan, you may include a concise fenced \`mermaid\` block and you MUST use ${MERMAID_RENDER_TOOL} to render it in the same response.`,
+ '- Never inline Mermaid syntax as plain text. Mermaid content must stay inside fenced \`mermaid\` blocks only.'
+ ];
+ const rulesSection = [...baseRules, ...(planAgentMermaidEnabled ? mermaidRules : [])].join('\n');
+ const planStyleGuideRules = planAgentMermaidEnabled
+ ? `- NO code blocks except relevant Mermaid diagrams when they materially clarify architecture, sequencing, dependencies, or ownership in the plan.
+- If you use Mermaid, keep the diagram concise, preserve the Mermaid source in the plan markdown so it survives persistence and handoff, and always call ${MERMAID_RENDER_TOOL} rather than leaving the syntax unrendered.`
+ : '- NO code blocks — describe changes, link to files and specific symbols/functions';
return `You are a PLANNING AGENT, pairing with the user to create a detailed, actionable plan.
@@ -119,9 +136,7 @@ Your SOLE responsibility is planning. NEVER start implementation.
**Current plan**: \`/memories/session/plan.md\` - update using #tool:vscode/memory .
-- STOP if you consider running file editing tools — plans are for others to execute. The only write tool you have is #tool:vscode/memory for persisting plans.
-- Use #tool:vscode/askQuestions freely to clarify requirements — don't make large assumptions
-- Present a well-researched plan with loose ends tied BEFORE implementation
+${rulesSection}
@@ -189,7 +204,7 @@ Keep iterating until explicit approval or handoff.
\`\`\`
Rules:
-- NO code blocks — describe changes, link to files and specific symbols/functions
+${planStyleGuideRules}
- NO blocking questions at the end — ask during workflow via #tool:vscode/askQuestions
- The plan MUST be presented to the user, don't just mention the plan file.
`;
@@ -197,6 +212,7 @@ Rules:
private buildCustomizedConfig(): AgentConfig {
const additionalTools = this.configurationService.getConfig(ConfigKey.PlanAgentAdditionalTools);
+ const planAgentMermaidEnabled = this.configurationService.getConfig(ConfigKey.PlanAgentMermaidEnabled);
const coreDefaultModel = this.configurationService.getNonExtensionConfig('chat.planAgent.defaultModel');
const modelOverride = coreDefaultModel || this.configurationService.getConfig(ConfigKey.Deprecated.PlanAgentModel);
@@ -221,6 +237,9 @@ Rules:
// Collect tools to add
const toolsToAdd: string[] = [...additionalTools];
+ if (planAgentMermaidEnabled) {
+ toolsToAdd.push(MERMAID_RENDER_TOOL);
+ }
// Always include askQuestions tool (now provided by core)
toolsToAdd.push('vscode/askQuestions');
@@ -235,7 +254,7 @@ Rules:
...BASE_PLAN_AGENT_CONFIG,
tools,
handoffs: [startImplementationHandoff, openInEditorHandoff, ...(BASE_PLAN_AGENT_CONFIG.handoffs ?? [])],
- body: PlanAgentProvider.buildAgentBody(),
+ body: PlanAgentProvider.buildAgentBody(planAgentMermaidEnabled),
...(modelOverride ? { model: modelOverride } : {}),
};
}
diff --git a/src/extension/agents/vscode-node/test/planAgentProvider.spec.ts b/src/extension/agents/vscode-node/test/planAgentProvider.spec.ts
index 1eaa729888..18a29887ff 100644
--- a/src/extension/agents/vscode-node/test/planAgentProvider.spec.ts
+++ b/src/extension/agents/vscode-node/test/planAgentProvider.spec.ts
@@ -82,12 +82,25 @@ suite('PlanAgentProvider', () => {
assert.ok(content.includes('search'));
assert.ok(content.includes('read'));
assert.ok(content.includes('memory'));
+ assert.ok(!content.includes('vscode.mermaid-chat-features/renderMermaidDiagram'));
// Should not have model override (not in base content)
assert.ok(content.includes('name: Plan'));
assert.ok(content.includes('description: Researches and outlines multi-step plans'));
});
+ test('includes Mermaid render tool when plan Mermaid setting is enabled', async () => {
+ await mockConfigurationService.setConfig(ConfigKey.PlanAgentMermaidEnabled, true);
+
+ const provider = createProvider();
+ const agents = await provider.provideCustomAgents({}, {} as any);
+
+ assert.equal(agents.length, 1);
+ const content = await getAgentContent(agents[0]);
+
+ assert.ok(content.includes('vscode.mermaid-chat-features/renderMermaidDiagram'));
+ });
+
test('merges additionalTools setting with base tools', async () => {
await mockConfigurationService.setConfig(ConfigKey.PlanAgentAdditionalTools, ['customTool1', 'customTool2']);
@@ -199,6 +212,19 @@ suite('PlanAgentProvider', () => {
assert.equal(eventFired, true);
});
+ test('fires onDidChangeCustomAgents when PlanAgentMermaidEnabled setting changes', async () => {
+ const provider = createProvider();
+
+ let eventFired = false;
+ provider.onDidChangeCustomAgents(() => {
+ eventFired = true;
+ });
+
+ await mockConfigurationService.setConfig(ConfigKey.PlanAgentMermaidEnabled, true);
+
+ assert.equal(eventFired, true);
+ });
+
test('fires onDidChangeCustomAgents when model setting changes', async () => {
const provider = createProvider();
@@ -267,6 +293,31 @@ suite('PlanAgentProvider', () => {
assert.ok(content.includes('Your SOLE responsibility is planning. NEVER start implementation.'));
});
+ test('keeps non-Mermaid code blocks disallowed by default', async () => {
+ const provider = createProvider();
+ const agents = await provider.provideCustomAgents({}, {} as any);
+
+ const content = await getAgentContent(agents[0]);
+
+ assert.ok(content.includes('```markdown'));
+ assert.ok(content.includes('NO code blocks — describe changes, link to files and specific symbols/functions'));
+ assert.ok(!content.includes('NO code blocks except relevant Mermaid diagrams'));
+ });
+
+ test('documents Mermaid exception when plan Mermaid setting is enabled', async () => {
+ await mockConfigurationService.setConfig(ConfigKey.PlanAgentMermaidEnabled, true);
+
+ const provider = createProvider();
+ const agents = await provider.provideCustomAgents({}, {} as any);
+
+ const content = await getAgentContent(agents[0]);
+
+ assert.ok(content.includes('When a Mermaid diagram would materially improve the plan, you may include a concise fenced `mermaid` block and you MUST use vscode.mermaid-chat-features/renderMermaidDiagram to render it in the same response.'));
+ assert.ok(content.includes('Never inline Mermaid syntax as plain text. Mermaid content must stay inside fenced `mermaid` blocks only.'));
+ assert.ok(content.includes('NO code blocks except relevant Mermaid diagrams when they materially clarify architecture, sequencing, dependencies, or ownership in the plan.'));
+ assert.ok(content.includes('always call vscode.mermaid-chat-features/renderMermaidDiagram rather than leaving the syntax unrendered'));
+ });
+
test('handles empty additionalTools array gracefully', async () => {
await mockConfigurationService.setConfig(ConfigKey.PlanAgentAdditionalTools, []);
diff --git a/src/extension/tools/vscode-node/switchAgentTool.ts b/src/extension/tools/vscode-node/switchAgentTool.ts
index 9ab54f779b..bcdeb7383c 100644
--- a/src/extension/tools/vscode-node/switchAgentTool.ts
+++ b/src/extension/tools/vscode-node/switchAgentTool.ts
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
+import { ConfigKey } from '../../../platform/configuration/common/configurationService';
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
import { LanguageModelTextPart, LanguageModelToolResult, MarkdownString } from '../../../vscodeTypes';
import { PlanAgentProvider } from '../../agents/vscode-node/planAgentProvider';
@@ -26,7 +27,7 @@ export class SwitchAgentTool implements ICopilotTool {
throw new Error(vscode.l10n.t('Only "Plan" agent is supported'));
}
- const planAgentBody = PlanAgentProvider.buildAgentBody();
+ const planAgentBody = PlanAgentProvider.buildAgentBody(vscode.workspace.getConfiguration().get(ConfigKey.PlanAgentMermaidEnabled.fullyQualifiedId, false));
// Execute command to switch agent
await vscode.commands.executeCommand('workbench.action.chat.toggleAgentMode', {
diff --git a/src/platform/configuration/common/configurationService.ts b/src/platform/configuration/common/configurationService.ts
index 1d4cfdaf7f..102c5db6c9 100644
--- a/src/platform/configuration/common/configurationService.ts
+++ b/src/platform/configuration/common/configurationService.ts
@@ -988,6 +988,8 @@ export namespace ConfigKey {
/** Additional tools to enable for the Plan agent (additive to base tools) */
export const PlanAgentAdditionalTools = defineSetting('chat.planAgent.additionalTools', ConfigType.Simple, []);
+ /** Whether the Plan agent can include Mermaid diagrams in plans when relevant */
+ export const PlanAgentMermaidEnabled = defineSetting('chat.planAgent.mermaid.enabled', ConfigType.Simple, false);
/** Model override for Implement agent (empty = use default) */
export const ImplementAgentModel = defineSetting('chat.implementAgent.model', ConfigType.Simple, '');