diff --git a/src/user/runner.rs b/src/user/runner.rs index a5ff4be..f925281 100644 --- a/src/user/runner.rs +++ b/src/user/runner.rs @@ -422,6 +422,7 @@ impl Agent { if let Some(ref tool_calls) = msg.tool_calls { if !tool_calls.is_empty() { self.push_message(msg.clone()); + for call in tool_calls { self.dispatch_tool_call(call, None, ui_tx, &mut ds) .await; @@ -499,53 +500,7 @@ impl Agent { return; } - // Handle memory tools — needs &mut self for node tracking - if call.function.name.starts_with("memory_") { - let result = tools::memory::dispatch(&call.function.name, &args, None); - let text = match &result { - Ok(s) => s.clone(), - Err(e) => format!("Error: {:#}", e), - }; - - // Disambiguate memory renders from other tool results - let memory_key = if result.is_ok() { - match call.function.name.as_str() { - "memory_render" => - args.get("key").and_then(|v| v.as_str()).map(String::from), - _ => None, - } - } else { - None - }; - - let output = tools::ToolOutput { - text, - is_yield: false, - images: Vec::new(), - model_switch: None, - dmn_pause: false, - }; - let _ = ui_tx.send(UiMessage::ToolResult { - name: call.function.name.clone(), - result: output.text.clone(), - }); - let _ = ui_tx.send(UiMessage::ToolFinished { id: call.id.clone() }); - let mut msg = Message::tool_result(&call.id, &output.text); - msg.stamp(); - if let Some(key) = memory_key { - self.push_entry(ConversationEntry::Memory { key, message: msg }); - } else { - self.push_entry(ConversationEntry::Message(msg)); - } - ds.had_tool_calls = true; - if output.text.starts_with("Error:") { - ds.tool_errors += 1; - } - - self.publish_context_state(); - return; - } - + // Dispatch through unified path let output = tools::dispatch(&call.function.name, &args, &self.process_tracker).await; @@ -555,7 +510,7 @@ impl Agent { ds.had_tool_calls = true; } if output.model_switch.is_some() { - ds.model_switch = output.model_switch; + ds.model_switch = output.model_switch.clone(); } if output.dmn_pause { ds.dmn_pause = true; @@ -570,11 +525,20 @@ impl Agent { }); let _ = ui_tx.send(UiMessage::ToolFinished { id: call.id.clone() }); + // Tag memory_render results for context deduplication + if call.function.name == "memory_render" && !output.text.starts_with("Error:") { + if let Some(key) = args.get("key").and_then(|v| v.as_str()) { + let mut msg = Message::tool_result(&call.id, &output.text); + msg.stamp(); + self.push_entry(ConversationEntry::Memory { key: key.to_string(), message: msg }); + self.publish_context_state(); + return; + } + } + self.push_message(Message::tool_result(&call.id, &output.text)); if !output.images.is_empty() { - // Only one live image in context at a time — age out any - // previous ones to avoid accumulating ~90KB+ per image. self.age_out_images(); self.push_message(Message::user_with_images( "Here is the image you requested:",