Skip to main content

Module rehydrate

Module rehydrate 

Source
Expand description

T12 — model-invocable rehydration/retrieval over the sidecar (SPEC.md TR-1): the two agent intrinsics expand_reduction and sidecar_search. “The missing half of A7”: until this landed, only the USER could recover reduced content mid-flight (C4 /expand); the model itself had no way to ask for it back, which is exactly what capped how aggressive every other reducer could safely be.

§The two resolution sources

Both functions here are pure resolvers — no filesystem I/O, no no session container required — over TWO canonical message slices:

  • minted_view: the messages the reductions in log were minted against (a live agent passes history[1..]; offline callers pass a loaded Session’s .messages). Every SidecarPtr hash is verified against THIS slice, because this is the slice super::project_messages hashed when it created the reduction.
  • recorded: optionally, the recorder’s full-fidelity recorded messages (the sidecar reloaded from disk). Defense in depth, since TR-12 (SPEC.md D6/A7 supersession, Agent::run_loop): for any session recorded under that gate — a recorder and a super::ReductionPolicy both installed, which is what actually triggers minting a reduction over history in the first place — Agent::cap_tool_output is off, so minted_view (history[1..]) already holds the same full bytes as recorded, and every upgrade below is a provable no-op (rc != minted fails, minted returned unchanged). The path still matters for two cases where history/the sidecar genuinely can diverge: (1) a legacy sidecar recorded before this gate existed, whose reductions were minted from an already-cap_tool_output-capped history (minted_view’s copy is a capped prefix + an honest cap notice, and the ONLY place the full bytes still exist is the recorded copy); (2) a policy-without-recorder agent (gate off because nothing durable backs the full bytes) that later gains a recorder. In either case, when recorded is provided and its copy of an addressed message matches the full supersession key — same index, same role, same tool_call_id (present on BOTH sides; only tool results are ever capped, and the id is unique per call), and the minted copy is a cap-notice-bearing prefix of the recorded copy — the recorded bytes are returned instead. The tool_call_id component is what makes the key exact rather than heuristic: after Agent::rewind_to truncates history (the sidecar is append-only), a re-run command can produce a same-index, same-role tool result sharing the old run’s entire kept prefix, but it always carries a fresh tool_call_id, so the old-timeline copy can never satisfy the key. Anything else falls back to the hash-verified minted copy, so a wrong-bytes substitution is structurally impossible: the result is always either the exact bytes the hash was minted from, or their verified same-call full-length superset.

§No new reductions, no sentinel text

This module deliberately mints no new Reduction records and never writes REDUCTION_SENTINEL text. An oversized expand_reduction result is just an ordinary tool result once the agent loop pushes it onto history — the EXISTING super::project_messages A7 pass re-truncates it (with a fresh, genuinely reversible stub, recorded in the log like any other reduction) once it ages out of ReductionPolicy::protect_last_n_tool_results on a later turn. That is what SPEC.md TR-1 dev/05 (“expand results are reduction-eligible”) tests, and it is also why TR-1’s “no leak-guard special case needed” holds: nothing in this module ever produces sentinel-bearing text, so a session that used these intrinsics exports through export_session’s existing, unconditional leak guard (A11) unmodified. byte_range is the proactive tool: a caller who slices a large reduction into sub-ReductionPolicy- cap chunks never needs the safety net at all.

Note that expanding does NOT remove the reduction from the log — the stub stays in the projected view (unchanged), and the expanded content arrives as a new tool result. That is correct and deliberate: the log entry must survive so the stub keeps resolving (for a later re-expand, for sidecar_search, and for C4’s offline tooling); contrast super::invert_one_messages, which really does splice the original back into a view and therefore removes the entry.

Structs§

ExpandOutcome
The result of a successful expand_reduction call.
SidecarSearchMatch
One match sidecar_search found.
SidecarSearchResult
The complete result of a sidecar_search call. Bounded by construction (SPEC.md TR-1 fix pass): at most MAX_MATCHES snippets, each at most SNIPPET_MAX_BYTES, and the whole serialized form at most MAX_RESULT_BYTES — a broad query over megabytes of hidden content can never blow the result back up to the size the reductions saved, and the truncation is honest, structured JSON (never a mid-structure byte chop).

Constants§

CAP_NOTICE_MARKER
Marker appended to a retained prefix when a runtime caps tool output.
REASONING_CONTENT_PART_TYPES
content_parts block "type" values that carry a reasoning payload as model-VISIBLE content (as opposed to the metadata-only keys above) — e.g. a future/foreign provider that echoes {"type":"thinking",...} or {"type":"reasoning",...} blocks back into a message’s content array for continuation. None of supercode’s own content_parts constructors (ChatMessage::user_with_images/tool_result_with_image) ever produce these types today, so this branch is defensive breadth against future/ foreign content rather than something exercised by supercode’s own native loop yet.
REASONING_METADATA_KEYS
ChatMessage::metadata keys that carry a source model’s private reasoning/thinking payload — populated today by Session’s foreign- format importers:

Functions§

expand_reduction
Resolve the expand_reduction(reduction_id, byte_range?) agent intrinsic (SPEC.md TR-1 dev/01): the exact original bytes behind reduction id in log, resolved against minted_view (hash-verified) with recorded preferred for cap-diverged content — see the module doc comment for the two-source contract. With byte_range = Some((start, end)), just that [start, end) slice (end clamped to total_bytes, char-boundary-safe).
filter_reasoning_artifacts
Mid-session model switch (§1.10, dep 8): strip every reasoning artifact out of history in place — both the metadata keys (REASONING_METADATA_KEYS) and any content_parts blocks whose "type" is in REASONING_CONTENT_PART_TYPES — so model-A’s reasoning never reaches model-B’s context on the next request built from this history. Returns the count of MESSAGES actually touched (had at least one metadata key removed and/or at least one content part removed) — Agent::switch_model records this in the persisted model_change entry (ModelChangeRecord::reasoning_artifacts_filtered).
reduction_total_bytes
Total byte length of the original content behind reduction id — what ExpandOutcome::total_bytes would report — without slicing anything. Used by the agent’s argument-validation error path so a malformed byte_range error can name the true size the caller is ranging over.
sidecar_search
Resolve the sidecar_search(query) agent intrinsic (SPEC.md TR-1 dev/04): substring/regex search over content CURRENTLY reduced out of the view — every Reduction in log, restricted to its hidden_span so a still-visible portion (e.g. ToolOutputTruncated’s kept prefix) is never matched. “The same string visible in the live view is not double-reported” is upheld entirely from the log’s own records — this still never inspects the live view: for most kinds the hidden span is a pure function of the reduction itself, and for ReductionKind::DuplicateOutput (TR-2, whose content is byte-identical to a canonical instance that is usually still fully visible) visibility is decided by whether any OTHER log record reduces the canonical’s address — no per-message record there and no enclosing TurnsCleared range means the canonical is fully visible, so the duplicate’s content is not hidden and search skips it (hidden_span returns None). See the module doc comment for the minted_view/recorded two-source contract.