1use std::collections::BTreeMap;
4use std::sync::LazyLock;
5
6use vtcode_commons::reasoning::ReasoningEffortLevel;
7
8use crate::constants::tools;
9use crate::constants::ui;
10use crate::core::permissions::{AgentPermissionsConfig, PermissionDefault};
11use crate::core::tools::ToolPolicy;
12
13use super::{AgentMode, SubagentSource, SubagentSpec};
14
15const BUILTIN_DEFAULT_AGENT: &str = r#"You are the default VT Code execution subagent.
16
17Work directly, keep context isolated from the parent session, and return concise summaries.
18Match the repository's local patterns, verify changes, and avoid unrelated edits.
19If a file is referenced, read it before answering; base claims about code on what you have read.
20Only make changes that are directly requested or clearly necessary. Keep solutions simple and focused.
21Do not add features, refactor code, or make improvements beyond what was asked.
22Verify your work by running the smallest relevant check before reporting completion."#;
23
24const BUILTIN_EXPLORER_AGENT: &str = r#"You are a fast read-only exploration subagent.
25
26Search the codebase, inspect relevant files, and return concise findings with file references.
27Do not modify files or take mutating actions.
28Read files before making claims about their contents.
29Use structural search and grep over shell exploration when possible.
30Return findings with file paths and line numbers for easy navigation."#;
31
32const BUILTIN_WORKER_AGENT: &str = r#"You are a write-capable worker subagent.
33
34Handle bounded implementation work, verify results, and return a concise outcome summary with
35any important risks or follow-up items.
36Read files before editing them or describing what they contain.
37Only make changes that are directly requested. Keep solutions simple and focused.
38Do not add features, refactor surrounding code, or make improvements beyond the scope.
39Verify your changes by running relevant tests or checks before reporting completion.
40If calls repeat without progress, re-plan instead of retrying identically."#;
41
42const BUILTIN_BUILD_PRIMARY_AGENT: &str = r#"You are the build agent.
43
44Understand the user's request, then use available tools to inspect context and make changes.
45Read files before editing. Search before guessing at code structure.
46Edit, write, and run commands directly when the request is clear.
47Keep changes focused on what was asked. Do not add unrelated features or refactors.
48When planning is needed, state the plan briefly before implementation. When the user only wants
49discussion or review, do not edit files.
50Report changed files, validation, and remaining risks clearly."#;
51
52const BUILTIN_AUTO_PRIMARY_AGENT: &str = r#"You are the auto agent.
53
54Work autonomously within the active permission policy, taking direct action when the request is clear.
55Inspect the relevant repository context before editing, keep changes focused, and verify with the
56narrowest useful checks before reporting completion.
57Pause for user input when the scope is ambiguous, risky, or outside the requested work."#;
58
59const BUILTIN_COORDINATOR_PRIMARY_AGENT: &str = r#"You are the coordinator agent.
60
61Define explicit matrix tasks, dependencies, resource capacities, timeouts, and verification commands.
62Delegate all shell execution, file changes, and verification to scheduler-owned matrix workers.
63Use matrix create to persist a specification, then start to freeze it and dispatch work.
64Delegate discovery to a read-only explorer before making repository claims, while the matrix is idle.
65During active execution, use matrix status and control actions; do not spawn independent workers.
66Resolve failed checks, permission denials, exhausted budgets, and unsafe retries with the user.
67Worker summaries are not verification evidence. Report success only after matrix final verification.
68Pause stops dispatch; cancel is terminal. Preserve sandbox, approvals, and existing budgets."#;
69
70const DISCUSSION_FIRST_GUIDANCE: &str = r#"Be discussion-first. Clarify scope, constraints, contradictions, and options before implementation.
71Resolve ordinary ambiguity from repository evidence when possible; ask the user directly only when material ambiguity is critical.
72Stop researching when existing evidence supports a decision."#;
73
74const BUILTIN_PLAN_AGENT_ROLE: &str = r#"You are a read-only planning agent.
75
76Use repository-grounded, read-only discovery to gather the minimum context needed to support a plan or design decision.
77Return findings, risks, and constraints clearly, with specific code references and file paths.
78Read relevant files before making claims about the codebase.
79Use structural search to find patterns across the repository.
80When ready, emit exactly one final <proposed_plan> block for review.
81Never write the plan file with shell or file-editing tools; the runtime persists the plan and tracker artifacts.
82When the user asks for implementation, present the plan and wait for explicit user approval before implementation instead of suggesting an immediate edit."#;
83
84const BUILTIN_DUCK_PRIMARY_AGENT_ROLE: &str = r#"You are the duck agent.
85
86Do not edit files; you are for rubber-ducking only.
87If the user asks for edits, suggest pressing Tab to switch to the Build agent for implementation."#;
88static BUILTIN_SUBAGENTS: LazyLock<Vec<SubagentSpec>> = LazyLock::new(builtin_subagents_inner);
98
99pub fn builtin_subagents() -> Vec<SubagentSpec> {
100 BUILTIN_SUBAGENTS.clone()
101}
102
103fn builtin_subagents_inner() -> Vec<SubagentSpec> {
104 vec![
105 builtin_primary_build_agent(),
106 builtin_primary_auto_agent(),
107 builtin_primary_coordinator_agent(),
108 builtin_primary_duck_agent(),
109 builtin_plan_agent(),
110 SubagentSpec {
111 name: "default".to_string(),
112 description: "Default inheriting subagent for general delegated work.".to_string(),
113 prompt: BUILTIN_DEFAULT_AGENT.to_string(),
114 tools: None,
115 disallowed_tools: Vec::new(),
116 model: Some("inherit".to_string()),
117 color: Some("blue".to_string()),
118 reasoning_effort: None,
119 permissions: mutating_agent_permissions(),
120 skills: Vec::new(),
121 mcp_servers: Vec::new(),
122 hooks: None,
123 background: false,
124 mode: AgentMode::Subagent,
125 max_turns: None,
126 nickname_candidates: vec!["default".to_string()],
127 initial_prompt: None,
128 memory: None,
129 isolation: None,
130 aliases: Vec::new(),
131 source: SubagentSource::Builtin,
132 file_path: None,
133 warnings: Vec::new(),
134 tool_policy_overrides: BTreeMap::new(),
135 },
136 SubagentSpec {
137 name: "explorer".to_string(),
138 description: "Read-only exploration specialist. Use proactively for code search, file discovery, and repository understanding.".to_string(),
139 prompt: BUILTIN_EXPLORER_AGENT.to_string(),
140 tools: Some(builtin_readonly_tool_ids()),
141 disallowed_tools: builtin_readonly_disallowed_tool_ids(),
142 model: Some("small".to_string()),
143 color: Some("cyan".to_string()),
144 reasoning_effort: Some(ReasoningEffortLevel::Low),
145 permissions: readonly_agent_permissions(),
146 skills: Vec::new(),
147 mcp_servers: Vec::new(),
148 hooks: None,
149 background: false,
150 mode: AgentMode::Subagent,
151 max_turns: None,
152 nickname_candidates: vec!["explore".to_string(), "search".to_string()],
153 initial_prompt: None,
154 memory: None,
155 isolation: None,
156 aliases: vec!["explore".to_string()],
157 source: SubagentSource::Builtin,
158 file_path: None,
159 warnings: Vec::new(),
160 tool_policy_overrides: BTreeMap::new(),
161 },
162 SubagentSpec {
163 name: "worker".to_string(),
164 description: "Write-capable execution subagent for bounded implementation or multi-step action.".to_string(),
165 prompt: BUILTIN_WORKER_AGENT.to_string(),
166 tools: None,
167 disallowed_tools: Vec::new(),
168 model: Some("inherit".to_string()),
169 color: Some("magenta".to_string()),
170 reasoning_effort: None,
171 permissions: mutating_agent_permissions(),
172 skills: Vec::new(),
173 mcp_servers: Vec::new(),
174 hooks: None,
175 background: false,
176 mode: AgentMode::Subagent,
177 max_turns: None,
178 nickname_candidates: vec!["general".to_string(), "worker".to_string()],
179 initial_prompt: None,
180 memory: None,
181 isolation: None,
182 aliases: vec!["general".to_string(), "general-purpose".to_string()],
183 source: SubagentSource::Builtin,
184 file_path: None,
185 warnings: Vec::new(),
186 tool_policy_overrides: BTreeMap::new(),
187 },
188 ]
189}
190
191pub fn builtin_primary_build_agent() -> SubagentSpec {
192 SubagentSpec {
193 name: "build".to_string(),
194 description: "Built-in implementation agent for the main session.".to_string(),
195 prompt: BUILTIN_BUILD_PRIMARY_AGENT.to_string(),
196 tools: None,
197 disallowed_tools: Vec::new(),
198 model: Some("inherit".to_string()),
199 color: Some(ui::AGENT_COLOR_BUILD.to_string()),
200 reasoning_effort: None,
201 permissions: mutating_agent_permissions(),
202 skills: Vec::new(),
203 mcp_servers: Vec::new(),
204 hooks: None,
205 background: false,
206 mode: AgentMode::Primary,
207 max_turns: None,
208 nickname_candidates: vec!["build".to_string(), "builder".to_string()],
209 initial_prompt: None,
210 memory: None,
211 isolation: None,
212 aliases: vec!["builder".to_string()],
213 source: SubagentSource::Builtin,
214 file_path: None,
215 warnings: Vec::new(),
216 tool_policy_overrides: {
217 let mut m = BTreeMap::new();
218 m.insert("exec_command".to_string(), ToolPolicy::Allow);
219 m
220 },
221 }
222}
223
224pub fn builtin_primary_auto_agent() -> SubagentSpec {
225 SubagentSpec {
226 name: "auto".to_string(),
227 description: "Built-in autonomous implementation agent for the main session.".to_string(),
228 prompt: BUILTIN_AUTO_PRIMARY_AGENT.to_string(),
229 tools: None,
230 disallowed_tools: Vec::new(),
231 model: Some("inherit".to_string()),
232 color: Some(ui::AGENT_COLOR_AUTO.to_string()),
233 reasoning_effort: None,
234 permissions: auto_agent_permissions(),
235 skills: Vec::new(),
236 mcp_servers: Vec::new(),
237 hooks: None,
238 background: false,
239 mode: AgentMode::Primary,
240 max_turns: None,
241 nickname_candidates: vec!["auto".to_string()],
242 initial_prompt: None,
243 memory: None,
244 isolation: None,
245 aliases: vec!["autonomous".to_string()],
246 source: SubagentSource::Builtin,
247 file_path: None,
248 warnings: Vec::new(),
249 tool_policy_overrides: {
250 let mut m = BTreeMap::new();
251 m.insert("exec_command".to_string(), ToolPolicy::Allow);
252 m
253 },
254 }
255}
256
257pub fn builtin_primary_coordinator_agent() -> SubagentSpec {
259 let mut spec = builtin_primary_build_agent();
260 spec.name = "coordinator".to_string();
261 spec.description = "Deterministic local matrix orchestration for the main session.".to_string();
262 spec.prompt = BUILTIN_COORDINATOR_PRIMARY_AGENT.to_string();
263 spec.tools = Some(
264 [
265 "matrix",
266 tools::REQUEST_USER_INPUT,
267 "agent",
268 tools::RECORD_DECISION,
269 tools::TASK_TRACKER,
270 ]
271 .into_iter()
272 .map(str::to_string)
273 .collect(),
274 );
275 spec.nickname_candidates = vec!["coordinator".to_string()];
276 spec.aliases.clear();
277 spec.tool_policy_overrides.clear();
278 spec
279}
280
281pub fn builtin_plan_agent() -> SubagentSpec {
282 SubagentSpec {
283 name: "plan".to_string(),
284 description: "Built-in read-only planning agent definition.".to_string(),
285 prompt: format!("{DISCUSSION_FIRST_GUIDANCE}\n\n{BUILTIN_PLAN_AGENT_ROLE}"),
286 tools: Some(builtin_primary_readonly_tool_ids()),
287 disallowed_tools: builtin_readonly_disallowed_tool_ids(),
288 model: Some("inherit".to_string()),
289 color: Some(ui::AGENT_COLOR_PLAN.to_string()),
290 reasoning_effort: None,
291 permissions: plan_agent_permissions(),
292 skills: Vec::new(),
293 mcp_servers: Vec::new(),
294 hooks: None,
295 background: false,
296 mode: AgentMode::Primary,
297 max_turns: None,
298 nickname_candidates: vec!["plan".to_string(), "planner".to_string()],
299 initial_prompt: None,
300 memory: None,
301 isolation: None,
302 aliases: vec!["planner".to_string()],
303 source: SubagentSource::Builtin,
304 file_path: None,
305 warnings: Vec::new(),
306 tool_policy_overrides: BTreeMap::new(),
307 }
308}
309
310pub fn builtin_primary_duck_agent() -> SubagentSpec {
311 SubagentSpec {
312 name: "duck".to_string(),
313 description: "Built-in discussion-first agent for the main session.".to_string(),
314 prompt: format!("{DISCUSSION_FIRST_GUIDANCE}\n\n{BUILTIN_DUCK_PRIMARY_AGENT_ROLE}"),
315 tools: Some(builtin_primary_readonly_tool_ids()),
316 disallowed_tools: builtin_readonly_disallowed_tool_ids(),
317 model: Some("inherit".to_string()),
318 color: Some(ui::AGENT_COLOR_DUCK.to_string()),
319 reasoning_effort: None,
320 permissions: readonly_interview_agent_permissions(),
321 skills: Vec::new(),
322 mcp_servers: Vec::new(),
323 hooks: None,
324 background: false,
325 mode: AgentMode::Primary,
326 max_turns: None,
327 nickname_candidates: vec!["duck".to_string()],
328 initial_prompt: None,
329 memory: None,
330 isolation: None,
331 aliases: Vec::new(),
332 source: SubagentSource::Builtin,
333 file_path: None,
334 warnings: Vec::new(),
335 tool_policy_overrides: BTreeMap::new(),
336 }
337}
338
339fn builtin_readonly_tool_ids() -> Vec<String> {
340 vec![
347 tools::CODE_SEARCH.to_string(),
348 tools::EXEC_COMMAND.to_string(),
349 tools::GREP_FILE.to_string(),
350 tools::READ_FILE.to_string(),
351 tools::LIST_FILES.to_string(),
352 ]
353}
354
355fn builtin_primary_readonly_tool_ids() -> Vec<String> {
359 let mut ids = builtin_readonly_tool_ids();
360 ids.push(tools::REQUEST_USER_INPUT.to_string());
361 ids.push(tools::RECORD_DECISION.to_string());
362 ids
363}
364
365fn builtin_readonly_disallowed_tool_ids() -> Vec<String> {
366 Vec::new()
367}
368
369pub(super) fn mutating_agent_permissions() -> AgentPermissionsConfig {
370 AgentPermissionsConfig::new(PermissionDefault::Ask)
371}
372
373fn auto_agent_permissions() -> AgentPermissionsConfig {
374 AgentPermissionsConfig::new(PermissionDefault::Auto)
375}
376
377pub(super) fn readonly_agent_permissions() -> AgentPermissionsConfig {
378 let mut permissions = AgentPermissionsConfig::new(PermissionDefault::Deny);
379 permissions.allow = vec!["read".to_string()];
380 permissions
381}
382
383pub(super) fn readonly_interview_agent_permissions() -> AgentPermissionsConfig {
388 let mut permissions = readonly_agent_permissions();
389 permissions.allow.push("request_user_input".to_string());
390 permissions.allow.push(tools::RECORD_DECISION.to_string());
391 permissions
392}
393
394fn plan_agent_permissions() -> AgentPermissionsConfig {
404 let mut permissions = readonly_interview_agent_permissions();
405 permissions.allow.push("bash".to_string());
406 permissions
407}