agcodex_core/subagents/
mod.rs

1//! Subagent system for AGCodex
2//!
3//! The subagent system enables specialized AI assistants for task-specific workflows.
4//! Each subagent operates with its own context, custom prompts, and tool permissions.
5//!
6//! ## Key Features
7//! - **Specialized Agents**: Code review, refactoring, debugging, testing, etc.
8//! - **Mode Override**: Agents can override operating mode (Plan/Build/Review)
9//! - **Template System**: Reusable agent configurations
10//! - **Hot Reload**: Dynamic loading of agent configurations
11//! - **Chaining & Parallel**: Sequential (→) or parallel (+) execution
12//!
13//! ## Usage
14//! ```
15//! @agent-code-reviewer - Review code for quality and security
16//! @agent-refactorer → @agent-test-writer - Chain agents sequentially
17//! @agent-performance + @agent-security - Run agents in parallel
18//! ```
19//!
20//! ## Configuration
21//! Agents are configured via TOML files in:
22//! - `~/.agcodex/agents/global/` - Available everywhere
23//! - `./.agcodex/agents/` - Project-specific agents
24//! - `templates/` - Reusable templates
25
26pub 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
38// Re-export main types for convenience
39pub 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
108/// Result type for subagent operations
109pub type SubagentResult<T> = std::result::Result<T, SubagentError>;
110
111/// Errors that can occur in the subagent system
112#[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/// Context passed to subagents during execution
155#[derive(Debug, Clone)]
156pub struct SubagentContext {
157    /// Unique identifier for this execution
158    pub execution_id: Uuid,
159
160    /// Current operating mode
161    pub mode: OperatingMode,
162
163    /// Available tools for this agent
164    pub available_tools: Vec<String>,
165
166    /// Conversation history (limited)
167    pub conversation_context: String,
168
169    /// Current working directory
170    pub working_directory: std::path::PathBuf,
171
172    /// User-provided parameters
173    pub parameters: HashMap<String, String>,
174
175    /// Agent-specific metadata
176    pub metadata: HashMap<String, serde_json::Value>,
177}
178
179/// Status of a subagent execution
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub enum SubagentStatus {
182    /// Agent is waiting to start
183    Pending,
184    /// Agent is currently running
185    Running,
186    /// Agent completed successfully
187    Completed,
188    /// Agent failed with an error
189    Failed(String),
190    /// Agent was cancelled
191    Cancelled,
192}
193
194/// Result of a subagent execution
195#[derive(Debug, Clone)]
196pub struct SubagentExecution {
197    /// Unique identifier for this execution
198    pub id: Uuid,
199
200    /// Name of the agent that was executed
201    pub agent_name: String,
202
203    /// Current status
204    pub status: SubagentStatus,
205
206    /// Agent output/response
207    pub output: Option<String>,
208
209    /// Files modified by the agent
210    pub modified_files: Vec<std::path::PathBuf>,
211
212    /// Execution start time
213    pub started_at: std::time::SystemTime,
214
215    /// Execution end time (if completed)
216    pub completed_at: Option<std::time::SystemTime>,
217
218    /// Error information (if failed)
219    pub error: Option<String>,
220}
221
222impl SubagentExecution {
223    /// Create a new pending execution
224    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    /// Mark the execution as started
238    pub fn start(&mut self) {
239        self.status = SubagentStatus::Running;
240        self.started_at = std::time::SystemTime::now();
241    }
242
243    /// Mark the execution as completed with output
244    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    /// Mark the execution as failed with an error
252    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    /// Get the execution duration
259    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// Include comprehensive test suite from tests.rs
299#[cfg(test)]
300mod tests;