Symptom (live session, gpt-5.6-sol @ Extra high, v0.0.200)
codex api error: Your input exceeds the context window of this model. Please adjust your input and try again.
[gpt-5.6-sol · Extra high · codex · 95k/270k ctx (35% · compact@216k)]
The meter says 95k/270k, compaction is armed at 216k — yet the backend rejects for exceeding the window, and every retry fails the same way (the session is wedged until a manual /compact or /clear).
Root cause — the meter and the wire measure different things
stepResponses appends every output item to local history, including each turn's encrypted reasoning item (agent_steps.zig:42); buildBody replays them with store:false + include: [reasoning.encrypted_content].
- Within a turn, the WS delta transport chains with
previous_response_id; the server discards prior-turn reasoning from chained context, so usage.total_tokens tracks a pruned conversation. recordUsageResponses copies that number into last_context_tokens (agent_request.zig) — this is the 95k.
runTurn closes the WS per turn (agent.zig:299), so the first request of every turn is a full-history resend — local history, all accumulated reasoning items included. At Extra-high effort on a long session that true cost can be ~3x the chained number.
- The full resend is precisely the request the backend rejects — so no corrective usage ever arrives,
last_context_tokens stays at the stale chained value, the pre-turn compact check (mainloop.zig:705) and the ApiError recovery (mainloop.zig:654) both see 95k < 216k, and nothing compacts. Wedged.
Why codex CLI doesn't hit this
Its ContextManager.get_total_token_usage() doesn't trust the last server report alone: it adds an estimate for reasoning items the server accounting excluded (get_non_last_reasoning_items_tokens() when server_reasoning_included is false) plus items appended since the last report, and compaction gates on that corrected number. Confirmed against openai/codex codex-rs/core/src/context_manager/history.rs.
Fix (parity + belt-and-braces)
- Meter correction:
last_context_tokens = max(server total, full-input estimate) on the responses path — estimate = serialized bytes of the full input array / 4 via a counting discard writer (zero-alloc). Makes compact@80% fire before the wall even while WS deltas keep server totals small.
- Context-rejection recovery: when the codex error contains "exceeds the context window", pin
last_context_tokens to the window so the existing ApiError compact-and-recover path actually engages — rescues already-wedged sessions on their next input.
- Reasoning-aware trim: before the compaction summary request on the responses path, drop reasoning items older than the last user message (exactly the items the server itself discards in chains) so the compaction request itself fits under the wall it's trying to escape.
Related: #165 (transport divergence — this is the accounting consequence of the delta transport).
Symptom (live session, gpt-5.6-sol @ Extra high, v0.0.200)
The meter says 95k/270k, compaction is armed at 216k — yet the backend rejects for exceeding the window, and every retry fails the same way (the session is wedged until a manual /compact or /clear).
Root cause — the meter and the wire measure different things
stepResponsesappends every output item to local history, including each turn's encrypted reasoning item (agent_steps.zig:42);buildBodyreplays them withstore:false+include: [reasoning.encrypted_content].previous_response_id; the server discards prior-turn reasoning from chained context, sousage.total_tokenstracks a pruned conversation.recordUsageResponsescopies that number intolast_context_tokens(agent_request.zig) — this is the 95k.runTurncloses the WS per turn (agent.zig:299), so the first request of every turn is a full-history resend — local history, all accumulated reasoning items included. At Extra-high effort on a long session that true cost can be ~3x the chained number.last_context_tokensstays at the stale chained value, the pre-turn compact check (mainloop.zig:705) and the ApiError recovery (mainloop.zig:654) both see 95k < 216k, and nothing compacts. Wedged.Why codex CLI doesn't hit this
Its
ContextManager.get_total_token_usage()doesn't trust the last server report alone: it adds an estimate for reasoning items the server accounting excluded (get_non_last_reasoning_items_tokens()whenserver_reasoning_includedis false) plus items appended since the last report, and compaction gates on that corrected number. Confirmed against openai/codexcodex-rs/core/src/context_manager/history.rs.Fix (parity + belt-and-braces)
last_context_tokens = max(server total, full-input estimate)on the responses path — estimate = serialized bytes of the fullinputarray / 4 via a counting discard writer (zero-alloc). Makes compact@80% fire before the wall even while WS deltas keep server totals small.last_context_tokensto the window so the existing ApiError compact-and-recover path actually engages — rescues already-wedged sessions on their next input.Related: #165 (transport divergence — this is the accounting consequence of the delta transport).