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 name(&self) -> &str {
12        "shell_send"
13    }
14
15    fn description(&self) -> &str {
16        "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."
17    }
18
19    fn parameters(&self) -> Value {
20        json!({
21            "type": "object",
22            "properties": {
23                "session_id": {
24                    "type": "string",
25                    "description": "Session ID from shell_start"
26                },
27                "input": {
28                    "type": "string",
29                    "description": "Text to send to the shell. Use \\n for Enter, \\x03 for Ctrl-C, \\x04 for Ctrl-D"
30                },
31                "timeout_ms": {
32                    "type": "integer",
33                    "description": "Override readiness timeout for this send (ms)"
34                }
35            },
36            "required": ["session_id", "input"]
37        })
38    }
39
40    async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String> {
41        let mgr = ctx
42            .capabilities
43            .session_manager
44            .as_ref()
45            .ok_or_else(|| RuntimeError::Tool("Shell sessions not available".into()))?;
46
47        let session_id = params["session_id"]
48            .as_str()
49            .ok_or_else(|| RuntimeError::Tool("Missing session_id parameter".into()))?;
50        let input = params["input"]
51            .as_str()
52            .ok_or_else(|| RuntimeError::Tool("Missing input parameter".into()))?;
53        let timeout_ms = params["timeout_ms"].as_u64();
54
55        let result = mgr
56            .send_input(
57                session_id,
58                input,
59                timeout_ms,
60                ctx.channels.tx_delta.as_ref(),
61                ctx.limits.max_tool_buffer,
62            )
63            .await?;
64
65        if result.status == "active" {
66            Ok(result.output)
67        } else {
68            let mut out = result.output;
69            out.push_str(&format!("\n[{}]", result.status));
70            Ok(out)
71        }
72    }
73}