1use super::*;
4use serde::Deserialize;
5
6pub struct SleepTool;
7
8#[async_trait]
9impl Tool for SleepTool {
10 fn name(&self) -> &str {
11 "Sleep"
12 }
13 fn description(&self) -> &str {
14 "Pause execution for the specified number of milliseconds."
15 }
16 fn permission_level(&self) -> PermissionLevel {
17 PermissionLevel::None
18 }
19
20 fn input_schema(&self) -> Value {
21 serde_json::json!({
22 "type": "object",
23 "properties": {
24 "duration_ms": { "type": "integer", "description": "Duration to sleep in milliseconds (max 60000)" }
25 },
26 "required": ["duration_ms"]
27 })
28 }
29
30 async fn execute(&self, input: Value, _ctx: &ToolContext) -> ToolResult {
31 #[derive(Deserialize)]
32 struct Input {
33 duration_ms: u64,
34 }
35
36 let input: Input = match serde_json::from_value(input) {
37 Ok(i) => i,
38 Err(e) => return ToolResult::error(format!("Invalid input: {}", e)),
39 };
40
41 let duration = input.duration_ms.min(60_000);
42 tokio::time::sleep(std::time::Duration::from_millis(duration)).await;
43 ToolResult::success(format!("Slept for {}ms", duration))
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use crate::permissions::AllowAll;
51 use std::sync::Arc;
52
53 fn test_ctx() -> ToolContext {
54 ToolContext {
55 working_dir: std::env::temp_dir(),
56 session_id: "test".into(),
57 permissions: Arc::new(AllowAll),
58 cost_tracker: Arc::new(CostTracker::new()),
59 mcp_manager: None,
60 extensions: Extensions::default(),
61 }
62 }
63
64 #[tokio::test]
65 async fn test_sleep_100ms() {
66 let tool = SleepTool;
67 let start = std::time::Instant::now();
68 let result = tool
69 .execute(serde_json::json!({"duration_ms": 100}), &test_ctx())
70 .await;
71 assert!(!result.is_error);
72 assert!(start.elapsed().as_millis() >= 90);
73 }
74
75 #[tokio::test]
76 async fn test_sleep_capped() {
77 let tool = SleepTool;
78 let result = tool
80 .execute(serde_json::json!({"duration_ms": 1}), &test_ctx())
81 .await;
82 assert!(result.content.contains("1ms"));
83 }
84}