PAF becomes saf/device (history kept), STATE.md dissolves into saf/state.md with the dated era archived, the substrate SAF moves up from souveraine, and every agreement points at saf/INDEX.md and nowhere else. one map, nothing to remember
142 lines
6.9 KiB
Markdown
142 lines
6.9 KiB
Markdown
# Replay
|
|
|
|
Compaction is what she lets go of. Replay is what she carries forward. They are
|
|
two halves of the same question, and replay is the one that decides whether
|
|
compaction means anything at all.
|
|
|
|
The code is `core::session::replay_messages`.
|
|
|
|
## One history
|
|
|
|
There is a single record: typed message blocks stored for a conversation. Her
|
|
turn, her subconscious a moment later, the TUI, and the Panel all read that
|
|
record. None of them holds a transcript of its own.
|
|
|
|
That is not tidiness. It follows from what the substrate already says: one
|
|
consciousness in two modes, and she is not the glass. A second projection is a
|
|
second opinion about what happened, and the two drift the moment one is
|
|
touched. There were three of them once. They disagreed about images before
|
|
anyone noticed.
|
|
|
|
One record does not mean one lossy encoding. A surface receives the blocks
|
|
verbatim and draws their types. A model provider receives the narrowest valid
|
|
wire projection of those blocks. Both are views of the same record; neither is
|
|
allowed to rewrite the record into its own authority.
|
|
|
|
## What crosses to a model
|
|
|
|
A stored message is made of blocks. The model projection preserves the
|
|
strongest valid form each block still has:
|
|
|
|
| stored shape | crosses as |
|
|
|---|---|
|
|
| text | assistant/user text |
|
|
| complete tool round | one assistant `tool_calls` message, then bound tool results |
|
|
| call with no result | native call plus an explicit did-not-complete result |
|
|
| result whose call is gone | `Result (name): output`, or `Error (name): output` prose |
|
|
| reasoning | omitted unless a provider-valid signed thinking block exists |
|
|
| image | native image, or a marker saying it was not seen |
|
|
|
|
Text crosses as speech. Tool calls and their results cross as a native tool
|
|
round when the pair exists; orphaned evidence degrades explicitly rather than
|
|
breaking the wire. Images cross natively when the model can see and otherwise
|
|
become an honest unseen marker.
|
|
|
|
Reasoning is different. It remains a typed `reasoning` block in persistence,
|
|
token accounting, compaction input, transcript APIs, and the Panel's
|
|
collapsible Thinking card. It is not assistant speech. Anthropic will only
|
|
accept replayed thinking with the original signature, which the generic
|
|
persisted block does not carry. The honest generic projection therefore omits
|
|
it. Flattening it into `[Reasoning]: ...` puts an internal trace into the
|
|
assistant's visible prose and teaches the next model to imitate the leak.
|
|
|
|
The projection matches exhaustively. Adding a block kind still fails review at
|
|
the owning matches rather than disappearing behind a wildcard.
|
|
|
|
Native tool rounds are what make a turn's own work available to the turn after
|
|
it. When tool blocks were dropped, everything she learned by *doing* survived
|
|
only as whatever she happened to write down in prose, and she would re-derive
|
|
findings she had already reached. It also left microcompact — whose entire job
|
|
is blurring old tool output — with nothing in the stored payload to blur.
|
|
|
|
Microcompact does not drop tools. It retains every call and replaces only the
|
|
bulk of older tool-result output with an explicit cleared marker, keeping the
|
|
recent results intact. The typed record is compacted first; replay projects the
|
|
result afterward.
|
|
|
|
## Current authority never replays
|
|
|
|
Identity memory says who an agent understands herself to be. It cannot say
|
|
which Unix principal is handling this request now. Every model request receives
|
|
a fresh runtime-principal system block described in
|
|
[agent principals](../identity/02-agent-principal.md): dedicated resident,
|
|
intentional borrowed-user mode, actual UID, expected account, node, and drift.
|
|
|
|
That block is synthesized from current process and admission facts after
|
|
history replay. It is not stored as assistant or user text, not committed to
|
|
the agent's memfs, and not an input to compaction. Resume recomputes it. A mode
|
|
switch recomputes it. Ani and Aster receive the same `annie` account fact;
|
|
Kitty, Gem, and Hal receive the explicit warning that they are borrowing
|
|
Casey's Unix authority.
|
|
|
|
This is the same invariant as reasoning: a typed system fact must not be
|
|
flattened into speech. A stale replay must never convince a borrowed mode that
|
|
it owns Casey's permissions, or convince an unadmitted resident that it has
|
|
the account it was meant to receive.
|
|
|
|
## Three properties that are load-bearing
|
|
|
|
All three were learned from failures, and all look like details until they
|
|
break.
|
|
|
|
**One declaration for one stored assistant round.** Replaying block by block
|
|
splits a turn that called several tools into adjacent assistant messages.
|
|
OpenAI-shaped providers reject that shape. All calls from the stored assistant
|
|
message remain on one wire message.
|
|
|
|
**A native call is always answered.** Each result follows the assistant tool
|
|
declaration with its original call id. If a turn died before a result landed,
|
|
replay synthesizes an explicit did-not-complete result. If compaction removed
|
|
the call but left its result, that result cannot carry a tool role honestly and
|
|
degrades to named success/error prose. No half-pair reaches a provider.
|
|
|
|
**Doing never becomes saying.** A tool call must not become assistant prose
|
|
such as `Tool use: bash(...)`. That falsifies the record and teaches the model
|
|
that writing the sentence is how the action is performed. Calls stay calls;
|
|
results stay bound evidence whenever the pair can still be made valid.
|
|
|
|
## Surface hydration
|
|
|
|
`GET /v1/conversations/:id/messages` returns the stored
|
|
`ConversationMessage`s verbatim. The Panel maps `text` to speech, `reasoning`
|
|
to `ThinkingCard`, and tool use/result blocks to `ToolCard`. Live SSE follows
|
|
the same vocabulary: `assistant_message`, `reasoning_message`, tool call, and
|
|
tool return remain distinct events. A resume must use this typed path, never
|
|
`replay_messages`, which is the model projection.
|
|
|
|
That separation makes the visible invariant precise: Reasoning and Thinking
|
|
are the same typed thing. They may be folded or hidden by a surface, and kept
|
|
for later audit or reflection, but they may not be relabelled as ordinary text.
|
|
|
|
## The model capability that varies
|
|
|
|
Whether the model can see changes the image projection. Images ride as native
|
|
multipart content, or degrade to a marker that says plainly they were not
|
|
visible. The caller supplies that one fact; nothing else about the generic
|
|
projection changes.
|
|
|
|
A degraded image is named as unseen rather than dropped. A model told it did
|
|
not look can say so. A model handed a stub shaped like success proceeds as
|
|
though it had looked.
|
|
|
|
## Open edges
|
|
|
|
Live tool rounds inside a turn and replayed completed rounds both use real
|
|
`tool_call_id` linkage. Their repair rules differ: live execution knows whether
|
|
a result is still coming, while replay must make an interrupted persisted round
|
|
valid and truthful before it crosses the wire.
|
|
|
|
Persisting provider-valid signed thinking would permit a native reasoning
|
|
projection for providers that support it. Until the record carries that proof,
|
|
reasoning stays available to surfaces and later internal readers but does not
|
|
cross as invented assistant speech.
|