Clarify malformed tool call boundaries

Co-Authored-By: siGit Code <noreply@sigit.si>

Seto Elkahfi committed Sep 13, 2026 at 15:39 UTC 90e69e7d7f94e2cd2332b5a0961a751c81830cc2
1 file changed +14
src/inline_tool_calls.rs
+14
@@ -100,6 +100,12 @@ fn parse_xml_block(inner: &str, tools: &[ToolSpec]) -> Option<Recovered> {
100 let mut args = serde_json::Map::new();
101 let mut first_key = true;
102 while !rest.is_empty() {
103 + // The GLM alias is permitted exactly once, at the first key. A later
104 + // `<tool_call>` must reject the complete block rather than letting a
105 + // partial call escape recovery.
106 + if !first_key && rest.starts_with(XML_OPEN_TAG) {
107 + return None;
108 + }
109 let key_open = if first_key && malformed_first_key {
110 XML_OPEN_TAG
111 } else {
@@ -559,6 +565,14 @@ mod tests {
565 assert_eq!(args["cwd"], "/tmp");
566 }
567
568 + #[test]
569 + fn unknown_glm_tool_is_left_as_text() {
570 + let text = "<tool_call>not_offered<tool_call>command</arg_key><arg_value>pwd</arg_value></tool_call>";
571 + let (out, calls) = extract(text, &[run_command_spec()]);
572 + assert_eq!(out, text);
573 + assert!(calls.is_empty());
574 + }
575 +
576 #[test]
577 fn malformed_glm_marker_is_accepted_only_for_the_first_argument() {
578 let text = "<tool_call>run_command<arg_key>command</arg_key><arg_value>pwd</arg_value><tool_call>cwd</arg_key><arg_value>/tmp</arg_value></tool_call>";