supercode-reduce 0.4.12

Optional lossless, reversible session reduction for Supercode
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
//! TR-6 (T16) — superseded-output eviction: deterministic same-tool/
//! canonicalized-args supersession behind [`super::ReductionKind::Superseded`].
//!
//! **The rule.** Two (or more) tool results in the same slice were produced
//! by the SAME tool, called with the SAME canonicalized arguments (a re-run
//! of `cargo test`, a re-run of `git diff`, a re-listed directory). Only the
//! chronologically LATEST such result is ever worth keeping in full — an
//! older run of the identical command is superseded information, exactly the
//! way an old failing `cargo test` run becomes noise once the fixed re-run
//! lands. Unlike [`super::ReductionKind::DuplicateOutput`] (TR-2), the
//! superseded and superseding contents are NOT required to be byte-identical
//! — that is the whole point (a stale failing run vs. a later passing one).
//!
//! **v1 canonicalization ([`canonical_key`]).** Deliberately narrow: EXACT
//! match after only the most trivial normalization (trim leading/trailing
//! whitespace; for a tool with a configured "command" field —
//! [`ReductionPolicy::supersede_command_fields`] — collapse internal
//! whitespace RUNS in that one field to a single space). No semantic
//! equivalence guessing of any kind: `ls -la` and `ls -al` list the same
//! information to a human but are DIFFERENT commands here, and stay that way
//! — TR-6.md's frozen spec is explicit that a false supersession (silently
//! hiding a result that was NOT actually superseded) is worse than a missed
//! one (an old result that could have been evicted stays visible instead).
//! Every detection here is a pure function of `(tool_name, arguments)` — no
//! disk I/O, no ambient state — so re-running it against the same transcript
//! always yields the same key for the same call (SPEC.md TR-6 dev/05:
//! determinism).
//!
//! [`ReductionPolicy::supersede_command_fields`]: super::ReductionPolicy::supersede_command_fields

use std::collections::{HashMap, HashSet};

use supercode_interchange::{ChatMessage, Role};

/// One non-read tool-result occurrence in a slice, paired back to the
/// assistant `tool_calls` entry that produced it (by `tool_call_id`, searched
/// BACKWARD from the result — the same direction [`super::detect_reads`]
/// searches in, for the same reason: the result is what [`super::project_messages`]
/// actually reduces, and the call is only consulted to learn what produced
/// it). `key` is this occurrence's [`canonical_key`] — two occurrences with
/// the same key are supersession candidates against each other.
#[derive(Debug, Clone)]
pub(crate) struct SupersedeCandidate {
    /// Index of the `Role::Tool` result in the slice this was detected
    /// against.
    pub(crate) index: usize,
    /// The tool name from the paired assistant call (for the stub summary).
    pub(crate) tool_name: String,
    /// This occurrence's canonicalization key ([`canonical_key`]).
    pub(crate) key: String,
}

/// Find every candidate tool-result occurrence in `msgs`: a `Role::Tool`
/// result (excluding `read_indices` — the read-family passes, A8/TR-3, own
/// that address space exclusively, with strictly richer path-aware
/// redundancy handling than a flat "same tool+args" key could express, the
/// same carve-out [`super::project_messages`]'s TR-2 pass makes) whose
/// `tool_call_id` resolves to a paired assistant `tool_calls` entry. A result
/// with no resolvable pairing (e.g. an imported transcript that never
/// recorded the originating call) is never a candidate — there is no
/// `(tool, args)` identity to key it by.
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
}

/// v1 supersession canonicalization key for one `(tool_name, arguments)` call
/// — see the module doc comment for the exact-match-after-trivial-
/// normalization contract this upholds. `arguments` is the tool call's raw
/// serialized JSON argument string (`FunctionCall::arguments`).
///
/// When `tool_name` has a configured command-bearing field
/// (`command_fields`, e.g. `bash`/`shell`/`exec_command` -> `"command"`) and
/// `arguments` parses as a JSON object with that field present as a string,
/// the key is built from the WHOLE-STRING-trimmed, internal-whitespace-
/// collapsed value of just that field — so `"cargo   test\n"` and
/// `"cargo test"` key identically, but `"cargo test"` and `"cargo test --lib"`
/// never do (no token-level or flag-level equivalence reasoning). For every
/// other tool (no configured field, or the field is absent/non-string/the
/// arguments don't parse as an object), the key falls back to the entire
/// `arguments` string, trimmed only — never whitespace-collapsed internally,
/// since a multi-argument JSON object's internal whitespace is not safely
/// collapsible the way one shell command line's is.
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());
    // NUL never appears in a tool name or a trimmed JSON/command string, so
    // this is a safe, unambiguous separator between the two halves of the key
    // (no tool name can ever collide with another tool name + a differently
    // split argument string).
    format!("{tool_name}\u{0}{normalized_args}")
}

