Imagine we are still asking OpenClaw to revise a project checklist.
It started simply: read the file, organize a few tasks, and improve the wording. Then we inspected test output, debated two approaches, rejected one, and added a constraint: “Leave the database alone. Today, we are only editing documentation.” Several hours later, the conversation contains pages of logs, code, and discussion.
Then the interface says it is compacting the context.
We want the work to continue, of course. But a shorter conversation is not our main concern. Will it remember the instruction about the database? Will the rejected approach quietly return as the next thing to implement?
What context compaction needs to hand over is an unfinished task. Following OpenClaw’s implementation reveals at least three separate jobs: deciding when to reorganize, choosing which original messages to retain, and connecting the resulting state back to execution. An unclear boundary in any of them can leave the next turn continuing the wrong story.
This article uses the fixed OpenClaw v2026.5.22 commit and its Pi 0.75.4 dependency. The checklist is a hypothetical example. The local experiments use constructed messages, not a conversation with a real model.
The desk is filling up
Think of a model’s context window as a desk with limited space. User requests, tool results, and earlier answers are spread across it. The next generation needs room too.
Pi’s basic threshold check is short. With compaction enabled, it triggers when context usage exceeds the window size minus a reserve. The comparison is strictly greater than: with an illustrative window of 1,000 tokens and a reserve of 200, usage of 800 does not trigger it; 801 does. These are small experiment parameters chosen to make the boundary visible. Threshold implementation
Two budgets are easy to confuse. reserveTokens affects when to make room. keepRecentTokens influences how much recent original content the history partition should retain. Increasing the former moves the trigger earlier; increasing the latter tends to keep more recent material. They are different controls, and neither tells us exactly how long the summary will be.
Context size is not always measured precisely. The code uses valid model usage when available and estimates messages added afterward. Per-message estimates use a heuristic such as character count divided by four, with special handling for some content types. This is not a universal tokenizer, and it does not guarantee equal accuracy for Chinese, code, and images. OpenClaw adds other budget and safeguard behavior, so Pi’s basic formula is not the entire system’s only trigger. Usage and estimation

Estimation can still fail to prevent an overflow. Pi’s session layer handles that differently from ordinary threshold compaction. An overflow from the same model enters recovery, with one compact-and-retry attempt. Crossing the ordinary threshold does not automatically rerun the answer that just finished. It also avoids using old usage from before the compaction boundary, which would amount to checking the newly cleared desk against an outdated inventory. Recovery and repeated-trigger guards
That is a useful state-machine design: proactive maintenance and failure recovery can share a compaction capability while having different entry conditions and follow-up actions.
The two parameters also have different costs. Starting cleanup early can mean more frequent summarization calls, with their latency and expense. Keeping a large recent tail can leave the compacted context surprisingly large. In a turn that repeatedly reads files, increasing retention may not help because the newest tool output is precisely what occupies the space. Configuration should reflect the task rather than aim for one fixed ratio that fits every conversation.
Where can the history actually be cut?
Suppose the latest turn has four parts: the user asks for a checklist review, the assistant calls a file-reading tool, the tool returns a large result, and the assistant offers an initial assessment. We now need to retain a recent portion and summarize the rest.
The obvious approach is to keep the last few thousand tokens. But messages have structure. A tool result needs its corresponding call. Half a JSON object or a result with no visible origin can make the next request difficult to interpret.
Pi’s findCutPoint first identifies valid message boundaries, then walks backward from the newest entry while accumulating estimated sizes. Once it reaches the budget, it selects a valid cut at or after that position. Tool results are not valid starting points. Assistant messages can be, allowing their tool calls and the following results to remain together. Cut-point algorithm
The retention budget is therefore approximate. It does not promise to keep exactly that many tokens. In our constructed example, the budget landed inside a tool result, so the cut moved forward to the following assistant message. With a larger budget, it moved back to the assistant’s tool call, keeping the result with it. The algorithm respects usable structural boundaries.
Even a structurally valid cut can begin halfway through a task turn. The original user request may be before the cut, while the retained assistant message is still explaining it.
Pi records turnStartIndex for this case. It separates earlier history, the discarded prefix of the current turn, and the retained suffix. prepareCompaction exposes messagesToSummarize and turnPrefixMessages separately. The summary needs to explain both earlier work and why the current turn reached its present state. Preparation
In the checklist example, keeping “the second approach is unsuitable” is not enough if the second approach and the reason for rejecting it have disappeared. Handing over the turn prefix addresses that problem more directly than simply increasing the retention budget.
The algorithm mainly performs linear scans: collecting boundaries, accumulating sizes backward, and finding the turn’s starting point. It does not assign every message a semantic value and search for an optimal combination. This keeps preparation predictable and testable. Much of the judgment about information value falls to summarization, making summary quality a separate capability that needs its own evaluation.
The summarizer also has a context limit
At this point, it would be misleading to assume Pi always generates a summary that OpenClaw uses unchanged. Pi first emits a session_before_compact extension event. An extension can supply a compaction result or cancel the operation. OpenClaw’s safeguard implementation uses this interface for staged summarization, file-operation information, tool failures, and identifier preservation. A configured custom compaction provider also uses the relevant extension path. Extension interface · OpenClaw safeguard
There is a recursive-looking problem here: the conversation is already large, and the model responsible for summarizing it has a context limit of its own.
OpenClaw’s summarization helpers organize material into chunks and stages using message sizes and the available window, then merge the results. Consider splitMessagesByTokenShare. It distributes estimated tokens across chunks while also tracking the IDs of tool calls whose results are still outstanding. Reaching an average token share is not enough reason to split an unfinished tool exchange arbitrarily. Chunking and tool pairing · Staged summarization
This is a different layer from the earlier cut-point algorithm. That algorithm chooses which original messages survive into the next turn. This layer chooses which material goes into an individual summarization request. Both must respect message structure, but they are not one operation.
The summary’s intended content is concrete too. Pi’s prompt structure asks for goals, constraints, progress, decisions, and next actions. Later compactions can update an existing summary. OpenClaw adds instructions such as preserving identifiers exactly. For our task, “do not modify the database” and “two checklist items remain” matter more than a long error report that has already been resolved. Summary generation and updates · Identifier policy
These are instructions and safeguards, not proof of lossless compression. Multiple stages add model calls and opportunities to lose information in intermediate summaries. Requiring exact paths does not guarantee they survive every time. A constraint appearing in a summary also does not give the executor an independent permission boundary.
Putting the handover back into the next turn
Once the summary exists, execution still needs a definite continuation point. Alongside summary, Pi’s compaction result includes firstKeptEntryId: the entry from which original content should remain visible.
The session layer checks cancellation, writes a compaction entry with appendCompaction, rebuilds the model view through buildSessionContext, and assigns it to agent.state.messages. The rebuilt view contains the summary and retained recent content, with later messages following them. The summary and its continuation boundary become recoverable state rather than a temporary piece of prompt text assembled in memory. Persistence and reattachment · Session-context reconstruction

