diff --git a/src/agent/tools/mod.rs b/src/agent/tools/mod.rs index cb0b65b..408ba34 100644 --- a/src/agent/tools/mod.rs +++ b/src/agent/tools/mod.rs @@ -158,81 +158,46 @@ pub fn truncate_output(mut s: String, max: usize) -> String { s } -/// Dispatch a tool call by name. -/// -/// Tries agent-specific tools first (control, vision), then -/// delegates to thought::dispatch for shared tools. -/// -/// Note: working_stack is handled in runner.rs before reaching this -/// function (it needs mutable context access). -/// Dispatch a tool call by name. Handles all tools: -/// agent-specific (control, vision), memory/journal, file/bash. +/// Dispatch a tool call by name through the registry. pub async fn dispatch( name: &str, args: &serde_json::Value, ) -> ToolOutput { - // Agent-specific tools - let rich_result = match name { - "pause" => Some(control::pause(args)), - "switch_model" => Some(control::switch_model(args)), - "yield_to_user" => Some(control::yield_to_user(args)), - "view_image" => Some(vision::view_image(args)), - _ => None, - }; - if let Some(result) = rich_result { - return result.unwrap_or_else(ToolOutput::error); - } + dispatch_with_agent(name, args, None).await +} - if let Some(output) = dispatch_shared(name, args, None).await { - return output; +/// Dispatch a tool call with optional agent context. +pub async fn dispatch_with_agent( + name: &str, + args: &serde_json::Value, + agent: Option>>, +) -> ToolOutput { + for tool in tools() { + if tool.def.function.name == name { + return match (tool.handler)(agent, args.clone()).await { + Ok(s) => ToolOutput::text(s), + Err(e) => ToolOutput::error(e), + }; + } } - ToolOutput::error(format!("Unknown tool: {}", name)) } -/// Dispatch shared tools (memory, file, bash). Used by both the -/// interactive agent and subconscious agents. Provenance tracks -/// which agent made the call for memory attribution. +/// Dispatch shared tools — used by subconscious agents. pub async fn dispatch_shared( name: &str, args: &serde_json::Value, - provenance: Option<&str>, + _provenance: Option<&str>, ) -> Option { - // Memory and journal tools - if name.starts_with("memory_") || name.starts_with("journal_") || name == "output" { - let result = memory::dispatch(name, args, provenance); - return Some(match result { - Ok(s) => ToolOutput::text(s), - Err(e) => ToolOutput::error(e), - }); + for tool in tools() { + if tool.def.function.name == name { + return Some(match (tool.handler)(None, args.clone()).await { + Ok(s) => ToolOutput::text(s), + Err(e) => ToolOutput::error(e), + }); + } } - - // Channel tools - if name.starts_with("channel_") { - let result = channels::dispatch(name, args).await; - return Some(match result { - Ok(s) => ToolOutput::text(s), - Err(e) => ToolOutput::error(e), - }); - } - - // File and execution tools - let result = match name { - "read_file" => read::read_file(args), - "write_file" => write::write_file(args), - "edit_file" => edit::edit_file(args), - "bash" => bash::run_bash(args).await, - "web_fetch" => web::web_fetch(args).await, - "web_search" => web::web_search(args).await, - "grep" => grep::grep(args), - "glob" => glob::glob_search(args), - _ => return None, - }; - - Some(match result { - Ok(s) => ToolOutput::text(s), - Err(e) => ToolOutput::error(e), - }) + None } /// Return all registered tools with definitions + handlers. diff --git a/src/claude/mcp-server.rs b/src/claude/mcp-server.rs index c4e90a4..fdcc1b7 100644 --- a/src/claude/mcp-server.rs +++ b/src/claude/mcp-server.rs @@ -68,34 +68,32 @@ fn notify(method: &str, params: Value) { // ── Tool definitions ──────────────────────────────────────────── fn tool_definitions() -> Vec { - use poc_memory::agent::tools; - - let all_defs = tools::memory::definitions().into_iter() - .chain(tools::channels::definitions()); - - all_defs.map(|td| json!({ - "name": td.function.name, - "description": td.function.description, - "inputSchema": td.function.parameters, - })).collect() + poc_memory::agent::tools::tools().into_iter() + .map(|t| json!({ + "name": t.def.function.name, + "description": t.def.function.description, + "inputSchema": t.def.function.parameters, + })) + .collect() } // ── Tool dispatch ─────────────────────────────────────────────── fn dispatch_tool(name: &str, args: &Value) -> Result { - use poc_memory::agent::tools; + let tools = poc_memory::agent::tools::tools(); + let tool = tools.iter().find(|t| t.def.function.name == name); + let Some(tool) = tool else { + return Err(format!("unknown tool: {name}")); + }; - if name.starts_with("memory_") || name.starts_with("journal_") || name == "output" { - return tools::memory::dispatch(name, args, None) - .map_err(|e| e.to_string()); - } - - if name.starts_with("channel_") { - return tools::channels::dispatch_blocking(name, args) - .map_err(|e| e.to_string()); - } - - Err(format!("unknown tool: {name}")) + // Run async handler on a blocking runtime + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .map_err(|e| e.to_string())?; + let local = tokio::task::LocalSet::new(); + local.block_on(&rt, (tool.handler)(None, args.clone())) + .map_err(|e| e.to_string()) } // ── Main loop ───────────────────────────────────────────────────