I hit my API token limit mid-task on OpenClaw once. The context got wiped. Everything the agent knew — the objective, the decisions, the half-finished work — gone. I started from square one.

That's a bad afternoon. But it's also a bad architecture, and the two are related.

Agents burn context fast, and every long-running one eventually faces the same wall: the window fills up, and something has to give. There are memory-optimisation skills you can bolt on, but they're manual — you have to remember to invoke them, and by the time you remember, you're already over. I wanted the platform to handle it. Automatically. Before the wall.

Two problems, one answer: make the agent's memory the platform's job, and make every step of it visible.

What I built

Two hooks at the AgentRunner seam. prepareTurn() before the run, recordTurn() after — on success and failure. Between them:

  • Step trace — one event per Codex item, correlated by runId, with status, duration, and a redacted preview. When a Run fails, failingStepIndex names the step.
  • Compaction — when measured context crosses the trigger, the objective is preserved verbatim, recent turns are carried in full, the complete transcript is written to disk, and the thread restarts clean.
  • Crash accounting — a pending-turn watermark. If the process dies mid-turn, the next turn gets told "verify the workspace before acting; do not blindly repeat it." Never a replay, because replaying is how you duplicate a side effect.

State lives outside the container. The one crossing is a read-only mount, so the agent can read its own transcript and can't rewrite its own audit trail.

Challenges

My first design was fiction. I specified a function taking ChatMessage[]. Then I traced the seam and discovered nothing in the codebase can produce one — history lives inside Codex's own rollout. This platform does not own the conversation. I rewrote everything around the two levers that actually exist: prepend text to the next prompt, and decide whether that prompt starts a fresh thread.

My tests proved something false. I'd "proven" compaction could never loop, because usage collapses after a reset. True — in tests, which used a fake post-reset value of 40 tokens. Against a real model, Codex sends its own system prompt every turn, and that floor is ~11.8k tokens. Compaction can only ever help when

$$F < L \cdot p$$

(floor below limit × trigger). Below that, every reset lands right back over the line and the agent compacts forever. It now measures $F$ live, disarms, and says so out loud.

Then my redactor ate the evidence. I ran a live test: "Remember this token: ORDER-4471-ZULU." The redaction heuristic saw token: and helpfully replaced the entire objective with [REDACTED] — destroying the one string the checkpoint promises to keep verbatim. Now it's two-tier: precise matching on anything persisted, heuristics only on machine-generated output.

And my own UI lied to me. It inferred failure from step status, so a timed-out Run rendered as perfectly healthy. Fixed — it now reads the actual outcome and prints "run terminated."

What I learned

Every real bug came from trying to falsify my own claims — running against a live model instead of a mock, asking "what if the value is unknown?" instead of picking a default. Guessing a context window is worse than admitting you don't know one, so an unknown model now disables compaction and tells you why, rather than silently assuming 128k and overflowing at 32k.

Results

75 tests passing. Live against gpt-5.1: context hit 296,264 tokens, compaction folded 6 turns, the thread reset, and usage dropped to 26,010 — an $11\times$ reduction.

Then I asked it what token I'd given it at the very start.

ORDER-4471-ZULU.

Fresh thread. Nothing lost. Which is the whole point — the afternoon I lost my context is the afternoon that shouldn't happen to anyone.

Built With

Share this project:

Updates