Skip to main content

atman_runtime/tools/
sleep.rs

1use std::time::Duration;
2
3use crate::error::RuntimeError;
4use crate::tool::{BoxFut, Tier, Tool, ToolArgs, ToolCtx, ToolResult};
5use crate::value::Value;
6
7pub struct Sleep;
8
9impl Tool for Sleep {
10    fn name(&self) -> &str {
11        "sleep"
12    }
13
14    fn tier(&self) -> Tier {
15        Tier::Zero
16    }
17
18    fn description(&self) -> Option<&str> {
19        Some(
20            "Pause the workflow for a specified duration. Does NOT sleep the system — only the workflow waits. Use after spawning a background command to give it time to start, then check output. Max 60000ms (60s).",
21        )
22    }
23
24    fn input_schema(&self) -> serde_json::Value {
25        serde_json::json!({
26            "type": "object",
27            "properties": {
28                "ms": {"type": "integer", "description": "Milliseconds to wait. Max 60000."}
29            },
30            "required": ["ms"]
31        })
32    }
33
34    fn call<'a>(&'a self, args: ToolArgs, _ctx: &'a ToolCtx) -> BoxFut<'a, ToolResult> {
35        Box::pin(async move {
36            let ms = extract_int(&args, "ms", 0)?.clamp(0, 60_000) as u64;
37            tokio::time::sleep(Duration::from_millis(ms)).await;
38            Ok(Value::Unit)
39        })
40    }
41}
42
43fn extract_int(args: &ToolArgs, name: &str, pos: usize) -> Result<i64, RuntimeError> {
44    let value = match args.named(name) {
45        Some(v) => v,
46        None => args.positional(pos)?,
47    };
48    match value {
49        Value::Int(n) => Ok(*n),
50        other => Err(RuntimeError::TypeMismatch {
51            expected: "integer".into(),
52            actual: other.kind_name().into(),
53        }),
54    }
55}