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}