claude_agent_sdk/subagents/types.rs
1//! Type definitions for Subagent system
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// A subagent - a specialized Claude instance with specific capabilities
7///
8/// # Example
9///
10/// ```
11/// use claude_agent_sdk::subagents::Subagent;
12///
13/// let subagent = Subagent {
14/// name: "code-reviewer".to_string(),
15/// description: "Expert code reviewer".to_string(),
16/// instructions: "Review code for bugs and best practices".to_string(),
17/// allowed_tools: vec!["Read".to_string(), "Grep".to_string()],
18/// max_turns: Some(5),
19/// model: Some("claude-sonnet-4".to_string()),
20/// };
21/// ```
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Subagent {
24 /// Unique name for this subagent
25 pub name: String,
26
27 /// Description of the subagent's purpose
28 pub description: String,
29
30 /// Specific instructions for the subagent
31 pub instructions: String,
32
33 /// Tools that this subagent is allowed to use
34 pub allowed_tools: Vec<String>,
35
36 /// Maximum number of turns (None = no limit)
37 pub max_turns: Option<u32>,
38
39 /// Model to use (None = use default)
40 pub model: Option<String>,
41}
42
43/// Configuration for multiple subagents
44///
45/// # Example
46///
47/// ```
48/// use claude_agent_sdk::subagents::{SubagentConfig, Subagent, DelegationStrategy};
49///
50/// let config = SubagentConfig {
51/// subagents: vec![
52/// Subagent {
53/// name: "reviewer".to_string(),
54/// description: "Code reviewer".to_string(),
55/// instructions: "Review code".to_string(),
56/// allowed_tools: vec!["Read".to_string()],
57/// max_turns: Some(5),
58/// model: None,
59/// },
60/// ],
61/// delegation_strategy: DelegationStrategy::Auto,
62/// };
63/// ```
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct SubagentConfig {
66 /// List of available subagents
67 pub subagents: Vec<Subagent>,
68
69 /// Strategy for delegating to subagents
70 pub delegation_strategy: DelegationStrategy,
71}
72
73impl SubagentConfig {
74 /// Create a new subagent configuration
75 ///
76 /// # Arguments
77 ///
78 /// * `delegation_strategy` - The delegation strategy to use
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// # use claude_agent_sdk::subagents::{DelegationStrategy, SubagentConfig};
84 /// let config = SubagentConfig::new(DelegationStrategy::Auto);
85 /// ```
86 pub fn new(delegation_strategy: DelegationStrategy) -> Self {
87 Self {
88 subagents: Vec::new(),
89 delegation_strategy,
90 }
91 }
92
93 /// Add a subagent to the configuration
94 ///
95 /// # Arguments
96 ///
97 /// * `subagent` - The subagent to add
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// # use claude_agent_sdk::subagents::{SubagentConfig, Subagent, DelegationStrategy};
103 /// # let mut config = SubagentConfig::new(DelegationStrategy::Auto);
104 /// let subagent = Subagent {
105 /// name: "agent".to_string(),
106 /// description: "Description".to_string(),
107 /// instructions: "Instructions".to_string(),
108 /// allowed_tools: vec![],
109 /// max_turns: None,
110 /// model: None,
111 /// };
112 /// config.add_subagent(subagent);
113 /// ```
114 pub fn add_subagent(&mut self, subagent: Subagent) {
115 self.subagents.push(subagent);
116 }
117
118 /// Get a subagent by name
119 ///
120 /// # Arguments
121 ///
122 /// * `name` - The name of the subagent to retrieve
123 ///
124 /// # Returns
125 ///
126 /// `Some(subagent)` if found, `None` otherwise
127 ///
128 /// # Example
129 ///
130 /// ```
131 /// # use claude_agent_sdk::subagents::{SubagentConfig, Subagent, DelegationStrategy};
132 /// # let mut config = SubagentConfig::new(DelegationStrategy::Auto);
133 /// # let subagent = Subagent {
134 /// # name: "agent".to_string(),
135 /// # description: "Description".to_string(),
136 /// # instructions: "Instructions".to_string(),
137 /// # allowed_tools: vec![],
138 /// # max_turns: None,
139 /// # model: None,
140 /// # };
141 /// # config.add_subagent(subagent);
142 /// if let Some(agent) = config.get_subagent("agent") {
143 /// println!("Found agent: {}", agent.name);
144 /// }
145 /// ```
146 pub fn get_subagent(&self, name: &str) -> Option<&Subagent> {
147 self.subagents.iter().find(|s| s.name == name)
148 }
149
150 /// Convert to a HashMap for efficient lookup
151 ///
152 /// # Returns
153 ///
154 /// A HashMap mapping subagent names to subagents
155 ///
156 /// # Example
157 ///
158 /// ```
159 /// # use claude_agent_sdk::subagents::{SubagentConfig, DelegationStrategy};
160 /// # let config = SubagentConfig::new(DelegationStrategy::Auto);
161 /// let map = config.to_map();
162 /// ```
163 pub fn to_map(&self) -> HashMap<String, Subagent> {
164 self.subagents
165 .iter()
166 .map(|s| (s.name.clone(), s.clone()))
167 .collect()
168 }
169}
170
171/// Strategy for delegating work to subagents
172///
173/// # Variants
174///
175/// * `Auto` - Claude automatically decides when to delegate
176/// * `Manual` - Requires explicit SubagentTool calls
177/// * `ToolCall` - Delegate through tool calls
178///
179/// # Example
180///
181/// ```
182/// use claude_agent_sdk::subagents::DelegationStrategy;
183///
184/// let strategy = DelegationStrategy::Auto;
185/// ```
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
187pub enum DelegationStrategy {
188 /// Claude automatically decides when to delegate to subagents
189 Auto,
190
191 /// Manual delegation requires explicit SubagentTool calls
192 Manual,
193
194 /// Delegation happens through tool calls
195 ToolCall,
196}
197
198/// Represents a single subagent execution call
199///
200/// # Example
201///
202/// ```
203/// use claude_agent_sdk::subagents::SubagentCall;
204///
205/// let call = SubagentCall {
206/// subagent_name: "reviewer".to_string(),
207/// input: "Review this code".to_string(),
208/// output: None,
209/// };
210/// ```
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct SubagentCall {
213 /// Name of the subagent to call
214 pub subagent_name: String,
215
216 /// Input to provide to the subagent
217 pub input: String,
218
219 /// Output from the subagent (None if not yet executed)
220 pub output: Option<String>,
221}
222
223impl SubagentCall {
224 /// Create a new subagent call
225 ///
226 /// # Arguments
227 ///
228 /// * `subagent_name` - Name of the subagent to call
229 /// * `input` - Input for the subagent
230 ///
231 /// # Example
232 ///
233 /// ```
234 /// # use claude_agent_sdk::subagents::SubagentCall;
235 /// let call = SubagentCall::new("reviewer", "Review this code");
236 /// ```
237 pub fn new(subagent_name: impl Into<String>, input: impl Into<String>) -> Self {
238 Self {
239 subagent_name: subagent_name.into(),
240 input: input.into(),
241 output: None,
242 }
243 }
244
245 /// Check if the call has been executed
246 ///
247 /// # Returns
248 ///
249 /// `true` if the call has an output, `false` otherwise
250 ///
251 /// # Example
252 ///
253 /// ```
254 /// # use claude_agent_sdk::subagents::SubagentCall;
255 /// let call = SubagentCall::new("agent", "input");
256 /// assert!(!call.is_executed());
257 /// ```
258 pub fn is_executed(&self) -> bool {
259 self.output.is_some()
260 }
261}
262
263/// Output from a subagent execution
264///
265/// # Example
266///
267/// ```
268/// use claude_agent_sdk::subagents::SubagentOutput;
269///
270/// let output = SubagentOutput {
271/// subagent_name: "reviewer".to_string(),
272/// messages: vec![],
273/// };
274/// ```
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct SubagentOutput {
277 /// Name of the subagent that produced this output
278 pub subagent_name: String,
279
280 /// Messages produced by the subagent
281 /// Note: This is a placeholder - in a full implementation,
282 /// this would contain actual Message types from the SDK
283 pub messages: Vec<serde_json::Value>,
284}
285
286/// Errors that can occur in subagent operations
287///
288/// # Variants
289///
290/// * `NotFound` - Subagent not found
291/// * `AlreadyExists` - Subagent with this name already exists
292/// * `ExecutionFailed` - Subagent execution failed
293/// * `InvalidInput` - Invalid input provided
294///
295/// # Example
296///
297/// ```
298/// use claude_agent_sdk::subagents::SubagentError;
299///
300/// let error = SubagentError::NotFound("my-agent".to_string());
301/// ```
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub enum SubagentError {
304 /// Subagent not found
305 NotFound(String),
306
307 /// Subagent with this name already exists
308 AlreadyExists(String),
309
310 /// Subagent execution failed
311 ExecutionFailed(String),
312
313 /// Invalid input provided
314 InvalidInput(String),
315}
316
317impl std::fmt::Display for SubagentError {
318 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319 match self {
320 SubagentError::NotFound(name) => write!(f, "Subagent not found: {}", name),
321 SubagentError::AlreadyExists(name) => write!(f, "Subagent already exists: {}", name),
322 SubagentError::ExecutionFailed(msg) => write!(f, "Execution failed: {}", msg),
323 SubagentError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
324 }
325 }
326}
327
328impl std::error::Error for SubagentError {}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_subagent_creation() {
336 let subagent = Subagent {
337 name: "test-agent".to_string(),
338 description: "Test".to_string(),
339 instructions: "Instructions".to_string(),
340 allowed_tools: vec!["Read".to_string()],
341 max_turns: Some(5),
342 model: Some("claude-sonnet-4".to_string()),
343 };
344
345 assert_eq!(subagent.name, "test-agent");
346 assert_eq!(subagent.max_turns, Some(5));
347 }
348
349 #[test]
350 fn test_subagent_config_new() {
351 let config = SubagentConfig::new(DelegationStrategy::Auto);
352 assert!(config.subagents.is_empty());
353 assert_eq!(config.delegation_strategy, DelegationStrategy::Auto);
354 }
355
356 #[test]
357 fn test_subagent_config_add() {
358 let mut config = SubagentConfig::new(DelegationStrategy::Manual);
359
360 let subagent = Subagent {
361 name: "agent".to_string(),
362 description: "Description".to_string(),
363 instructions: "Instructions".to_string(),
364 allowed_tools: vec![],
365 max_turns: None,
366 model: None,
367 };
368
369 config.add_subagent(subagent);
370 assert_eq!(config.subagents.len(), 1);
371 }
372
373 #[test]
374 fn test_subagent_config_get() {
375 let mut config = SubagentConfig::new(DelegationStrategy::Auto);
376
377 let subagent = Subagent {
378 name: "agent".to_string(),
379 description: "Description".to_string(),
380 instructions: "Instructions".to_string(),
381 allowed_tools: vec![],
382 max_turns: None,
383 model: None,
384 };
385
386 config.add_subagent(subagent);
387 assert!(config.get_subagent("agent").is_some());
388 assert!(config.get_subagent("nonexistent").is_none());
389 }
390
391 #[test]
392 fn test_subagent_config_to_map() {
393 let mut config = SubagentConfig::new(DelegationStrategy::ToolCall);
394
395 config.add_subagent(Subagent {
396 name: "agent1".to_string(),
397 description: "Agent 1".to_string(),
398 instructions: "Instructions 1".to_string(),
399 allowed_tools: vec![],
400 max_turns: None,
401 model: None,
402 });
403
404 config.add_subagent(Subagent {
405 name: "agent2".to_string(),
406 description: "Agent 2".to_string(),
407 instructions: "Instructions 2".to_string(),
408 allowed_tools: vec![],
409 max_turns: None,
410 model: None,
411 });
412
413 let map = config.to_map();
414 assert_eq!(map.len(), 2);
415 assert!(map.contains_key("agent1"));
416 assert!(map.contains_key("agent2"));
417 }
418
419 #[test]
420 fn test_delegation_strategy_equality() {
421 assert_eq!(DelegationStrategy::Auto, DelegationStrategy::Auto);
422 assert_ne!(DelegationStrategy::Auto, DelegationStrategy::Manual);
423 }
424
425 #[test]
426 fn test_subagent_call_new() {
427 let call = SubagentCall::new("agent", "input");
428 assert_eq!(call.subagent_name, "agent");
429 assert_eq!(call.input, "input");
430 assert!(!call.is_executed());
431 }
432
433 #[test]
434 fn test_subagent_call_executed() {
435 let mut call = SubagentCall::new("agent", "input");
436 assert!(!call.is_executed());
437
438 call.output = Some("output".to_string());
439 assert!(call.is_executed());
440 }
441
442 #[test]
443 fn test_subagent_error_display() {
444 let error = SubagentError::NotFound("agent".to_string());
445 assert_eq!(format!("{}", error), "Subagent not found: agent");
446
447 let error = SubagentError::AlreadyExists("agent".to_string());
448 assert_eq!(format!("{}", error), "Subagent already exists: agent");
449 }
450
451 #[test]
452 fn test_subagent_output() {
453 let output = SubagentOutput {
454 subagent_name: "agent".to_string(),
455 messages: vec![],
456 };
457
458 assert_eq!(output.subagent_name, "agent");
459 assert!(output.messages.is_empty());
460 }
461}