use std::sync::Arc;
use agent_base::{AgentResult, ToolContext, TypedTool};
use agent_works::multi_agent::MultiAgentRuntime;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SpawnAgentArgs {
pub task_name: String,
pub message: String,
#[serde(default)]
pub agent_type: Option<String>,
#[serde(default)]
pub system_prompt: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub reasoning_effort: Option<String>,
#[serde(default)]
pub fork_history: Option<String>,
#[serde(default = "default_depth")]
pub depth: i32,
#[serde(default)]
pub full_permission: bool,
}
fn default_depth() -> i32 {
1
}
#[derive(Debug, Serialize)]
pub struct SpawnAgentOutput {
pub agent_path: String,
pub message: String,
}
pub struct SpawnAgentTool {
runtime: Arc<MultiAgentRuntime>,
}
impl SpawnAgentTool {
pub fn new(runtime: Arc<MultiAgentRuntime>) -> Self {
Self { runtime }
}
}
#[async_trait::async_trait]
impl TypedTool for SpawnAgentTool {
type Args = SpawnAgentArgs;
type Output = SpawnAgentOutput;
fn name(&self) -> &'static str {
"spawn_agent"
}
fn description(&self) -> &'static str {
"Spawn a new sub-agent to execute a task independently.\n\
The sub-agent runs concurrently and reports results via its mailbox.\n\
Use agent_type for preset roles, or system_prompt for custom instructions.\n\
Set full_permission=true only when the sub-agent may take dangerous\n\
actions without approval (default false = deny all)."
}
async fn call_typed(&self, args: Self::Args, ctx: &ToolContext) -> AgentResult<Self::Output> {
let system_prompt = args
.system_prompt
.or(args
.agent_type
.map(|t| format!("You are a {} specialist.", t)))
.unwrap_or_else(|| args.message.clone());
let depth = args.depth;
let tool_count = 0;
match self
.runtime
.spawn_child_with_history(
&args.task_name,
system_prompt,
depth,
tool_count,
args.full_permission,
args.fork_history,
&ctx.session_id,
)
.await
{
Ok(agent_path) => {
let _ = self
.runtime
.send_task(&agent_path, args.message.clone(), true);
Ok(SpawnAgentOutput {
agent_path,
message: "Agent spawned successfully".to_string(),
})
}
Err(e) => Ok(SpawnAgentOutput {
agent_path: String::new(),
message: format!("Failed to spawn agent: {}", e),
}),
}
}
}