echo_agent/agent/subagent/
builder.rs1use std::path::PathBuf;
4
5use super::types::{ExecutionMode, SubagentDefinition, SubagentKind};
6
7pub struct SubagentBuilder {
29 definition: SubagentDefinition,
30}
31
32impl SubagentBuilder {
33 pub fn new(name: impl Into<String>) -> Self {
35 Self {
36 definition: SubagentDefinition {
37 name: name.into(),
38 description: String::new(),
39 kind: SubagentKind::BuiltIn,
40 execution_mode: ExecutionMode::Sync,
41 model: None,
42 system_prompt: None,
43 tool_filter: None,
44 max_iterations: None,
45 token_limit: None,
46 inherit_history: None,
47 inherit_memory: false,
48 timeout_secs: 0,
49 can_delegate: false,
50 tags: Vec::new(),
51 },
52 }
53 }
54
55 pub fn description(mut self, desc: impl Into<String>) -> Self {
57 self.definition.description = desc.into();
58 self
59 }
60
61 pub fn kind(mut self, kind: SubagentKind) -> Self {
63 self.definition.kind = kind;
64 self
65 }
66
67 pub fn custom(mut self, path: impl Into<PathBuf>) -> Self {
69 self.definition.kind = SubagentKind::Custom { path: path.into() };
70 self
71 }
72
73 pub fn plugin(mut self, source: impl Into<String>) -> Self {
75 self.definition.kind = SubagentKind::Plugin {
76 source: source.into(),
77 };
78 self
79 }
80
81 pub fn sync_mode(mut self) -> Self {
83 self.definition.execution_mode = ExecutionMode::Sync;
84 self
85 }
86
87 pub fn fork_mode(mut self) -> Self {
89 self.definition.execution_mode = ExecutionMode::Fork;
90 self.definition.inherit_history = Some(10);
91 self.definition.inherit_memory = true;
92 self
93 }
94
95 pub fn teammate_mode(mut self) -> Self {
97 self.definition.execution_mode = ExecutionMode::Teammate;
98 self
99 }
100
101 pub fn model(mut self, model: impl Into<String>) -> Self {
103 self.definition.model = Some(model.into());
104 self
105 }
106
107 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
109 self.definition.system_prompt = Some(prompt.into());
110 self
111 }
112
113 pub fn tools(mut self, tools: Vec<impl Into<String>>) -> Self {
115 self.definition.tool_filter = Some(tools.into_iter().map(Into::into).collect());
116 self
117 }
118
119 pub fn max_iterations(mut self, max: usize) -> Self {
121 self.definition.max_iterations = Some(max);
122 self
123 }
124
125 pub fn token_limit(mut self, limit: usize) -> Self {
127 self.definition.token_limit = Some(limit);
128 self
129 }
130
131 pub fn inherit_history(mut self, count: usize) -> Self {
133 self.definition.inherit_history = Some(count);
134 self
135 }
136
137 pub fn inherit_memory(mut self) -> Self {
139 self.definition.inherit_memory = true;
140 self
141 }
142
143 pub fn timeout(mut self, secs: u64) -> Self {
145 self.definition.timeout_secs = secs;
146 self
147 }
148
149 pub fn can_delegate(mut self) -> Self {
151 self.definition.can_delegate = true;
152 self
153 }
154
155 pub fn tag(mut self, tag: impl Into<String>) -> Self {
157 self.definition.tags.push(tag.into());
158 self
159 }
160
161 pub fn tags(mut self, tags: Vec<impl Into<String>>) -> Self {
163 self.definition
164 .tags
165 .extend(tags.into_iter().map(Into::into));
166 self
167 }
168
169 pub fn build(self) -> SubagentDefinition {
171 let mut def = self.definition;
173 if def.description.is_empty() {
174 def.description = format!("Subagent '{}'", def.name);
175 }
176 def
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_builder_sync() {
186 let def = SubagentBuilder::new("worker")
187 .description("Does work")
188 .sync_mode()
189 .timeout(60)
190 .build();
191
192 assert_eq!(def.name, "worker");
193 assert_eq!(def.execution_mode, ExecutionMode::Sync);
194 assert_eq!(def.timeout_secs, 60);
195 assert!(def.inherit_history.is_none());
196 }
197
198 #[test]
199 fn test_builder_fork() {
200 let def = SubagentBuilder::new("researcher")
201 .description("Researches")
202 .fork_mode()
203 .model("qwen3")
204 .system_prompt("You are a researcher")
205 .tools(vec!["search", "read"])
206 .inherit_history(10)
207 .timeout(120)
208 .tag("research")
209 .can_delegate()
210 .build();
211
212 assert_eq!(def.execution_mode, ExecutionMode::Fork);
213 assert_eq!(def.model.as_deref(), Some("qwen3"));
214 assert_eq!(def.inherit_history, Some(10));
215 assert!(def.can_delegate);
216 assert_eq!(def.tags, vec!["research"]);
217 assert_eq!(
218 def.tool_filter.as_deref(),
219 Some(["search".to_string(), "read".to_string()].as_slice())
220 );
221 }
222
223 #[test]
224 fn test_builder_teammate() {
225 let def = SubagentBuilder::new("tm").teammate_mode().build();
226
227 assert_eq!(def.execution_mode, ExecutionMode::Teammate);
228 assert!(def.inherit_history.is_none());
229 }
230
231 #[test]
232 fn test_builder_auto_description() {
233 let def = SubagentBuilder::new("auto").build();
234 assert!(def.description.contains("auto"));
235 }
236
237 #[test]
238 fn test_builder_custom_kind() {
239 let def = SubagentBuilder::new("custom")
240 .custom("/path/to/agent.md")
241 .build();
242 assert!(matches!(def.kind, SubagentKind::Custom { .. }));
243 }
244
245 #[test]
246 fn test_builder_plugin_kind() {
247 let def = SubagentBuilder::new("plugin")
248 .plugin("remote://registry")
249 .build();
250 assert!(matches!(def.kind, SubagentKind::Plugin { .. }));
251 }
252}