Compaction survival
Long-running conversations eventually hit the context-window limit, and the usual escape — summarize and compact — silently discards whatever only existed in the window. This pattern makes compaction safe: checkpoint() persists the working context as a durable entry before you compact, and the fresh window seeds itself from memory. Use it any time an agent conversation can outlive one context window.
How it works
window fills → checkpoint() → compact → new window seeds from get_context() → continuecheckpoint(context_snapshot=..., label=..., metadata=...) writes a checkpoint entry to long-term memory — a durable snapshot of task state, independent of both the LLM window and the run's live working memory. The response returns checkpoint_id and a token_estimate for the snapshot. After compaction, the new window recovers the state two ways:
- Assembled:
get_context()includes acheckpointssection in its priority order, so a resume query brings the snapshot back alongside rules, lessons, and facts. - Targeted: fetch only checkpoints with
mode="sections",sections=["checkpoints"], or recall them directly withentry_types=["checkpoint"].
Don't confuse this with working memory's own lifecycle. Working-memory variables decay and expire automatically — that's the runtime pruning scratch state, not you preserving it (see Working memory vs long-term memory). A checkpoint is the deliberate act: you choose the moment and the content, and the snapshot survives runs.
checkpoint() | Working-memory decay | |
|---|---|---|
| Trigger | You call it (pre-compaction, risky transition, handoff) | TTL expiry, relevance pruning |
| Content | Whatever snapshot you pass | Live variables, verbatim |
| Lifetime | Durable — survives runs | The current task |
| Recovery | get_context() / recall() | None (or runtime graduation to LTM) |
Walkthrough
At Nimbus, Ari's escalation for Acme Corp's CSV-export incident reaches turn 80 and the window is full. Three attempted fixes, a pending approval from Rex, and the current hypothesis exist only in the conversation. Before compacting:
Nothing durable was lost: the compacted turns are gone from the window, but the task state, the pending approval, and the next step were re-derived from memory, not from a lossy summary alone.
Scoping reflection to "since the checkpoint"
The checkpoint also becomes a temporal anchor. The reflect request accepts a checkpoint_id so reflection considers only evidence created after that checkpoint — useful when the pre-compaction half of the run has already been reflected on. Rust forwards it as a typed field:
use mubit_sdk::ReflectOptions;
let lessons = client.reflect(ReflectOptions {
run_id: Some("nimbus:ari:esc-2201".into()),
checkpoint_id: Some(checkpoint_id),
..ReflectOptions::default()
}).await?;checkpoint_id is a wire-level field on the reflect request — the typed Python and JS reflect() helpers do not forward it. Use the low-level passthrough (client.advanced.reflect({ run_id, checkpoint_id }) in JS; in Python too from SDK v0.12.0) or, on Python 0.11.0 and earlier, send it directly over the transport (POST /v2/control/reflect with {"run_id": ..., "checkpoint_id": ...}).
When not to use it
Skip the ceremony when the task fits comfortably in one window, and don't checkpoint every turn — knowledge that is already durable as typed entries (facts, lessons, traces written along the way) needs no snapshot. Checkpoint at the moments that matter: before compaction, before a risky transition, before a handoff. For reversible what-if exploration, a core branch session is the better tool — see Sessions & branching.
Related
- Working memory vs long-term memory — the decay behavior checkpoints protect against
- The memory model — where the
checkpointentry type sits in the taxonomy - Context assembly — the budgeted seeding call in detail
- Sessions & branching — checkpoints vs reversible branch sessions