use std::collections::{HashMap, HashSet};
use supercode_interchange::{ChatMessage, Role};
#[derive(Debug, Clone)]
pub(crate) struct SupersedeCandidate {
pub(crate) index: usize,
pub(crate) tool_name: String,
pub(crate) key: String,
}
pub(crate) fn detect(
msgs: &[ChatMessage],
read_indices: &HashSet<usize>,
command_fields: &HashMap<String, String>,
) -> Vec<SupersedeCandidate> {
let mut out = Vec::new();
for (i, msg) in msgs.iter().enumerate() {
if msg.role != Role::Tool || read_indices.contains(&i) {
continue;
}
let Some(call_id) = msg.tool_call_id.as_deref() else {
continue;
};
let Some((tool_name, arguments)) = msgs[..i].iter().rev().find_map(|m| {
if m.role != Role::Assistant {
return None;
}
m.tool_calls()
.iter()
.find(|c| c.id == call_id)
.map(|c| (c.function.name.clone(), c.function.arguments.clone()))
}) else {
continue;
};
let key = canonical_key(&tool_name, &arguments, command_fields);
out.push(SupersedeCandidate {
index: i,
tool_name,
key,
});
}
out
}
pub(crate) fn canonical_key(
tool_name: &str,
arguments: &str,
command_fields: &HashMap<String, String>,
) -> String {
let normalized_args = command_fields
.get(tool_name)
.and_then(|field| {
serde_json::from_str::<serde_json::Value>(arguments)
.ok()
.and_then(|v| v.get(field).and_then(|f| f.as_str()).map(str::to_string))
})
.map(|command| collapse_whitespace(command.trim()))
.unwrap_or_else(|| arguments.trim().to_string());
format!("{tool_name}\u{0}{normalized_args}")
}
fn collapse_whitespace(s: &str) -> String {
#[derive(PartialEq)]
enum Quote {
None,
Single,
Double,
}
let mut out = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
let mut quote = Quote::None;
let mut last_was_space = false;
while let Some(c) = chars.next() {
match quote {
Quote::Single => {
out.push(c);
if c == '\'' {
quote = Quote::None;
last_was_space = false;
}
}
Quote::Double => {
if c == '\\' {
out.push(c);
if let Some(next) = chars.next() {
out.push(next);
}
} else if c == '"' {
out.push(c);
quote = Quote::None;
last_was_space = false;
} else {
out.push(c);
}
}
Quote::None => {
if c == '\\' {
out.push(c);
if let Some(next) = chars.next() {
out.push(next);
}
last_was_space = false;
} else if c == '"' {
out.push(c);
quote = Quote::Double;
last_was_space = false;
} else if c == '\'' {
out.push(c);
quote = Quote::Single;
last_was_space = false;
} else if c.is_whitespace() {
if !last_was_space {
out.push(' ');
}
last_was_space = true;
} else {
out.push(c);
last_was_space = false;
}
}
}
}
if quote == Quote::None {
out
} else {
s.to_string()
}
}
pub(crate) fn default_command_fields() -> HashMap<String, String> {
let mut m = HashMap::new();
m.insert("bash".to_string(), "command".to_string());
m.insert("shell".to_string(), "command".to_string());
m.insert("exec_command".to_string(), "command".to_string());
m
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn command_tool_trims_and_collapses_internal_whitespace_only() {
let fields = default_command_fields();
let a = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
let b = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
assert_eq!(a, b, "internal whitespace runs must collapse identically");
let c = canonical_key("bash", r#"{"command":" cargo test\n"}"#, &fields);
assert_eq!(a, c, "leading/trailing whitespace must be trimmed");
}
#[test]
fn different_commands_never_share_a_key() {
let fields = default_command_fields();
let diff = canonical_key("bash", r#"{"command":"git diff"}"#, &fields);
let diff_stat = canonical_key("bash", r#"{"command":"git diff --stat"}"#, &fields);
assert_ne!(
diff, diff_stat,
"different arguments must never canonicalize to the same key"
);
let ls_a = canonical_key("bash", r#"{"command":"ls a/"}"#, &fields);
let ls_b = canonical_key("bash", r#"{"command":"ls b/"}"#, &fields);
assert_ne!(ls_a, ls_b);
let la = canonical_key("bash", r#"{"command":"ls -la"}"#, &fields);
let al = canonical_key("bash", r#"{"command":"ls -al"}"#, &fields);
assert_ne!(
la, al,
"v1 must never guess `ls -la` and `ls -al` are equivalent"
);
}
#[test]
fn non_command_tool_falls_back_to_whole_trimmed_arguments() {
let fields = default_command_fields();
let a = canonical_key("read_file", r#"{"path":"a.rs"}"#, &fields);
let b = canonical_key("read_file", r#" {"path":"a.rs"} "#, &fields);
assert_eq!(a, b, "outer whitespace around the arguments must trim");
let different = canonical_key("read_file", r#"{"path":"b.rs"}"#, &fields);
assert_ne!(a, different);
}
#[test]
fn different_tool_names_never_collide_even_with_the_same_arguments() {
let fields = default_command_fields();
let a = canonical_key("bash", r#"{"command":"x"}"#, &fields);
let b = canonical_key("shell", r#"{"command":"x"}"#, &fields);
assert_ne!(a, b);
}
#[test]
fn double_quoted_internal_whitespace_is_preserved_not_collapsed() {
let fields = default_command_fields();
let one_space = canonical_key("bash", r#"{"command":"echo \"a b\""}"#, &fields);
let two_spaces = canonical_key("bash", r#"{"command":"echo \"a b\""}"#, &fields);
assert_ne!(
one_space, two_spaces,
"whitespace inside a double-quoted literal must never collapse"
);
}
#[test]
fn single_quoted_internal_whitespace_is_preserved_not_collapsed() {
let fields = default_command_fields();
let one_space = canonical_key("bash", r#"{"command":"echo 'a b'"}"#, &fields);
let two_spaces = canonical_key("bash", r#"{"command":"echo 'a b'"}"#, &fields);
assert_ne!(
one_space, two_spaces,
"whitespace inside a single-quoted literal must never collapse"
);
}
#[test]
fn grep_quoted_pattern_internal_whitespace_is_preserved() {
let fields = default_command_fields();
let one_space = canonical_key("bash", r#"{"command":"grep \"foo bar\" f"}"#, &fields);
let two_spaces = canonical_key("bash", r#"{"command":"grep \"foo bar\" f"}"#, &fields);
assert_ne!(
one_space, two_spaces,
"a quoted grep pattern's internal whitespace must never collapse"
);
}
#[test]
fn unquoted_separator_whitespace_still_collapses() {
let fields = default_command_fields();
let a = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
let b = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
assert_eq!(
a, b,
"unquoted separator whitespace runs must still collapse"
);
}
#[test]
fn unbalanced_quote_falls_back_to_trim_only_never_falsely_equates() {
let fields = default_command_fields();
let unterminated = canonical_key("bash", r#"{"command":"echo \"unterminated"}"#, &fields);
let well_formed_collapsed =
canonical_key("bash", r#"{"command":"echo unterminated"}"#, &fields);
assert_ne!(
unterminated, well_formed_collapsed,
"an unbalanced-quote command must never be falsely equated with another command"
);
let a = canonical_key("bash", r#"{"command":" echo \"a b"}"#, &fields);
let b = canonical_key("bash", r#"{"command":"echo \"a b"}"#, &fields);
assert_eq!(
a, b,
"outer whitespace must still trim even under the unbalanced-quote fallback"
);
let differently_spaced = canonical_key("bash", r#"{"command":"echo \"a b"}"#, &fields);
assert_ne!(
a, differently_spaced,
"under the unbalanced-quote fallback, internal spacing differences must never be collapsed away"
);
}
#[test]
fn command_tool_trims_and_collapses_internal_whitespace_only_still_holds() {
let fields = default_command_fields();
let a = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
let b = canonical_key("bash", r#"{"command":"cargo test"}"#, &fields);
assert_eq!(a, b);
}
}