We checked this locally with the original Pi modules and a temporary directory: write six messages, append a compaction entry, then reopen the session. The new view contained the summary and designated tail, while all six original messages remained in the file. A shorter model view therefore does not imply deleted history. This experiment did not test power loss, concurrent writers, or summary quality from a real model.
OpenClaw also offers optional transcript rotation. After successful compaction, it creates a successor transcript, carries over necessary state and the unsummarized tail, and retains the old file. This differs again from pruning tool results temporarily before a request; pruning is not a persisted summary checkpoint. Compaction, rotation, and pruning
Why a manual cleanup can leave less behind
One branch can make real behavior look different from the architecture diagram: /compact.
In this version, OpenClaw attempts to strengthen the boundary after manual compaction. An explicitly configured agents.defaults.compaction.keepRecentTokens preserves the recent tail. Otherwise, when the latest entry is a compaction entry, its summary is nonempty, and summarizable messages exist before the retained tail, the boundary can be changed to the compaction entry itself. The rebuilt view then starts from the summary. Manual-boundary guards and implementation · Call site
“Compaction always retains the most recent 20,000 tokens” is therefore not a valid promise. We must distinguish Pi defaults from OpenClaw’s effective configuration, and automatic maintenance from an explicit checkpoint request. This concerns the original message tail; the summary itself may still contain recent material preserved by an extension.
If I were designing a similar system, I would make the trigger, boundary selection, summarizer, and persistence step independently replaceable. Trigger tests would cover thresholds and recovery limits. Boundary tests would cover tool exchanges and oversized turns. Summary evaluation would use fixed tasks to measure retained constraints and progress. Persistence tests would cover cancellation, reopening, and failure. Token reduction alone cannot stand in for all of those acceptance criteria.
The 26 local checks for this article cover budget boundaries, cut points, turn prefixes, file tracking, and session reconstruction. They also exercise the pure transformation used to rewrite a manual boundary. A complete Gateway run, live model calls, and summary-quality evaluation remain outside this reproduction.
Back at the checklist, a useful handover should tell the next turn that the file has been read, which approach was rejected, what remains unfinished, and why the database must stay untouched today. Continuing to produce text is only the beginning. Continuing with the right working state is what makes context compaction worth designing carefully.

