Skip to main content

atomr_agents_tool/
handoff.rs

1//! `HandoffTool` — built-in helper for multi-agent handoff patterns.
2//!
3//! Returns a `ToolReturn::Command(ToolControl::Handoff)` that downstream
4//! routers (supervisor / swarm / network / hierarchical) interpret to
5//! transfer control. Lives in `agents-tool::stdlib::handoff`.
6
7use async_trait::async_trait;
8use atomr_agents_core::{InvokeCtx, Result, ToolId, Value};
9
10use crate::descriptor::{ToolDescriptor, ToolSchema};
11use crate::tool_return::{RichTool, ToolControl, ToolReturn};
12
13pub struct HandoffTool {
14    pub default_target: String,
15    descriptor: ToolDescriptor,
16}
17
18impl HandoffTool {
19    pub fn new(default_target: impl Into<String>) -> Self {
20        let target = default_target.into();
21        Self {
22            descriptor: ToolDescriptor {
23                id: ToolId::from(format!("handoff_{target}")),
24                name: format!("handoff_to_{target}"),
25                description: format!("Hand off control to the {target} agent."),
26                schema: ToolSchema(serde_json::json!({
27                    "type": "object",
28                    "properties": {
29                        "target": {"type": "string"},
30                        "payload": {},
31                    }
32                })),
33            },
34            default_target: target,
35        }
36    }
37}
38
39#[async_trait]
40impl RichTool for HandoffTool {
41    fn descriptor(&self) -> &ToolDescriptor {
42        &self.descriptor
43    }
44    async fn invoke_rich(&self, args: Value, _ctx: &InvokeCtx) -> Result<ToolReturn> {
45        let target = args
46            .get("target")
47            .and_then(|v| v.as_str())
48            .map(|s| s.to_string())
49            .unwrap_or_else(|| self.default_target.clone());
50        let payload = args.get("payload").cloned().unwrap_or(Value::Null);
51        Ok(ToolReturn::Command(ToolControl::Handoff { target, payload }))
52    }
53}