Explanation
Context and compaction¶
A model reads a bounded window of tokens, and a working chat outgrows it. Nawa's answer is to measure the window exactly and to maintain the conversation inside it openly, rather than to let a provider cut it silently. This page explains the accounting, the maintenance, and the rules that came from watching them fail.
Tokens, never characters¶
Model context budgets are in tokens; document extraction controls explicitly labelled in characters are separate. Before each request the engine materialises the request the provider will receive — messages, tools, native reasoning state, images — and counts it: with the model's own tokenizer where one is bundled, with a provider's same-origin counting endpoint where it declares one, and with a conservative local estimate otherwise. An estimate is marked as one (the gauge shows ~), reserves headroom for what it cannot measure, and cannot certify that a request fits; a null or negative count is not zero.
The window itself is measured, not assumed: the capability probe at setup time reads what the deployment declares, and a provider's report of how many prompt tokens it actually processed corrects the record in both directions — a silent truncation clamps the window down, a successfully processed larger prompt raises a false clamp back up.
One maintainer, two thresholds¶
One function, fitRequestContext, owns context maintenance for the lead, the coder and every other agent. It compares the measured request with the shared budget and, when the request crosses the trigger (Settings → LLM → Advanced → Compact context at (%), 80% by default, lowered by a known output requirement), rebuilds a smaller candidate and accepts it only after counting it again in full. The destination (50%) is where a rebuilt conversation lands; it is not a second trigger, so a valid cached checkpoint above it is not rebuilt prematurely.
Compaction summarises the older part of the conversation into a checkpoint in batches sized by tokens against the summariser's own budget, keeps the retained recent rounds verbatim, keeps every unresolved observation — a rejected finish, a failed test — verbatim wherever it sits, and keeps the original objective before the current work so nothing reads as a restart. Deduplication uses exact text; case-folding and whitespace normalisation merged distinct facts once, and the checkpoint schema was bumped to invalidate the results. An accepted reduction becomes the next active history, not a temporary view.
Each compaction is an event in the transcript — one start, one end, with the counts before and after — and the composer's gauge shows the same effective threshold the fitter used.
Evidence stays whole¶
A tool result too large for the request is not trimmed. It is stored complete in the workspace's evidence with its path, hash, byte size, source and completeness, and returned as a first page the model can read on; the coder's checkpoints refer to the same evidence rather than carrying it. Token pressure is never a reason to delete a fact.
Memory is not compaction¶
Durable memory — what Nawa should know about you across chats — is a separate store the model maintains with your approval on the Memory tab: it proposes a note during the turn, in your own words, and you save or dismiss it under the answer. Compaction never writes there, and a compacted checkpoint is never read as memory.
Why¶
The rules follow incidents recorded in the notes: a trace that looped for thirty-one minutes because an endpoint truncated the middle of a prompt without saying so; two coder compactions seventy-seven seconds apart because a checkpoint was allowed to grow to the trigger; a gauge that changed without a compaction because different measurements disagreed. Each fix moved a decision to the one owner that could make it with measured numbers.
Evidence: docs/compaction-unification-2026-09-14.md, docs/context-accounting-2026-09-14.md, docs/coder-checkpoint-headroom-2026-09-14.md, docs/context-growth-2026-09-14.md.