1use std::collections::HashSet;
7use std::sync::{Arc, RwLock};
8
9use crate::agents::agent::ActiveAgent;
10use crate::agents::catalog::AgentCatalog;
11use crate::module::{ContextComponent, Module, PromptComponent};
12use crate::tools::r#trait::ToolExecutor;
13use crate::Agent;
14
15pub mod complete_task;
16pub mod spawn_agent;
17
18pub use complete_task::CompleteTask;
19pub use spawn_agent::SpawnAgent;
20
21pub struct SpawnModule {
22 catalog: Arc<AgentCatalog>,
23 agents: Arc<RwLock<Vec<ActiveAgent>>>,
24}
25
26impl SpawnModule {
27 pub fn new(catalog: Arc<AgentCatalog>, initial_agent: Arc<dyn Agent>) -> Self {
28 Self {
29 catalog,
30 agents: Arc::new(RwLock::new(vec![ActiveAgent::new(initial_agent)])),
31 }
32 }
33
34 pub fn current_agent_name(&self) -> Option<String> {
36 self.agents
37 .read()
38 .ok()?
39 .last()
40 .map(|a| a.agent.name().to_string())
41 }
42
43 pub fn with_current_agent<F, R>(&self, f: F) -> Option<R>
45 where
46 F: FnOnce(&ActiveAgent) -> R,
47 {
48 let agents = self.agents.read().ok()?;
49 agents.last().map(f)
50 }
51
52 pub fn with_current_agent_mut<F, R>(&self, f: F) -> Option<R>
54 where
55 F: FnOnce(&mut ActiveAgent) -> R,
56 {
57 let mut agents = self.agents.write().ok()?;
58 agents.last_mut().map(f)
59 }
60
61 pub fn with_root_agent<F, R>(&self, f: F) -> Option<R>
63 where
64 F: FnOnce(&ActiveAgent) -> R,
65 {
66 let agents = self.agents.read().ok()?;
67 agents.first().map(f)
68 }
69
70 pub fn with_root_agent_mut<F, R>(&self, f: F) -> Option<R>
72 where
73 F: FnOnce(&mut ActiveAgent) -> R,
74 {
75 let mut agents = self.agents.write().ok()?;
76 agents.first_mut().map(f)
77 }
78
79 pub fn push_agent(&self, agent: ActiveAgent) {
81 if let Ok(mut agents) = self.agents.write() {
82 agents.push(agent);
83 }
84 }
85
86 pub fn pop_agent(&self) -> Option<ActiveAgent> {
88 let mut agents = self.agents.write().ok()?;
89 if agents.len() > 1 {
90 agents.pop()
91 } else {
92 None
93 }
94 }
95
96 pub fn stack_depth(&self) -> usize {
98 self.agents.read().map(|a| a.len()).unwrap_or(0)
99 }
100
101 pub fn reset_to_agent(&self, agent: Arc<dyn Agent>) {
103 if let Ok(mut agents) = self.agents.write() {
104 agents.clear();
105 agents.push(ActiveAgent::new(agent));
106 }
107 }
108
109 pub fn catalog(&self) -> &Arc<AgentCatalog> {
111 &self.catalog
112 }
113
114 pub fn with_agents<F, R>(&self, f: F) -> Option<R>
116 where
117 F: FnOnce(&[ActiveAgent]) -> R,
118 {
119 let agents = self.agents.read().ok()?;
120 Some(f(&agents))
121 }
122}
123
124fn agent_level(agent: &str) -> u8 {
132 match agent {
133 "tycode" => 0,
134 "coordinator" => 1,
135 "coder" => 2,
136 "context" | "debugger" | "planner" | "review" => 3,
138 _ => 3,
140 }
141}
142
143pub fn allowed_agents_for(agent: &str) -> HashSet<String> {
146 let level = agent_level(agent);
147
148 let all_agents = [
150 ("coordinator", 1),
151 ("coder", 2),
152 ("context", 3),
153 ("debugger", 3),
154 ("planner", 3),
155 ("review", 3),
156 ];
157
158 all_agents
159 .into_iter()
160 .filter(|(_, agent_level)| *agent_level > level)
161 .map(|(name, _)| name.to_string())
162 .collect()
163}
164
165impl Module for SpawnModule {
166 fn prompt_components(&self) -> Vec<Arc<dyn PromptComponent>> {
167 vec![]
168 }
169
170 fn context_components(&self) -> Vec<Arc<dyn ContextComponent>> {
171 vec![]
172 }
173
174 fn tools(&self) -> Vec<Arc<dyn ToolExecutor>> {
175 let current = self.current_agent_name().unwrap_or_default();
176 let allowed = allowed_agents_for(¤t);
177
178 let mut tools: Vec<Arc<dyn ToolExecutor>> = vec![Arc::new(CompleteTask)];
179
180 if !allowed.is_empty() {
181 tools.push(Arc::new(SpawnAgent::new(
182 self.catalog.clone(),
183 allowed,
184 current,
185 )));
186 }
187
188 tools
189 }
190}