Summary
Session 01a07542-3b63-7cfb-9b87-4ea321e750a2 (omo, 2026-09-06 05:47Z → 09-07 01:40Z, openai/gpt-6-astra) grew from 440k to 916,628 prompt tokens with zero compactions, then died on Error Code context_too_large: Your input exceeds the context window of this model. The goal extension re-prompted the identical context three times in 30 s (each one failed the same way) until the user aborted and switched models by hand.
Nothing in the runtime defended the session. Every layer that should have kept it alive was either switched off by one flag or was measuring the wrong budget.
Root-cause chain (all verified on origin/main 5a23f6edf)
1. compaction.enabled=false is persisted globally by a per-session RPC command (origin)
~/.omo/agent/settings.json carries "compaction": {"enabled": false} since at least 2026-08-30 (absent in the 08-26 backup, present in every backup from 08-30 on).
- The only writer of that key is
SettingsManager.setCompactionEnabled (packages/coding-agent/src/core/settings-manager.ts:1230), reached from AgentSession.setAutoCompactionEnabled (agent-session.ts:6723), which is what the RPC command set_auto_compaction calls (modes/rpc/connection-handler.ts:1339-1342).
- OmO Desktop relays its per-thread toggle
omo.autoCompaction.set straight onto that RPC (apps/server/src/provider/omo/omoSessionStats.ts:145). One thread flipping its own toggle rewrites the machine-wide persisted setting and silently disables auto-compaction for every senpi/omo session on the host, forever.
- Field impact: since 08-30 this host recorded 30 provider overflow deaths across 40 session files (26x Anthropic
prompt is too long at 985k-999k/1M on opus-5 and fable-5-1, 3x astra context_too_large, 1x sol) — all with zero compaction entries.
2. That one flag disables every recovery path, including emergency ones
_getAutoCompactionReason returns undefined for overflow as well as threshold when !settings.enabled (agent-session.ts:2107-2110).
_checkCompaction returns false at the top when !settings.enabled (:6177), so the one-shot overflow recovery (compact + retry, :6234-6262) never runs.
_enforceCompactionBeforeProvider skips the hard reserve gate (window - reserve) when !settings.enabled (:6080); _enforceFinalProviderAdmission likewise (:6143).
- Result: "auto-compaction off" is implemented as "no compaction ever, even when the provider has already rejected the request". The session cannot make progress by any path other than a human running
/compact or switching models.
3. Goal continuation re-prompts a deterministic failure
didTerminalProviderErrorEndTurn (extensions/builtin/goal/terminal-provider-error.ts:22-28) treats any stopReason: "error" turn as a provider failure and afterProviderFailure → afterAgentSettled queues a providerRecovery continuation (monitor-continuation.ts:189-230).
- A context overflow is not transient: the same context is sent again and rejected again. Session evidence:
#1320 error → #1322 goal-continuation → #1323 error → #1325 goal-continuation → #1326 error → #1328 goal-continuation → user abort.
- The
PROVIDER_ERROR_BLOCKED_REASON mechanical block exists but only after a retry budget; overflow needs to block immediately with a reason that names compaction.
4. gpt-6-astra contextWindow: 1,050,000 overstates the real prompt budget by 128k
- OpenAI splits the window into a hard input cap and a separate output cap: astra = 922,000 input + 128,000 output; GPT-5 family = 272,000 + 128,000 (the request is rejected with
context_too_large regardless of max_output_tokens).
- senpi uses
model.contextWindow everywhere as the prompt budget (core shouldCompact, getContextUsage, hard valve, clampMaxTokensToContext). The generator even says so — packages/ai/scripts/generate-models.ts:400-408: "GPT-6 Astra ships the documented maximum (1,050,000 = 922,000 input + 128,000 output)" — and then ships 1,050,000 as contextWindow.
- The catalog already uses the input-cap convention for gpt-5.4/5.5/5.6-luna/terra (
contextWindow: 272000), so astra is the inconsistent row. Codex's own models.json caps astra at 272k default / 872k max.
- Consequence even with compaction enabled: the core reserve gate sits at 1,033,616 and the scaled hard valve at ~1,000,848 — both above the provider's 922k cap, so the core paths can never fire before the provider rejects. The observed rejection (916,628 succeeded, +~14k tool results failed) matches the 922k cap exactly.
5. (Related, tracked separately) extension-triggered turns skip before_agent_start
sendCustomMessage({triggerTurn:true}) (goal continuations, task/monitor wakes) runs _enforceCompactionBeforeProvider + _promptAgent but never emitBeforeAgentStart, so the builtin adaptive threshold/valve does not evaluate on those turns (already noted 2026-09-03). Not causal here (the flag in #2 was off), but it narrows proactive compaction to user-typed turns in goal-driven sessions. Needs a design decision on what the hook receives for a non-user prompt; out of scope for this PR.
Ideal end state
set_auto_compaction (RPC) is session-scoped: it changes this session's behaviour and is reported back through get_state, but never rewrites the persisted global setting. The interactive /settings toggle keeps persisting (it is the settings editor).
compaction.enabled=false means no proactive compaction (threshold, speculative, idle, adaptive). Emergency paths stay armed: provider overflow → one-shot compact-and-retry; pre-prompt hard reserve gate; final admission gate. A session must never be able to reach a state where the only exit is a human.
- Goal continuation never re-prompts after a turn ended by a context overflow that recovery did not fix; it records a mechanical block (
context overflow ended the turn (compaction did not recover)) so the next user message resumes it.
gpt-6-astra catalog contextWindow = 922,000 (the input cap), consistent with the 272k rows and with the generator's own comment.
Tracking
Evidence pointers
Summary
Session
01a07542-3b63-7cfb-9b87-4ea321e750a2(omo, 2026-09-06 05:47Z → 09-07 01:40Z,openai/gpt-6-astra) grew from 440k to 916,628 prompt tokens with zero compactions, then died onError Code context_too_large: Your input exceeds the context window of this model.The goal extension re-prompted the identical context three times in 30 s (each one failed the same way) until the user aborted and switched models by hand.Nothing in the runtime defended the session. Every layer that should have kept it alive was either switched off by one flag or was measuring the wrong budget.
Root-cause chain (all verified on origin/main
5a23f6edf)1.
compaction.enabled=falseis persisted globally by a per-session RPC command (origin)~/.omo/agent/settings.jsoncarries"compaction": {"enabled": false}since at least 2026-08-30 (absent in the 08-26 backup, present in every backup from 08-30 on).SettingsManager.setCompactionEnabled(packages/coding-agent/src/core/settings-manager.ts:1230), reached fromAgentSession.setAutoCompactionEnabled(agent-session.ts:6723), which is what the RPC commandset_auto_compactioncalls (modes/rpc/connection-handler.ts:1339-1342).omo.autoCompaction.setstraight onto that RPC (apps/server/src/provider/omo/omoSessionStats.ts:145). One thread flipping its own toggle rewrites the machine-wide persisted setting and silently disables auto-compaction for every senpi/omo session on the host, forever.prompt is too longat 985k-999k/1M on opus-5 and fable-5-1, 3x astracontext_too_large, 1x sol) — all with zero compaction entries.2. That one flag disables every recovery path, including emergency ones
_getAutoCompactionReasonreturnsundefinedfor overflow as well as threshold when!settings.enabled(agent-session.ts:2107-2110)._checkCompactionreturnsfalseat the top when!settings.enabled(:6177), so the one-shot overflow recovery (compact + retry,:6234-6262) never runs._enforceCompactionBeforeProviderskips the hard reserve gate (window - reserve) when!settings.enabled(:6080);_enforceFinalProviderAdmissionlikewise (:6143)./compactor switching models.3. Goal continuation re-prompts a deterministic failure
didTerminalProviderErrorEndTurn(extensions/builtin/goal/terminal-provider-error.ts:22-28) treats anystopReason: "error"turn as a provider failure andafterProviderFailure→afterAgentSettledqueues aproviderRecoverycontinuation (monitor-continuation.ts:189-230).#1320 error → #1322 goal-continuation → #1323 error → #1325 goal-continuation → #1326 error → #1328 goal-continuation → user abort.PROVIDER_ERROR_BLOCKED_REASONmechanical block exists but only after a retry budget; overflow needs to block immediately with a reason that names compaction.4.
gpt-6-astracontextWindow: 1,050,000overstates the real prompt budget by 128kcontext_too_largeregardless ofmax_output_tokens).model.contextWindoweverywhere as the prompt budget (coreshouldCompact,getContextUsage, hard valve,clampMaxTokensToContext). The generator even says so —packages/ai/scripts/generate-models.ts:400-408: "GPT-6 Astra ships the documented maximum (1,050,000 = 922,000 input + 128,000 output)" — and then ships 1,050,000 ascontextWindow.contextWindow: 272000), so astra is the inconsistent row. Codex's ownmodels.jsoncaps astra at 272k default / 872k max.5. (Related, tracked separately) extension-triggered turns skip
before_agent_startsendCustomMessage({triggerTurn:true})(goal continuations, task/monitor wakes) runs_enforceCompactionBeforeProvider+_promptAgentbut neveremitBeforeAgentStart, so the builtin adaptive threshold/valve does not evaluate on those turns (already noted 2026-09-03). Not causal here (the flag in #2 was off), but it narrows proactive compaction to user-typed turns in goal-driven sessions. Needs a design decision on what the hook receives for a non-user prompt; out of scope for this PR.Ideal end state
set_auto_compaction(RPC) is session-scoped: it changes this session's behaviour and is reported back throughget_state, but never rewrites the persisted global setting. The interactive/settingstoggle keeps persisting (it is the settings editor).compaction.enabled=falsemeans no proactive compaction (threshold, speculative, idle, adaptive). Emergency paths stay armed: provider overflow → one-shot compact-and-retry; pre-prompt hard reserve gate; final admission gate. A session must never be able to reach a state where the only exit is a human.context overflow ended the turn (compaction did not recover)) so the next user message resumes it.gpt-6-astracatalogcontextWindow= 922,000 (the input cap), consistent with the 272k rows and with the generator's own comment.Tracking
compaction.enabled=falseenabledset_auto_compactionsession-scopedcontextWindow922,000 (generator + regenerated catalog + tests)bun testgreen (ascii box / mengmotaMac), typecheck + lintEvidence pointers
~/.omo/agent/sessions/--Users-yeongyu-sisyphuslabs--/2026-09-06T05-47-49-987Z_01a07542-….jsonl(1335 events, 0compactionentries;logs/compaction.loghas 0 lines for the session id).context_too_large→ TTSR paragraph-repeat detector misses near-repeat drift (exact hash match); needs normalized match gated by progress #1330 user abort → model switched.context_length_exceeded,param: "input").