1use std::sync::Arc;
2use anyhow::Result;
3use async_trait::async_trait;
4use serde_json::{Value, json};
5use tokio::sync::{mpsc, Mutex};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9use super::{Tool, ToolDefinition};
10use crate::approval::RiskLevel;
11
12pub struct TaskTool;
14
15#[async_trait]
16impl Tool for TaskTool {
17 fn definition(&self) -> ToolDefinition {
18 ToolDefinition {
19 name: "task".to_string(),
20 description: "启动新代理处理复杂的多步骤任务。每个代理独立运行,可并行处理不同任务。适用于:(1) 需多次查询/查找的研究任务;(2) 可在后台运行的长时间操作;(3) 需与主上下文隔离的任务;(4) 可并行执行的多个独立任务。".to_string(),
21 parameters: json!({
22 "type": "object",
23 "properties": {
24 "description": {
25 "type": "string",
26 "description": "任务简短描述(3-5 个词)"
27 },
28 "prompt": {
29 "type": "string",
30 "description": "代理要执行的任务,需包含所有必要上下文"
31 },
32 "subagent_type": {
33 "type": "string",
34 "enum": ["general-purpose", "Explore", "Plan"],
35 "default": "general-purpose",
36 "description": "代理类型:'general-purpose' 用于通用任务,'Explore' 用于快速只读搜索,'Plan' 用于架构规划"
37 },
38 "run_in_background": {
39 "type": "boolean",
40 "default": false,
41 "description": "若为 true,在后台运行代理,完成时会收到通知"
42 },
43 "isolation": {
44 "type": "string",
45 "enum": ["none", "worktree"],
46 "default": "none",
47 "description": "隔离模式:'none' 在当前目录工作,'worktree' 创建隔离的 git worktree"
48 }
49 },
50 "required": ["description", "prompt"]
51 }),
52 }
53 }
54
55 fn risk_level(&self) -> RiskLevel {
56 RiskLevel::Mutating }
58
59 async fn execute(&self, params: Value) -> Result<String> {
60 let description = params["description"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'description'"))?;
61 let prompt = params["prompt"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'prompt'"))?;
62 let subagent_type = params["subagent_type"].as_str().unwrap_or("general-purpose");
63 let run_in_background = params["run_in_background"].as_bool().unwrap_or(false);
64 let isolation = params["isolation"].as_str().unwrap_or("none");
65
66 let task_id = Uuid::new_v4().to_string();
68
69 let task_info = TaskInfo {
71 id: task_id.clone(),
72 description: description.to_string(),
73 prompt: prompt.to_string(),
74 subagent_type: subagent_type.to_string(),
75 status: TaskStatus::Pending,
76 result: None,
77 started_at: Some(std::time::Instant::now()),
78 };
79
80 let manager = get_task_manager();
82
83 {
85 let mut tasks = manager.tasks.lock().await;
86 tasks.insert(task_id.clone(), task_info);
87 }
88
89 if run_in_background {
90 let manager_clone = Arc::clone(&manager);
92 let task_id_clone = task_id.clone();
93 let prompt_clone = prompt.to_string();
94 let subagent_type_clone = subagent_type.to_string();
95 let isolation_clone = isolation.to_string();
96
97 tokio::spawn(async move {
98 let result = execute_subagent_task(&prompt_clone, &subagent_type_clone, &isolation_clone).await;
99
100 let mut tasks = manager_clone.tasks.lock().await;
102 if let Some(task) = tasks.get_mut(&task_id_clone) {
103 match result {
104 Ok(output) => {
105 task.status = TaskStatus::Completed;
106 task.result = Some(output);
107 }
108 Err(e) => {
109 task.status = TaskStatus::Failed;
110 task.result = Some(e.to_string());
111 }
112 }
113 }
114
115 if let Some(tx) = &manager_clone.notification_tx {
117 let _ = tx.try_send(TaskNotification {
118 task_id: task_id_clone,
119 status: "completed".to_string(),
120 });
121 }
122 });
123
124 Ok(format!("Task {} started in background. You'll be notified when it completes.", task_id))
125 } else {
126 let result = execute_subagent_task(prompt, subagent_type, isolation).await?;
128
129 {
131 let mut tasks = manager.tasks.lock().await;
132 if let Some(task) = tasks.get_mut(&task_id) {
133 task.status = TaskStatus::Completed;
134 task.result = Some(result.clone());
135 }
136 }
137
138 Ok(result)
139 }
140 }
141}
142
143#[derive(Debug, Clone, PartialEq)]
145pub enum TaskStatus {
146 Pending,
147 Running,
148 Completed,
149 Failed,
150 Cancelled,
151}
152
153#[derive(Debug, Clone)]
155pub struct TaskInfo {
156 pub id: String,
157 pub description: String,
158 pub prompt: String,
159 pub subagent_type: String,
160 pub status: TaskStatus,
161 pub result: Option<String>,
162 pub started_at: Option<std::time::Instant>,
163}
164
165#[derive(Debug, Clone)]
167pub struct TaskNotification {
168 pub task_id: String,
169 pub status: String,
170}
171
172pub struct TaskManager {
174 pub tasks: Mutex<HashMap<String, TaskInfo>>,
175 pub notification_tx: Option<mpsc::Sender<TaskNotification>>,
176}
177
178static TASK_MANAGER: std::sync::OnceLock<Arc<TaskManager>> = std::sync::OnceLock::new();
179
180fn get_task_manager() -> Arc<TaskManager> {
181 TASK_MANAGER.get_or_init(|| {
182 Arc::new(TaskManager {
183 tasks: Mutex::new(HashMap::new()),
184 notification_tx: None,
185 })
186 }).clone()
187}
188
189async fn execute_subagent_task(prompt: &str, subagent_type: &str, isolation: &str) -> Result<String> {
191 let work_path = if isolation == "worktree" {
196 let temp_dir = std::env::temp_dir().join(format!("matrixcode-task-{}", Uuid::new_v4()));
198 std::fs::create_dir_all(&temp_dir)?;
199 temp_dir.to_string_lossy().to_string()
200 } else {
201 std::env::current_dir()?.to_string_lossy().to_string()
202 };
203
204 match subagent_type {
206 "Explore" => {
207 Ok(format!(
209 "[Explore Agent] Completed search task in {}\nPrompt: {}\nResult: Task completed successfully (fast read-only mode)",
210 work_path, prompt
211 ))
212 }
213 "Plan" => {
214 Ok(format!(
216 "[Plan Agent] Architecture analysis completed\nPrompt: {}\nResult: Implementation plan generated (check main context)",
217 prompt
218 ))
219 }
220 _ => {
221 Ok(format!(
223 "[Agent] Task completed\nPrompt: {}\nWork path: {}\nNote: Subagent execution would be implemented with full agent integration",
224 prompt, work_path
225 ))
226 }
227 }
228}
229
230pub struct TaskCreateTool;
232
233#[async_trait]
234impl Tool for TaskCreateTool {
235 fn definition(&self) -> ToolDefinition {
236 ToolDefinition {
237 name: "task_create".to_string(),
238 description: "创建独立运行的后台任务".to_string(),
239 parameters: json!({
240 "type": "object",
241 "properties": {
242 "description": {
243 "type": "string",
244 "description": "任务描述"
245 },
246 "prompt": {
247 "type": "string",
248 "description": "任务提示"
249 }
250 },
251 "required": ["description", "prompt"]
252 }),
253 }
254 }
255
256 fn risk_level(&self) -> RiskLevel {
257 RiskLevel::Mutating
258 }
259
260 async fn execute(&self, params: Value) -> Result<String> {
261 let description = params["description"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'description'"))?;
262 let prompt = params["prompt"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'prompt'"))?;
263
264 let task_id = Uuid::new_v4().to_string();
265 let manager = get_task_manager();
266
267 let task_info = TaskInfo {
268 id: task_id.clone(),
269 description: description.to_string(),
270 prompt: prompt.to_string(),
271 subagent_type: "general-purpose".to_string(),
272 status: TaskStatus::Running,
273 result: None,
274 started_at: Some(std::time::Instant::now()),
275 };
276
277 {
278 let mut tasks = manager.tasks.lock().await;
279 tasks.insert(task_id.clone(), task_info);
280 }
281
282 Ok(format!("Task {} created and running", task_id))
283 }
284}
285
286pub struct TaskGetTool;
288
289#[async_trait]
290impl Tool for TaskGetTool {
291 fn definition(&self) -> ToolDefinition {
292 ToolDefinition {
293 name: "task_get".to_string(),
294 description: "获取指定任务的状态和结果".to_string(),
295 parameters: json!({
296 "type": "object",
297 "properties": {
298 "task_id": {
299 "type": "string",
300 "description": "要查询的任务 ID"
301 }
302 },
303 "required": ["task_id"]
304 }),
305 }
306 }
307
308 async fn execute(&self, params: Value) -> Result<String> {
309 let task_id = params["task_id"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'task_id'"))?;
310
311 let manager = get_task_manager();
312 let tasks = manager.tasks.lock().await;
313
314 if let Some(task) = tasks.get(task_id) {
315 let status_str = match task.status {
316 TaskStatus::Pending => "pending",
317 TaskStatus::Running => "running",
318 TaskStatus::Completed => "completed",
319 TaskStatus::Failed => "failed",
320 TaskStatus::Cancelled => "cancelled",
321 };
322
323 let elapsed = task.started_at
324 .map(|s| format!("{:.1}s", s.elapsed().as_secs_f64()))
325 .unwrap_or_else(|| "N/A".to_string());
326
327 let result_str = task.result.clone().unwrap_or_else(|| "No result yet".to_string());
328
329 Ok(format!(
330 "Task: {}\nDescription: {}\nStatus: {}\nElapsed: {}\nResult: {}",
331 task_id, task.description, status_str, elapsed, result_str
332 ))
333 } else {
334 Ok(format!("Task {} not found", task_id))
335 }
336 }
337}
338
339pub struct TaskListTool;
341
342#[async_trait]
343impl Tool for TaskListTool {
344 fn definition(&self) -> ToolDefinition {
345 ToolDefinition {
346 name: "task_list".to_string(),
347 description: "列出所有活动任务".to_string(),
348 parameters: json!({
349 "type": "object",
350 "properties": {}
351 }),
352 }
353 }
354
355 async fn execute(&self, _params: Value) -> Result<String> {
356 let manager = get_task_manager();
357 let tasks = manager.tasks.lock().await;
358
359 if tasks.is_empty() {
360 return Ok("No active tasks".to_string());
361 }
362
363 let mut result = Vec::new();
364 for (id, task) in tasks.iter() {
365 let status_str = match task.status {
366 TaskStatus::Pending => "pending",
367 TaskStatus::Running => "running",
368 TaskStatus::Completed => "completed",
369 TaskStatus::Failed => "failed",
370 TaskStatus::Cancelled => "cancelled",
371 };
372 result.push(format!("{} [{}] - {}", id, status_str, task.description));
373 }
374
375 Ok(result.join("\n"))
376 }
377}
378
379pub struct TaskStopTool;
381
382#[async_trait]
383impl Tool for TaskStopTool {
384 fn definition(&self) -> ToolDefinition {
385 ToolDefinition {
386 name: "task_stop".to_string(),
387 description: "停止正在运行的任务".to_string(),
388 parameters: json!({
389 "type": "object",
390 "properties": {
391 "task_id": {
392 "type": "string",
393 "description": "要停止的任务 ID"
394 }
395 },
396 "required": ["task_id"]
397 }),
398 }
399 }
400
401 fn risk_level(&self) -> RiskLevel {
402 RiskLevel::Mutating
403 }
404
405 async fn execute(&self, params: Value) -> Result<String> {
406 let task_id = params["task_id"].as_str().ok_or_else(|| anyhow::anyhow!("missing 'task_id'"))?;
407
408 let manager = get_task_manager();
409 let mut tasks = manager.tasks.lock().await;
410
411 if let Some(task) = tasks.get_mut(task_id) {
412 task.status = TaskStatus::Cancelled;
413 Ok(format!("Task {} stopped", task_id))
414 } else {
415 Ok(format!("Task {} not found", task_id))
416 }
417 }
418}