Переглянути джерело

feat(tools): wire TaskRegistry into task tool dispatch

Replace all 6 task tool stubs (TaskCreate/Get/List/Stop/Update/Output)
with real TaskRegistry-backed implementations:

- TaskCreate: creates task in global registry, returns real task_id
- TaskGet: retrieves full task state (status, messages, timestamps)
- TaskList: lists all tasks with metadata
- TaskStop: transitions task to stopped state with validation
- TaskUpdate: appends user messages to task message history
- TaskOutput: returns accumulated task output

Global registry uses OnceLock<TaskRegistry> singleton per process.
All existing tests pass (37 tools, 149 runtime, 102 CLI).
Jobdori 2 місяців тому
батько
коміт
e8692e45c4
1 змінених файлів з 79 додано та 35 видалено
  1. 79 35
      rust/crates/tools/src/lib.rs

+ 79 - 35
rust/crates/tools/src/lib.rs

@@ -11,14 +11,22 @@ use api::{
 use plugins::PluginTool;
 use reqwest::blocking::Client;
 use runtime::{
-    edit_file, execute_bash, glob_search, grep_search, load_system_prompt, read_file, write_file,
-    ApiClient, ApiRequest, AssistantEvent, BashCommandInput, ContentBlock, ConversationMessage,
-    ConversationRuntime, GrepSearchInput, MessageRole, PermissionMode, PermissionPolicy,
-    PromptCacheEvent, RuntimeError, Session, ToolError, ToolExecutor,
+    edit_file, execute_bash, glob_search, grep_search, load_system_prompt, read_file,
+    task_registry::TaskRegistry, write_file, ApiClient, ApiRequest, AssistantEvent,
+    BashCommandInput, ContentBlock, ConversationMessage, ConversationRuntime, GrepSearchInput,
+    MessageRole, PermissionMode, PermissionPolicy, PromptCacheEvent, RuntimeError, Session,
+    ToolError, ToolExecutor,
 };
 use serde::{Deserialize, Serialize};
 use serde_json::{json, Value};
 
+/// Global task registry shared across tool invocations within a session.
+fn global_task_registry() -> &'static TaskRegistry {
+    use std::sync::OnceLock;
+    static REGISTRY: OnceLock<TaskRegistry> = OnceLock::new();
+    REGISTRY.get_or_init(TaskRegistry::new)
+}
+
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ToolManifestEntry {
     pub name: String,
@@ -905,60 +913,96 @@ fn run_ask_user_question(input: AskUserQuestionInput) -> Result<String, String>
 
 #[allow(clippy::needless_pass_by_value)]
 fn run_task_create(input: TaskCreateInput) -> Result<String, String> {
-    let secs = std::time::SystemTime::now()
-        .duration_since(std::time::UNIX_EPOCH)
-        .unwrap_or_default()
-        .as_secs();
-    let task_id = format!("task_{secs:08x}");
+    let registry = global_task_registry();
+    let task = registry.create(&input.prompt, input.description.as_deref());
     to_pretty_json(json!({
-        "task_id": task_id,
-        "status": "created",
-        "prompt": input.prompt,
-        "description": input.description
+        "task_id": task.task_id,
+        "status": task.status,
+        "prompt": task.prompt,
+        "description": task.description,
+        "created_at": task.created_at
     }))
 }
 
 #[allow(clippy::needless_pass_by_value)]
 fn run_task_get(input: TaskIdInput) -> Result<String, String> {
-    to_pretty_json(json!({
-        "task_id": input.task_id,
-        "status": "unknown",
-        "message": "Task runtime not yet implemented"
-    }))
+    let registry = global_task_registry();
+    match registry.get(&input.task_id) {
+        Some(task) => to_pretty_json(json!({
+            "task_id": task.task_id,
+            "status": task.status,
+            "prompt": task.prompt,
+            "description": task.description,
+            "created_at": task.created_at,
+            "updated_at": task.updated_at,
+            "messages": task.messages,
+            "team_id": task.team_id
+        })),
+        None => Err(format!("task not found: {}", input.task_id)),
+    }
 }
 
 fn run_task_list(_input: Value) -> Result<String, String> {
+    let registry = global_task_registry();
+    let tasks: Vec<_> = registry
+        .list(None)
+        .into_iter()
+        .map(|t| {
+            json!({
+                "task_id": t.task_id,
+                "status": t.status,
+                "prompt": t.prompt,
+                "description": t.description,
+                "created_at": t.created_at,
+                "updated_at": t.updated_at,
+                "team_id": t.team_id
+            })
+        })
+        .collect();
     to_pretty_json(json!({
-        "tasks": [],
-        "message": "No tasks found"
+        "tasks": tasks,
+        "count": tasks.len()
     }))
 }
 
 #[allow(clippy::needless_pass_by_value)]
 fn run_task_stop(input: TaskIdInput) -> Result<String, String> {
-    to_pretty_json(json!({
-        "task_id": input.task_id,
-        "status": "stopped",
-        "message": "Task stop requested"
-    }))
+    let registry = global_task_registry();
+    match registry.stop(&input.task_id) {
+        Ok(task) => to_pretty_json(json!({
+            "task_id": task.task_id,
+            "status": task.status,
+            "message": "Task stopped"
+        })),
+        Err(e) => Err(e),
+    }
 }
 
 #[allow(clippy::needless_pass_by_value)]
 fn run_task_update(input: TaskUpdateInput) -> Result<String, String> {
-    to_pretty_json(json!({
-        "task_id": input.task_id,
-        "status": "updated",
-        "message": input.message
-    }))
+    let registry = global_task_registry();
+    match registry.update(&input.task_id, &input.message) {
+        Ok(task) => to_pretty_json(json!({
+            "task_id": task.task_id,
+            "status": task.status,
+            "message_count": task.messages.len(),
+            "last_message": input.message
+        })),
+        Err(e) => Err(e),
+    }
 }
 
 #[allow(clippy::needless_pass_by_value)]
 fn run_task_output(input: TaskIdInput) -> Result<String, String> {
-    to_pretty_json(json!({
-        "task_id": input.task_id,
-        "output": "",
-        "message": "No output available"
-    }))
+    let registry = global_task_registry();
+    match registry.output(&input.task_id) {
+        Ok(output) => to_pretty_json(json!({
+            "task_id": input.task_id,
+            "output": output,
+            "has_output": !output.is_empty()
+        })),
+        Err(e) => Err(e),
+    }
 }
 
 #[allow(clippy::needless_pass_by_value)]