use crate::{
core::{Agent, AgentRunTime, AgentRuntimeConfig},
sandbox::Sandbox,
tools::{ToolOutcome, ToolRegistry},
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SpawnAgentArgs {
task: String,
}
#[derive(Debug, Serialize)]
struct SpawnAgentOutput {
status: String,
child_session_id: String,
final_resp: Option<String>,
detail: Value,
}
pub fn register_spawn_agent(registry: &mut ToolRegistry, child_config: AgentRuntimeConfig) {
registry.register(
"spawn_agent",
"启动一个子 agent runtime 执行独立子任务。子 agent 使用独立会话历史,但共享父 agent 当前 sandbox,返回子会话 id 和最终结果",
false,
move |sandbox, args: SpawnAgentArgs| {
let child_config = child_config.clone();
async move { spawn_agent(child_config, sandbox, args).await }
},
);
}
async fn spawn_agent(
mut child_config: AgentRuntimeConfig,
parent_sandbox: Arc<dyn Sandbox>,
args: SpawnAgentArgs,
) -> Result<ToolOutcome, String> {
child_config.sandbox = parent_sandbox;
let runtime = AgentRunTime::from_config(child_config).map_err(|e| e.to_string())?;
let child_session_id = runtime.create_session().await.map_err(|e| e.to_string())?;
let ready = runtime
.submit(&child_session_id, args.task)
.await
.map_err(|e| e.to_string())?;
let outcome = runtime
.run(ready, CancellationToken::new())
.await
.map_err(|e| e.to_string())?;
let output = match outcome {
Agent::Success(ctx) => SpawnAgentOutput {
status: "success".to_string(),
child_session_id,
final_resp: ctx.final_resp,
detail: json!({}),
},
Agent::Fail(_, failure) => SpawnAgentOutput {
status: "failed".to_string(),
child_session_id,
final_resp: None,
detail: json!({
"kind": failure.kind.as_str(),
"message": failure.message,
}),
},
Agent::WaitingForUser(_, interaction) => SpawnAgentOutput {
status: "waiting_for_user".to_string(),
child_session_id,
final_resp: None,
detail: json!({
"interaction": interaction,
}),
},
Agent::Interrupted(_) => SpawnAgentOutput {
status: "interrupted".to_string(),
child_session_id,
final_resp: None,
detail: json!({}),
},
other => SpawnAgentOutput {
status: "stopped".to_string(),
child_session_id,
final_resp: None,
detail: json!({
"state": format!("{other:?}"),
}),
},
};
let value = serde_json::to_value(output).map_err(|e| e.to_string())?;
Ok(value.into())
}