agent_engine/tools/shell/
send.rs1use 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}