Path: blob/main/extensions/copilot/src/extension/agents/vscode-node/planAgentProvider.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';7import { AGENT_FILE_EXTENSION } from '../../../platform/customInstructions/common/promptTypes';8import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';9import { IFileSystemService } from '../../../platform/filesystem/common/fileSystemService';10import { ILogService } from '../../../platform/log/common/logService';11import { IExperimentationService } from '../../../platform/telemetry/common/nullExperimentationService';12import { Disposable } from '../../../util/vs/base/common/lifecycle';13import { AgentConfig, AgentHandoff, buildAgentMarkdown, DEFAULT_READ_TOOLS } from './agentTypes';1415/**16* Base Plan agent configuration - embedded from Plan.agent.md17* This avoids runtime file loading and YAML parsing dependencies.18*/19const BASE_PLAN_AGENT_CONFIG: AgentConfig = {20name: 'Plan',21description: 'Researches and outlines multi-step plans',22argumentHint: 'Outline the goal or problem to research',23target: 'vscode',24disableModelInvocation: true,25tools: [26...DEFAULT_READ_TOOLS,27],28handoffs: [], // Handoffs are generated dynamically in buildCustomizedConfig29body: '' // Body is generated dynamically in buildCustomizedConfig30};3132/**33* Provides the Plan agent dynamically with settings-based customization.34*35* This provider uses an embedded configuration and generates .agent.md content36* with settings-based customization (additional tools and model override).37* No external file loading or YAML parsing dependencies required.38*/39export class PlanAgentProvider extends Disposable implements vscode.ChatCustomAgentProvider {40readonly label = vscode.l10n.t('Plan Agent');4142private static readonly CACHE_DIR = 'plan-agent';43private static readonly AGENT_FILENAME = `Plan${AGENT_FILE_EXTENSION}`;4445private readonly _onDidChangeCustomAgents = this._register(new vscode.EventEmitter<void>());46readonly onDidChangeCustomAgents = this._onDidChangeCustomAgents.event;4748constructor(49@IConfigurationService private readonly configurationService: IConfigurationService,50@IVSCodeExtensionContext private readonly extensionContext: IVSCodeExtensionContext,51@IFileSystemService private readonly fileSystemService: IFileSystemService,52@ILogService private readonly logService: ILogService,53@IExperimentationService private readonly experimentationService: IExperimentationService,54) {55super();5657// Listen for settings changes to refresh agents58// Note: When settings change, we fire onDidChangeCustomAgents which causes VS Code to re-fetch59// the agent definition. However, handoff buttons already rendered may not work as60// these capture the model at render time.61this._register(this.configurationService.onDidChangeConfiguration(e => {62if (e.affectsConfiguration(ConfigKey.PlanAgentAdditionalTools.fullyQualifiedId) ||63e.affectsConfiguration(ConfigKey.Deprecated.PlanAgentModel.fullyQualifiedId) ||64e.affectsConfiguration('chat.planAgent.defaultModel') ||65e.affectsConfiguration(ConfigKey.ImplementAgentModel.fullyQualifiedId) ||66e.affectsConfiguration(ConfigKey.ExploreAgentEnabled.fullyQualifiedId) ||67e.affectsConfiguration(ConfigKey.Advanced.SearchSubagentToolEnabled.fullyQualifiedId)) {68this._onDidChangeCustomAgents.fire();69}70}));71}7273async provideCustomAgents(74_context: unknown,75_token: vscode.CancellationToken76): Promise<vscode.ChatResource[]> {77// Build config with settings-based customization78const config = this.buildCustomizedConfig();7980// Generate .agent.md content81const content = buildAgentMarkdown(config);8283// Write to cache file and return URI84const fileUri = await this.writeCacheFile(content);85return [{ uri: fileUri }];86}8788private async writeCacheFile(content: string): Promise<vscode.Uri> {89const cacheDir = vscode.Uri.joinPath(90this.extensionContext.globalStorageUri,91PlanAgentProvider.CACHE_DIR92);9394// Ensure cache directory exists95try {96await this.fileSystemService.stat(cacheDir);97} catch {98await this.fileSystemService.createDirectory(cacheDir);99}100101const fileUri = vscode.Uri.joinPath(cacheDir, PlanAgentProvider.AGENT_FILENAME);102await this.fileSystemService.writeFile(fileUri, new TextEncoder().encode(content));103this.logService.trace(`[PlanAgentProvider] Wrote agent file: ${fileUri.toString()}`);104return fileUri;105}106107static buildAgentBody(exploreEnabled: boolean, searchSubagentEnabled: boolean): string {108let discoverySection: string;109if (exploreEnabled) {110discoverySection = `## 1. Discovery111112Run 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.113114Update the plan with your findings.`;115} else if (searchSubagentEnabled) {116discoverySection = `## 1. Discovery117118Use #tool:searchSubagent 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 search subagents in parallel** — one per area — to speed up discovery.119120Update the plan with your findings.`;121} else {122discoverySection = `## 1. Discovery123124Search the codebase to gather context, analogous existing features to use as implementation templates, and potential blockers or ambiguities.125126Update the plan with your findings.`;127}128129return `You are a PLANNING AGENT, pairing with the user to create a detailed, actionable plan.130131You research the codebase → clarify with the user → capture findings and decisions into a comprehensive plan. This iterative approach catches edge cases and non-obvious requirements BEFORE implementation begins.132133Your SOLE responsibility is planning. NEVER start implementation.134135**Current plan**: \`/memories/session/plan.md\` - update using #tool:vscode/memory .136137<rules>138- 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.139- Use #tool:vscode/askQuestions freely to clarify requirements — don't make large assumptions140- Present a well-researched plan with loose ends tied BEFORE implementation141</rules>142143<workflow>144Cycle through these phases based on user input. This is iterative, not linear. If the user task is highly ambiguous, do only *Discovery* to outline a draft plan, then move on to alignment before fleshing out the full plan.145146${discoverySection}147148## 2. Alignment149150If research reveals major ambiguities or if you need to validate assumptions:151- Use #tool:vscode/askQuestions to clarify intent with the user.152- Surface discovered technical constraints or alternative approaches153- If answers significantly change the scope, loop back to **Discovery**154155## 3. Design156157Once context is clear, draft a comprehensive implementation plan.158159The plan should reflect:160- Structured concise enough to be scannable and detailed enough for effective execution161- Step-by-step implementation with explicit dependencies — mark which steps can run in parallel vs. which block on prior steps162- For plans with many steps, group into named phases that are each independently verifiable163- Verification steps for validating the implementation, both automated and manual164- Critical architecture to reuse or use as reference — reference specific functions, types, or patterns, not just file names165- Critical files to be modified (with full paths)166- Explicit scope boundaries — what's included and what's deliberately excluded167- Reference decisions from the discussion168- Leave no ambiguity169170Save the comprehensive plan document to \`/memories/session/plan.md\` via #tool:vscode/memory, then show the scannable plan to the user for review. You MUST show plan to the user, as the plan file is for persistence only, not a substitute for showing it to the user.171172## 4. Refinement173174On user input after showing the plan:175- Changes requested → revise and present updated plan. Update \`/memories/session/plan.md\` to keep the documented plan in sync176- Questions asked → clarify, or use #tool:vscode/askQuestions for follow-ups177- Alternatives wanted → loop back to **Discovery** with new subagent178- Approval given → acknowledge, the user can now use handoff buttons179180Keep iterating until explicit approval or handoff.181</workflow>182183<plan_style_guide>184\`\`\`markdown185## Plan: {Title (2-10 words)}186187{TL;DR - what, why, and how (your recommended approach).}188189**Steps**1901. {Implementation step-by-step — note dependency ("*depends on N*") or parallelism ("*parallel with step N*") when applicable}1912. {For plans with 5+ steps, group steps into named phases with enough detail to be independently actionable}192193**Relevant files**194- \`{full/path/to/file}\` — {what to modify or reuse, referencing specific functions/patterns}195196**Verification**1971. {Verification steps for validating the implementation (**Specific** tasks, tests, commands, MCP tools, etc; not generic statements)}198199**Decisions** (if applicable)200- {Decision, assumptions, and includes/excluded scope}201202**Further Considerations** (if applicable, 1-3 items)2031. {Clarifying question with recommendation. Option A / Option B / Option C}2042. {…}205\`\`\`206207Rules:208- NO code blocks — describe changes, link to files and specific symbols/functions209- NO blocking questions at the end — ask during workflow via #tool:vscode/askQuestions210- The plan MUST be presented to the user, don't just mention the plan file.211</plan_style_guide>`;212}213214private buildCustomizedConfig(): AgentConfig {215const additionalTools = this.configurationService.getConfig(ConfigKey.PlanAgentAdditionalTools);216const isExploreEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.ExploreAgentEnabled, this.experimentationService);217const isSearchSubagentEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolEnabled, this.experimentationService);218const coreDefaultModel = this.configurationService.getNonExtensionConfig<string>('chat.planAgent.defaultModel');219const modelOverride = coreDefaultModel || this.configurationService.getConfig(ConfigKey.Deprecated.PlanAgentModel);220221const implementAgentModelOverride = this.configurationService.getConfig(ConfigKey.ImplementAgentModel);222223// Build handoffs dynamically with model override224const startImplementationHandoff: AgentHandoff = {225label: 'Start Implementation',226agent: 'agent',227prompt: 'Start implementation',228send: true,229...(implementAgentModelOverride ? { model: implementAgentModelOverride } : {})230};231232const openInEditorHandoff: AgentHandoff = {233label: 'Open in Editor',234agent: 'agent',235prompt: '#createFile the plan as is into an untitled file (`untitled:plan-${camelCaseName}.prompt.md` without frontmatter) for further refinement.',236showContinueOn: false,237send: true238};239240// Collect tools to add241const toolsToAdd: string[] = [...additionalTools];242243// Always include askQuestions tool (now provided by core)244toolsToAdd.push('vscode/askQuestions');245246// When explore agent is enabled, include the 'agent' tool to allow sub-agent calls247if (isExploreEnabled) {248toolsToAdd.push('agent');249}250251// Merge additional tools (deduplicated)252const tools = toolsToAdd.length > 0253? [...new Set([...BASE_PLAN_AGENT_CONFIG.tools, ...toolsToAdd])]254: [...BASE_PLAN_AGENT_CONFIG.tools];255256// Start with base config257return {258...BASE_PLAN_AGENT_CONFIG,259// When explore agent is enabled, allow the Explore subagent260...(isExploreEnabled ? { agents: ['Explore'] } : {}),261tools,262handoffs: [startImplementationHandoff, openInEditorHandoff, ...(BASE_PLAN_AGENT_CONFIG.handoffs ?? [])],263body: PlanAgentProvider.buildAgentBody(isExploreEnabled, isSearchSubagentEnabled),264...(modelOverride ? { model: modelOverride } : {}),265};266}267}268269270