From 3467d345965bba0c3cf64870b3289991c5616c92 Mon Sep 17 00:00:00 2001 From: Chummy Date: Wed, 18 Feb 2026 10:04:13 +0800 Subject: [PATCH] fix(agent): avoid duplicate text in markdown tool_call fallback --- src/agent/loop_.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/agent/loop_.rs b/src/agent/loop_.rs index 0a01c34..54deb06 100644 --- a/src/agent/loop_.rs +++ b/src/agent/loop_.rs @@ -469,7 +469,6 @@ fn parse_tool_calls(response: &str) -> (String, Vec) { Regex::new(r"(?s)```tool[_-]?call\s*\n(.*?)(?:```||)") .unwrap() }); - let mut md_remaining = response; let mut md_text_parts: Vec = Vec::new(); let mut last_end = 0; @@ -494,9 +493,8 @@ fn parse_tool_calls(response: &str) -> (String, Vec) { md_text_parts.push(after.trim().to_string()); } text_parts = md_text_parts; - md_remaining = ""; + remaining = ""; } - let _ = md_remaining; // suppress unused warning } // SECURITY: We do NOT fall back to extracting arbitrary JSON from the response @@ -1666,6 +1664,46 @@ I will now call the tool with this payload: ); } + #[test] + fn parse_tool_calls_handles_markdown_tool_call_fence() { + let response = r#"I'll check that. +```tool_call +{"name": "shell", "arguments": {"command": "pwd"}} +``` +Done."#; + + let (text, calls) = parse_tool_calls(response); + assert_eq!(calls.len(), 1); + assert_eq!(calls[0].name, "shell"); + assert_eq!( + calls[0].arguments.get("command").unwrap().as_str().unwrap(), + "pwd" + ); + assert!(text.contains("I'll check that.")); + assert!(text.contains("Done.")); + assert!(!text.contains("```tool_call")); + } + + #[test] + fn parse_tool_calls_handles_markdown_tool_call_hybrid_close_tag() { + let response = r#"Preface +```tool-call +{"name": "shell", "arguments": {"command": "date"}} + +Tail"#; + + let (text, calls) = parse_tool_calls(response); + assert_eq!(calls.len(), 1); + assert_eq!(calls[0].name, "shell"); + assert_eq!( + calls[0].arguments.get("command").unwrap().as_str().unwrap(), + "date" + ); + assert!(text.contains("Preface")); + assert!(text.contains("Tail")); + assert!(!text.contains("```tool-call")); + } + #[test] fn parse_tool_calls_handles_toolcall_tag_alias() { let response = r#"