diff --git a/src/agent/dispatcher.rs b/src/agent/dispatcher.rs index d5fae9f06..ca3e9684d 100644 --- a/src/agent/dispatcher.rs +++ b/src/agent/dispatcher.rs @@ -128,7 +128,7 @@ impl ToolDispatcher for XmlToolDispatcher { ConversationMessage::Chat(ChatMessage::user(format!("[Tool results]\n{content}"))) } - fn prompt_instructions(&self, tools: &[Box]) -> String { + fn prompt_instructions(&self, _tools: &[Box]) -> String { let mut instructions = String::new(); instructions.push_str("## Tool Use Protocol\n\n"); instructions @@ -136,17 +136,6 @@ impl ToolDispatcher for XmlToolDispatcher { instructions.push_str( "```\n\n{\"name\": \"tool_name\", \"arguments\": {\"param\": \"value\"}}\n\n```\n\n", ); - instructions.push_str("### Available Tools\n\n"); - - for tool in tools { - let _ = writeln!( - instructions, - "- **{}**: {}\n Parameters: `{}`", - tool.name(), - tool.description(), - tool.parameters_schema() - ); - } instructions } diff --git a/src/agent/tests.rs b/src/agent/tests.rs index 6b36263a8..c94a5d0bd 100644 --- a/src/agent/tests.rs +++ b/src/agent/tests.rs @@ -1282,8 +1282,12 @@ fn xml_dispatcher_generates_tool_instructions() { assert!(instructions.contains("## Tool Use Protocol")); assert!(instructions.contains("")); - assert!(instructions.contains("echo")); - assert!(instructions.contains("Echoes the input")); + // Tool listing is handled by ToolsSection in prompt.rs, not by the + // dispatcher. prompt_instructions() must only emit the protocol envelope. + assert!( + !instructions.contains("echo"), + "dispatcher should not duplicate tool listing" + ); } #[test] diff --git a/src/memory/consolidation.rs b/src/memory/consolidation.rs index d57bbcc53..12bb4d5a5 100644 --- a/src/memory/consolidation.rs +++ b/src/memory/consolidation.rs @@ -45,10 +45,12 @@ pub async fn consolidate_turn( // Truncate very long turns to avoid wasting tokens on consolidation. // Use char-boundary-safe slicing to prevent panic on multi-byte UTF-8 (e.g. CJK text). let truncated = if turn_text.len() > 4000 { - let mut end = 4000; - while end > 0 && !turn_text.is_char_boundary(end) { - end -= 1; - } + let end = turn_text + .char_indices() + .map(|(i, _)| i) + .take_while(|&i| i <= 4000) + .last() + .unwrap_or(0); format!("{}…", &turn_text[..end]) } else { turn_text.clone() @@ -99,10 +101,12 @@ fn parse_consolidation_response(raw: &str, fallback_text: &str) -> Consolidation // Fallback: use truncated turn text as history entry. // Use char-boundary-safe slicing to prevent panic on multi-byte UTF-8. let summary = if fallback_text.len() > 200 { - let mut end = 200; - while end > 0 && !fallback_text.is_char_boundary(end) { - end -= 1; - } + let end = fallback_text + .char_indices() + .map(|(i, _)| i) + .take_while(|&i| i <= 200) + .last() + .unwrap_or(0); format!("{}…", &fallback_text[..end]) } else { fallback_text.to_string()