Context window
The window is the scarce resource, so it is planned rather than filled. Every turn is packed by a manager that counts what a request will really cost, demotes what can be recovered from disk, and never lets history vanish without saying so.
The budget
model_ctx is the ceiling. The packer does not aim at it — it aims at 75% of the budget minus a reserve, leaving room for the response and for a turn that arrives larger than expected.
internal/window/window.go records why it moved.
What counts as cost
Tool-call arguments are part of the request. For a whole-file write they dwarf the assistant's own text, which is often empty. Counting only message content let a 33K request look like 1K and sail past the budget into a hard exceeds the available context size from the engine. The packer counts content, tool names and arguments together.
Three zones
Green
The turn fits inside the usable budget. Nothing is modified.
Yellow — demotion
Over budget. Two moves, oldest first, until it fits:
- Tool output becomes a pointer. A bulky result is replaced by its event id:
[raw tool output demoted — full content at evt_000123 in the episodic log; re-run the tool if needed]. The text leaves the window; it does not leave the disk. - Replayed tool arguments are elided. A past assistant turn whose call carried a whole-file write does not need those arguments again, only the fact that the call happened. Anything over 200 tokens becomes
{"_elided":"arguments dropped from the window — see the episodic log"}.
Red — compaction
Still over budget after demotion. The oldest turns are dropped, keeping the system prompt and the last n turns. What replaces them is the important part.
The compaction briefing
A silent delete is the worst outcome: the model loses what it decided and why, with nothing in its place, so it re-opens settled questions and redoes finished work — and reads as disobedient rather than forgetful.
Dropped turns are replaced by a briefing assembled from the log, never written by the model. Asking a model to summarise what it just lost is circular. The brief carries what cannot be recovered by reading the workspace:
If nothing is known, the brief degrades honestly rather than inventing structure — an empty section beats a confident lie. When no brief is available at all, a plain marker is used instead:
[N earlier turns were compacted out of this window to stay under the
context limit. They are NOT lost — the full episodic log is on disk, and
files you already wrote are still in the workspace. If you need something
from before this point, re-read the files rather than assuming it never
happened.]
The report
Every build returns a Report, and the panel renders it live:
green, yellow or red.Ingress caps
Demotion is the second line of defence. The first is that every tool has an IngressCap — a maximum number of characters its output may contribute to the window at all. A grep that matches four thousand lines is truncated at the boundary rather than admitted and then demoted. The caps are listed on Tools.
One more thing the window buys
Cerveau sends enable_thinking: false on every request. A reasoning model left to its own devices will spend the entire budget inside <think> and return nothing — which is not a context-management problem so much as a context-annihilation one.