ai-agent-sdk 0.5.0

Idiomatic agent sdk inspired by the claude code source leak
Documentation
//! Agent tool - for creating and managing subagents.
//!
//! This tool allows the main agent to spawn specialized subagents
//! for complex multi-step tasks.

use crate::types::*;

/// Name of the agent tool
pub const AGENT_TOOL_NAME: &str = "Agent";

/// Agent tool implementation
pub struct AgentTool;

impl AgentTool {
    pub fn new() -> Self {
        Self
    }

    #[allow(dead_code)]
    fn input_schema(&self) -> ToolInputSchema {
        ToolInputSchema {
            schema_type: "object".to_string(),
            properties: serde_json::json!({
                "description": {
                    "type": "string",
                    "description": "A short description (3-5 words) summarizing what the agent will do"
                },
                "subagent_type": {
                    "type": "string",
                    "description": "The type of subagent to use (e.g., 'general-purpose', 'code-reviewer', 'test-runner'). If omitted, uses the general-purpose agent."
                },
                "prompt": {
                    "type": "string",
                    "description": "The task prompt for the subagent to execute"
                },
                "model": {
                    "type": "string",
                    "description": "Optional model override for this subagent"
                },
                "max_turns": {
                    "type": "number",
                    "description": "Maximum number of turns for this subagent (default: 10)"
                },
                "run_in_background": {
                    "type": "boolean",
                    "description": "Whether to run the agent in the background (default: false)"
                },
                "isolation": {
                    "type": "string",
                    "enum": ["worktree", "remote"],
                    "description": "Isolation mode: 'worktree' for git worktree, 'remote' for remote CCR"
                }
            }),
            required: Some(vec!["description".to_string(), "prompt".to_string()]),
        }
    }

    /// Execute the agent tool - spawn a subagent
    pub async fn execute(&self, input: serde_json::Value, _context: &ToolContext) -> Result<ToolResult, crate::error::AgentError> {
        let description = input["description"]
            .as_str()
            .ok_or_else(|| crate::error::AgentError::Tool("description is required".to_string()))?;

        let subagent_type = input["subagent_type"].as_str();
        let prompt = input["prompt"]
            .as_str()
            .ok_or_else(|| crate::error::AgentError::Tool("prompt is required".to_string()))?;

        let model = input["model"].as_str().map(|s| s.to_string());
        let max_turns = input["max_turns"].as_u64().unwrap_or(10) as u32;
        let run_in_background = input["run_in_background"].as_bool().unwrap_or(false);
        let isolation = input["isolation"].as_str();

        // Build subagent options
        let subagent_model = model.unwrap_or_else(|| "claude-sonnet-4-6".to_string());

        // Build response
        let mut response = format!("Launching subagent: {}\n", description);

        if let Some(t) = subagent_type {
            response.push_str(&format!("Type: {}\n", t));
        }

        response.push_str(&format!("Prompt: {}\n", prompt));
        response.push_str(&format!("Model: {}\n", subagent_model));
        response.push_str(&format!("Max turns: {}\n", max_turns));

        if run_in_background {
            response.push_str("Mode: background\n");
        }

        if let Some(iso) = isolation {
            response.push_str(&format!("Isolation: {}\n", iso));
        }

        response.push_str("\nNote: Full subagent implementation would spawn a new agent process here.");

        Ok(ToolResult {
            result_type: "text".to_string(),
            tool_use_id: "agent_tool".to_string(),
            content: response,
            is_error: Some(false),
        })
    }
}

impl Default for AgentTool {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_agent_tool_schema() {
        let tool = AgentTool::new();
        let schema = tool.input_schema();

        assert_eq!(schema.schema_type, "object");

        let props = &schema.properties;
        assert!(props.get("description").is_some());
        assert!(props.get("prompt").is_some());

        let required = schema.required.as_ref().unwrap();
        assert!(required.contains(&"description".to_string()));
        assert!(required.contains(&"prompt".to_string()));
    }
}