/// Collapse every run of Unicode whitespace in `s` to a single ASCII space —
/// QUOTE-AWARE: whitespace runs are only ever collapsed OUTSIDE a `"..."` or
/// `'...'` shell literal. Whitespace (and everything else) INSIDE a quoted
/// literal is preserved byte-for-byte, because it is part of the literal
/// VALUE the shell would pass to the command, not inter-token separator
/// whitespace — collapsing it would make `echo "a  b"` and `echo "a b"`
/// (two textually DIFFERENT commands whose quoted arguments differ) key
/// identically, a false supersession (TR-6.md: worse than a missed one).
///
/// Quote handling follows POSIX shell lexing close enough for this narrow
/// purpose:
/// - a `'...'` (single-quoted) literal has NO escaping at all — a `\` inside
///   it is a literal backslash, and only a following `'` closes it;
/// - OUTSIDE single-quotes (i.e. both unquoted and inside `"..."`), a `\`
///   escapes the very next character: the pair is copied through verbatim
///   and, critically, an escaped quote character (`\"`) does NOT toggle
///   quote state — `"a\"b"` is one continuous double-quoted literal, not two
///   adjacent ones.
///
/// CONSERVATIVE FALLBACK: if `s` ends still inside an open quote (unbalanced
/// — e.g. a truncated/malformed capture), the lex is ambiguous about which
/// bytes are "inside a literal" at all, so this returns `s` completely
/// UNCHANGED (trim was already applied by the caller before this is called;
/// no internal collapsing is attempted) rather than guess — an ambiguous
/// command must never risk being falsely equated with another.
///
/// Pure, allocation-only — never touches anything outside `s` itself.
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 => {
                // POSIX: no escaping inside single quotes; only a closing
                // `'` ends the literal.
                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);
                    }
                    // An escaped char (including an escaped `"`) never
                    // toggles quote state.
                } else if c == '"' {
                    out.push(c);
                    quote = Quote::None;
                    last_was_space = false;
                } else {
                    // Verbatim: whitespace inside a quoted literal is part
                    // of the value, never collapsed.
                    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 {
        // Unbalanced quote: the lex is ambiguous end-to-end. Conservative
        // fallback — trim-only (already done by the caller), no internal
        // collapse at all — so an ambiguous command can never be falsely
        // equated with a differently-spaced variant.
        s.to_string()
    }
}

/// The built-in default for [`ReductionPolicy::supersede_command_fields`]:
/// the same command-bearing tool identities T30/TR-4's
/// [`super::normalize::NORMALIZE_TOOLS`] normalizes (`bash`, `shell`,
/// `exec_command`), each keyed to their shared `"command"` argument field —
/// the one argument whose value is a literal shell command line, the case
/// TR-6.md's frozen spec calls out by name (`git diff` vs `git diff --stat`,
/// `ls a/` vs `ls b/`).
///
/// [`ReductionPolicy::supersede_command_fields`]: super::ReductionPolicy::supersede_command_fields
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);

        // v1 makes no semantic-equivalence guess: flag-reordered variants of
        // the "same" command are deliberately treated as different.
        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);
    }

    // ---- quote-aware collapse: the false-supersession fix -----------------
    //
    // Whitespace INSIDE a quoted shell literal is part of the literal's
    // VALUE, not inter-token separator whitespace — it must never collapse,
    // or two genuinely different commands (differing only in how much
    // whitespace sits inside a quoted argument) would wrongly canonicalize
    // to the same key: a false supersession, which TR-6.md calls worse than
    // a missed one.

    #[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() {
        // The intended, unchanged behavior: whitespace OUTSIDE any quote
        // (plain token separators) still collapses to one space.
        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();
        // Still "inside" a quote at end-of-string: the lex is ambiguous, so
        // the conservative fallback is trim-only (no internal collapse) —
        // this must NOT equate with a differently-spaced, well-formed
        // command that happens to produce the same collapsed form.
        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"
        );

        // Trim-only fallback: leading/trailing whitespace around the whole
        // field is still trimmed (that happens before collapse_whitespace
        // ever runs), but nothing internal is touched, including whitespace
        // that would otherwise have been an unquoted separator run.
        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() {
        // Pin: the pre-existing test above must keep passing unchanged after
        // the quote-aware rewrite (no quotes involved at all here).
        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);
    }
}