agent_engine/tools/subagent/
status.rs1use super::super::{Tool, ToolContext};
8use crate::{Result, RuntimeError};
9use serde_json::{json, Value};
10
11pub struct SubagentStatusTool;
12
13#[async_trait::async_trait]
14impl Tool for SubagentStatusTool {
15 fn origin(&self) -> crate::tools::ToolOrigin {
16 crate::tools::ToolOrigin::Builtin
17 }
18
19 fn name(&self) -> &str {
20 "subagent_status"
21 }
22
23 fn description(&self) -> &str {
24 "Poll the current state of a reactive subagent. Returns status \
25 (running/completed/cancelled/timed_out/failed), the last 500 characters of output \
26 produced so far, elapsed time in seconds, and the number of tool calls \
27 made. If the status is 'failed', an 'error' field contains the failure reason. \
28 Non-blocking — returns immediately."
29 }
30
31 fn parameters(&self) -> Value {
32 json!({
33 "type": "object",
34 "properties": {
35 "handle_id": {
36 "type": "string",
37 "description": "Handle ID returned by subagent_start (e.g. \"sa_3\")."
38 }
39 },
40 "required": ["handle_id"]
41 })
42 }
43
44 async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
45 let handle_id = params["handle_id"]
46 .as_str()
47 .ok_or_else(|| RuntimeError::Tool("Missing 'handle_id' parameter".to_string()))?
48 .to_string();
49
50 let registry = ctx.capabilities.subagent_registry.as_ref().ok_or_else(|| {
53 RuntimeError::Tool("SubagentRegistry not available on this ToolContext".to_string())
54 })?;
55
56 let reg = registry.lock().unwrap();
57
58 let handle = reg.get(&handle_id).ok_or_else(|| {
59 RuntimeError::Tool(format!("No subagent found with handle_id '{}'", handle_id))
60 })?;
61
62 let full: String = handle.partial_output();
65 let agent_name = handle.agent_name.clone();
66 let status = handle.status();
67 let status_str = status.as_str().to_string();
68 let elapsed = handle.elapsed_secs();
69 let tool_count = handle.tool_log().len();
70 let model = handle.model.clone();
71 let terminal = handle.terminal_diagnostic();
72 let authorization = handle.authorization.clone();
73 let _ = handle;
74 drop(reg);
75
76 let char_count = full.chars().count();
77 let partial_output: String = if char_count > 500 {
78 full.chars().skip(char_count - 500).collect()
79 } else {
80 full
81 };
82
83 let output_length = partial_output.chars().count();
84 if let Some(orchestration) = &ctx.capabilities.orchestration {
85 orchestration
86 .poll(&handle_id, &format!("{}:{}", status_str, tool_count))
87 .map_err(RuntimeError::Tool)?;
88 }
89
90 let mut resp = json!({
91 "handle_id": handle_id,
92 "agent_name": agent_name,
93 "status": status_str,
94 "partial_output": partial_output,
95 "elapsed_secs": (elapsed * 10.0).round() / 10.0,
96 "tool_count": tool_count,
97 "model": model,
98 "terminal_cause": terminal,
99 "authorization": authorization,
100 "output_length": output_length
101 });
102 if let Some(reason) = status.failure_reason() {
103 resp["error"] = json!(reason);
104 }
105 Ok(resp.to_string())
106 }
107}