Skip to main content

agent_engine/tools/shell/
send.rs

1//! `shell_send` tool — send input to an active shell session.
2
3use crate::tools::{Tool, ToolContext};
4use crate::{Result, RuntimeError};
5use serde_json::{json, Value};
6
7pub struct ShellSendTool;
8
9#[async_trait::async_trait]
10impl Tool for ShellSendTool {
11    fn origin(&self) -> crate::tools::ToolOrigin {
12        crate::tools::ToolOrigin::Builtin
13    }
14
15    fn name(&self) -> &str {
16        "shell_send"
17    }
18
19    fn description(&self) -> &str {
20        "Send input to an active shell session. Returns the output produced after sending the input. The input is sent exactly as received after JSON parsing — a JSON string containing \\n will send an actual newline (Enter key). Use \\x03 for Ctrl-C, \\x04 for Ctrl-D."
21    }
22
23    fn parameters(&self) -> Value {
24        json!({
25            "type": "object",
26            "properties": {
27                "session_id": {
28                    "type": "string",
29                    "description": "Session ID from shell_start"
30                },
31                "input": {
32                    "type": "string",
33                    "description": "Text to send to the shell. Use \\n for Enter, \\x03 for Ctrl-C, \\x04 for Ctrl-D"
34                },
35                "timeout_ms": {
36                    "type": "integer",
37                    "description": "Override readiness timeout for this send (ms)"
38                }
39            },
40            "required": ["session_id", "input"]
41        })
42    }
43
44    async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
45        let mgr = ctx
46            .capabilities
47            .session_manager
48            .as_ref()
49            .ok_or_else(|| RuntimeError::Tool("Shell sessions not available".into()))?;
50
51        let session_id = params["session_id"]
52            .as_str()
53            .ok_or_else(|| RuntimeError::Tool("Missing session_id parameter".into()))?;
54        let input = params["input"]
55            .as_str()
56            .ok_or_else(|| RuntimeError::Tool("Missing input parameter".into()))?;
57        let timeout_ms = params["timeout_ms"].as_u64();
58
59        let result = mgr
60            .send_input(
61                session_id,
62                input,
63                timeout_ms,
64                ctx.channels.tx_delta.as_ref(),
65                ctx.limits.max_tool_buffer,
66            )
67            .await?;
68
69        if result.status == "active" {
70            Ok(result.output)
71        } else {
72            let mut out = result.output;
73            out.push_str(&format!("\n[{}]", result.status));
74            Ok(out)
75        }
76    }
77}