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 inlogwere minted against (a live agent passeshistory[1..]; offline callers pass a loadedSession’s.messages). EverySidecarPtrhash is verified against THIS slice, because this is the slicesuper::project_messageshashed 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 asuper::ReductionPolicyboth installed, which is what actually triggers minting a reduction overhistoryin the first place —Agent::cap_tool_outputis off, sominted_view(history[1..]) already holds the same full bytes asrecorded, and every upgrade below is a provable no-op (rc != mintedfails,mintedreturned unchanged). The path still matters for two cases wherehistory/the sidecar genuinely can diverge: (1) a legacy sidecar recorded before this gate existed, whose reductions were minted from an already-cap_tool_output-cappedhistory(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, whenrecordedis provided and its copy of an addressed message matches the full supersession key — same index, same role, sametool_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. Thetool_call_idcomponent is what makes the key exact rather than heuristic: afterAgent::rewind_totruncates 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 freshtool_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§
- Expand
Outcome - The result of a successful
expand_reductioncall. - Sidecar
Search Match - One match
sidecar_searchfound. - Sidecar
Search Result - The complete result of a
sidecar_searchcall. Bounded by construction (SPEC.md TR-1 fix pass): at mostMAX_MATCHESsnippets, each at mostSNIPPET_MAX_BYTES, and the whole serialized form at mostMAX_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_partsblock"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 owncontent_partsconstructors (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::metadatakeys that carry a source model’s private reasoning/thinking payload — populated today bySession’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 reductionidinlog, resolved againstminted_view(hash-verified) withrecordedpreferred for cap-diverged content — see the module doc comment for the two-source contract. Withbyte_range = Some((start, end)), just that[start, end)slice (end clamped tototal_bytes, char-boundary-safe). - filter_
reasoning_ artifacts - Mid-session model switch (§1.10, dep 8): strip every reasoning artifact
out of
historyin place — both the metadata keys (REASONING_METADATA_KEYS) and anycontent_partsblocks whose"type"is inREASONING_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_modelrecords this in the persistedmodel_changeentry (ModelChangeRecord::reasoning_artifacts_filtered). - reduction_
total_ bytes - Total byte length of the original content behind reduction
id— whatExpandOutcome::total_byteswould report — without slicing anything. Used by the agent’s argument-validation error path so a malformedbyte_rangeerror 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 — everyReductioninlog, restricted to itshidden_spanso 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 forReductionKind::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 enclosingTurnsClearedrange means the canonical is fully visible, so the duplicate’s content is not hidden and search skips it (hidden_spanreturnsNone). See the module doc comment for theminted_view/recordedtwo-source contract.