Compaction
Every turn re-invokes your handler with the full transcript — that’s what makes runners stateless and sessions portable. Conversations that outgrow the model’s context need compaction, and sectr handles it without breaking either property.
The mechanism
Section titled “The mechanism”Your code (or your framework) computes the compacted context — sectr journals
it as a TranscriptCompacted event carrying the full post-compaction
canonical messages. From that point the transcript is:
- everything after the snapshot, plus the snapshot itself standing in for everything before it.
The snapshot is closed context by definition: ctx.messages
positionally resets its fold at it — user turns derive from
INVOCATION_STARTEDs after the snapshot, assistant messages from the
post-snapshot MESSAGE_* events, and so on. Identity without snapshots
holds (the fold needs no state), so crash recovery and approval resumes
derive the same context deterministically from the journal.
Who computes it
Section titled “Who computes it”Frameworks with built-in memory management (langgraph) compute
compaction naturally; the adapter journals the result. Hand-rolled
agents can emit TranscriptCompacted themselves:
yield TranscriptCompacted(messages=compact(ctx.messages))After that point, ctx.messages in the next turn folds through the
snapshot — the conversation continues with the compacted history.
Guidance
Section titled “Guidance”- Compact at a turn boundary when you can (end of a turn, before the next user message): the snapshot then covers a complete exchange and no in-flight streaming needs special handling.
- The snapshot must be canonical messages — the same OpenAI-style shape
ctx.messagesproduces — because the fold restarts from it. - Don’t try to journal a diff: the event carries the full context so that replay, resume, and rehydration need no history beyond it.