Skip to main content

project_messages

Function project_messages 

Source
pub fn project_messages(
    msgs: &[ChatMessage],
    policy: &ReductionPolicy,
    prior: &ReductionLog,
) -> (Vec<ChatMessage>, ReductionLog)
Expand description

Pure function of (msgs, policy, prior): the reduction engine’s actual body (SPEC.md A5, A7, A8, A9, A10, TR-2). A session adapter can delegate its canonical message slice here; a runtime agent loop calls this directly against history[1..] so a live agent can build the projected request view without needing a Session wrapper around its own history.

Deterministic and side-effect free: identical inputs produce byte-identical output; msgs is never mutated; nothing here touches the filesystem — including for A8: policy.read_freshness is precomputed data, populated by probe_read_freshness (the one place disk I/O happens) before this is ever called. Every reduction already recorded in prior reproduces verbatim (same id, same placeholder, byte-identical stub) — new reductions only ever target messages older than the protected tail, so the reduced prefix stays cache-stable across turns.

Pass order: TR-2 (ReductionKind::DuplicateOutput, content-hash dedup) runs FIRST, then TR-6 (ReductionKind::Superseded, same-tool/ canonicalized-args keep-latest), then T30/TR-4 (ReductionKind::OutputNormalized, ANSI/redraw collapse), then ReductionKind::ToolOutputTruncated (A7), then the read-family passes — ReductionKind::FileReadElided (A8) for an unchanged re-read, ReductionKind::FileReadDiffed (TR-3) for a changed one — then ReductionKind::ImageRedacted (A9), then TR-10 (ReductionKind::ToolInputElided, both the successful-call case and TR-6’s failed-call errored-input-pruning complement), then ReductionKind::TurnsCleared (A10) last (the one cardinality-changing pass). Each of TR-2, TR-6, T30/TR-4, and A7 claims a message’s index in reduced_this_run the moment it mints a reduction for it, and every pass after the first checks that set — so a single message is claimed by exactly one pass per project_messages call, never two.

TR-2 running before TR-6, T30/TR-4, and A7 means a byte-identical duplicate is deduped — the cheapest of the four reductions — rather than independently superseded, normalized, or truncated. TR-6 running before T30/TR-4 and A7 follows the identical reasoning one step further: a result about to be superseded down to one small stub never needs normalizing or truncating first either. See the TR-2 and TR-6 passes below for why this ordering must hold within a single call, not just “eventually” (in short: both verdicts are pure functions of msgs, so they are unaffected by running before or after T30/TR-4, but T30/TR-4 and A7 both read from view, which TR-2/TR-6 may have already stubbed — so TR-2 then TR-6 must go first, or their own claims could lose a race to a pass that mutates view ahead of them). T30/TR-4 running before A7 means a noisy bash/exec output is collapsed to its final rendered content BEFORE A7 ever measures it against tool_output_trigger_bytes — but T30/TR-4 only CLAIMS the message (taking it out of A7’s candidate pool) when its own normalized rendering already fits under tool_output_trigger_bytes; that rendering is what rides the wire, already bounded, so no truncation is needed on top of it. When the normalized rendering is STILL over the trigger — genuinely large, mostly distinct content, not just redraw noise — T30/TR-4 deliberately does not claim the message at all (raw, untouched) and lets it fall through to A7 below, which truncates the RAW bytes to tool_output_keep_bytes. Either way the wire payload for a terminal output is bounded by A7’s trigger — the P7 runaway-output safety net (SPEC.md/TR-12) is preserved for BOTH small-after-normalization and large-after-normalization outputs. TR-2 and TR-6 both never claim a read-type tool result (detect_reads), even a byte-identical or same-args re-read: the read-family passes own that address space exclusively, with strictly more information (path-aware freshness, a unified diff) than either TR-2’s flat “identical to msg #N” or TR-6’s flat “superseded by msg #N” stub could express; T30/TR-4 is likewise scoped to normalize::NORMALIZE_TOOLS tool identities, disjoint from READ_TOOLS, so it never contends with the read-family passes over the same index either.