atman_runtime/tools/
sleep.rs1use 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}