agcodex_core/subagents/
mod.rs1pub mod agents;
27pub mod built_in;
28pub mod config;
29pub mod context;
30pub mod invocation;
31pub mod manager;
32pub mod mcp_tools;
33pub mod orchestrator;
34pub mod parser;
35pub mod registry;
36pub mod worktree;
37
38pub use agents::AgentRegistry;
40pub use agents::AgentResult;
41pub use agents::AgentStatus;
42pub use agents::CodeReviewerAgent;
43pub use agents::DebuggerAgent;
44pub use agents::DocsAgent;
45pub use agents::Finding;
46pub use agents::PerformanceAgent;
47pub use agents::RefactorerAgent;
48pub use agents::SecurityAgent;
49pub use agents::Severity;
50pub use agents::Subagent;
51pub use agents::TestWriterAgent;
52pub use built_in::create_default_registry;
53pub use built_in::register_built_in_agents;
54pub use config::IntelligenceLevel;
55pub use config::SubagentConfig;
56pub use config::ToolPermission;
57pub use context::AgentContext;
58pub use context::AgentContextSnapshot;
59pub use context::AgentMessage;
60pub use context::CancellationToken;
61pub use context::ContextError;
62pub use context::ContextFinding;
63pub use context::ContextResult;
64pub use context::ExecutionMetricsSnapshot;
65pub use context::FindingSeverity;
66pub use context::MessagePriority;
67pub use context::MessageReceiver;
68pub use context::MessageTarget;
69pub use context::MessageType;
70pub use context::ProgressEvent;
71pub use context::ProgressInfo;
72pub use context::ProgressStage;
73pub use context::ProgressTracker;
74pub use invocation::AgentChain;
75pub use invocation::AgentInvocation;
76pub use invocation::ExecutionPlan;
77pub use invocation::ExecutionStep;
78pub use invocation::InvocationParser;
79pub use invocation::InvocationRequest;
80pub use manager::AgentHandle;
81pub use manager::AgentManager;
82pub use manager::AgentStats;
83pub use manager::MessageBus;
84pub use mcp_tools::McpAgentHandler;
85pub use mcp_tools::McpAgentTool;
86pub use mcp_tools::McpAgentToolProvider;
87pub use orchestrator::AgentOrchestrator;
88pub use orchestrator::ContextSnapshot;
89pub use orchestrator::OrchestratorConfig;
90pub use orchestrator::OrchestratorResult;
91pub use orchestrator::ProgressUpdate;
92pub use orchestrator::SharedContext;
93pub use parser::AgentParser;
94pub use parser::ChainOperator;
95pub use parser::ParsedInvocation;
96pub use registry::SubagentRegistry;
97pub use registry::SubagentRegistryError;
98pub use worktree::AgentWorktree;
99pub use worktree::ConflictStrategy;
100pub use worktree::MergeResult;
101pub use worktree::WorktreeManager;
102pub use worktree::WorktreePool;
103
104use crate::modes::OperatingMode;
105use std::collections::HashMap;
106use uuid::Uuid;
107
108pub type SubagentResult<T> = std::result::Result<T, SubagentError>;
110
111#[derive(thiserror::Error, Debug)]
113pub enum SubagentError {
114 #[error("agent not found: {name}")]
115 AgentNotFound { name: String },
116
117 #[error("invalid agent configuration: {0}")]
118 InvalidConfig(String),
119
120 #[error("agent execution failed: {0}")]
121 ExecutionFailed(String),
122
123 #[error("circular dependency detected in agent chain: {chain:?}")]
124 CircularDependency { chain: Vec<String> },
125
126 #[error("agent timeout: {name}")]
127 Timeout { name: String },
128
129 #[error("tool permission denied: {tool} for agent {agent}")]
130 ToolPermissionDenied { tool: String, agent: String },
131
132 #[error("mode restriction violation: {mode:?} does not allow {operation}")]
133 ModeRestriction {
134 mode: OperatingMode,
135 operation: String,
136 },
137
138 #[error("I/O error: {0}")]
139 Io(#[from] std::io::Error),
140
141 #[error("serialization error: {0}")]
142 Serialization(#[from] toml::de::Error),
143
144 #[error("tool error: {0}")]
145 Tool(#[from] crate::code_tools::ToolError),
146
147 #[error("file watching error: {0}")]
148 FileWatcher(String),
149
150 #[error("agent template not found: {name}")]
151 TemplateNotFound { name: String },
152}
153
154#[derive(Debug, Clone)]
156pub struct SubagentContext {
157 pub execution_id: Uuid,
159
160 pub mode: OperatingMode,
162
163 pub available_tools: Vec<String>,
165
166 pub conversation_context: String,
168
169 pub working_directory: std::path::PathBuf,
171
172 pub parameters: HashMap<String, String>,
174
175 pub metadata: HashMap<String, serde_json::Value>,
177}
178
179#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum SubagentStatus {
182 Pending,
184 Running,
186 Completed,
188 Failed(String),
190 Cancelled,
192}
193
194#[derive(Debug, Clone)]
196pub struct SubagentExecution {
197 pub id: Uuid,
199
200 pub agent_name: String,
202
203 pub status: SubagentStatus,
205
206 pub output: Option<String>,
208
209 pub modified_files: Vec<std::path::PathBuf>,
211
212 pub started_at: std::time::SystemTime,
214
215 pub completed_at: Option<std::time::SystemTime>,
217
218 pub error: Option<String>,
220}
221
222impl SubagentExecution {
223 pub fn new(agent_name: String) -> Self {
225 Self {
226 id: Uuid::new_v4(),
227 agent_name,
228 status: SubagentStatus::Pending,
229 output: None,
230 modified_files: Vec::new(),
231 started_at: std::time::SystemTime::now(),
232 completed_at: None,
233 error: None,
234 }
235 }
236
237 pub fn start(&mut self) {
239 self.status = SubagentStatus::Running;
240 self.started_at = std::time::SystemTime::now();
241 }
242
243 pub fn complete(&mut self, output: String, modified_files: Vec<std::path::PathBuf>) {
245 self.status = SubagentStatus::Completed;
246 self.output = Some(output);
247 self.modified_files = modified_files;
248 self.completed_at = Some(std::time::SystemTime::now());
249 }
250
251 pub fn fail(&mut self, error: String) {
253 self.status = SubagentStatus::Failed(error.clone());
254 self.error = Some(error);
255 self.completed_at = Some(std::time::SystemTime::now());
256 }
257
258 pub fn duration(&self) -> Option<std::time::Duration> {
260 self.completed_at
261 .and_then(|end| end.duration_since(self.started_at).ok())
262 }
263}
264
265#[cfg(test)]
266mod basic_tests {
267 use super::*;
268
269 #[test]
270 fn test_subagent_execution_lifecycle() {
271 let mut execution = SubagentExecution::new("test-agent".to_string());
272
273 assert_eq!(execution.status, SubagentStatus::Pending);
274 assert_eq!(execution.agent_name, "test-agent");
275
276 execution.start();
277 assert_eq!(execution.status, SubagentStatus::Running);
278
279 execution.complete("Success!".to_string(), vec![]);
280 assert_eq!(execution.status, SubagentStatus::Completed);
281 assert_eq!(execution.output.as_ref().unwrap(), "Success!");
282 assert!(execution.duration().is_some());
283 }
284
285 #[test]
286 fn test_subagent_execution_failure() {
287 let mut execution = SubagentExecution::new("failing-agent".to_string());
288
289 execution.start();
290 execution.fail("Something went wrong".to_string());
291
292 assert!(matches!(execution.status, SubagentStatus::Failed(_)));
293 assert_eq!(execution.error.as_ref().unwrap(), "Something went wrong");
294 assert!(execution.duration().is_some());
295 }
296}
297
298#[cfg(test)]
300mod tests;