Autocompact never fires proactively at the context edge — autonomous/headless sessions park until the next user message (desktop app + Agent SDK)
Summary
Autocompact never fires proactively when a session reaches the context edge. It fires only lazily, at the start of the next incoming turn (user message, hook/notification, or scheduled wakeup). A session that hits the edge with no future input source parks there indefinitely — this silently kills long-running autonomous/headless sessions overnight.
This affects both the desktop app and applications embedding the Agent SDK. Since the harness is closed source we can't tell which layer owns the compaction trigger (CLI core vs desktop app vs SDK) — part of the ask is clarifying that.
Environment
- Claude Code v2.1.205, Windows 11 Pro (10.0.26200)
- Desktop app (
entrypoint: claude-desktop), modelclaude-fable-5(200k window; also reproduced with non-1M models via the Agent SDK) - Long-running autonomous session (background subagents + scheduled wakeups)
Evidence (from one overnight session transcript, 2026-07-14)
The session JSONL shows six compact_boundary events across the night. Every one fired at next-turn start, immediately after a new input arrived — never at the moment the context threshold was crossed:
| compact_boundary (UTC) | Trigger immediately preceding it |
|---|---|
| 05:05:47 | incoming input (next turn start) |
| 07:02:31 | incoming input (next turn start) |
| 10:25:32 | incoming input (next turn start) |
| 12:52:01 | task-notification at 12:47 → turn grew to peak 168,184 tokens at 12:49 → compacted at next turn |
| 13:47:51 | task-notification at 13:45:53 |
| 14:50:51 | user message at 14:48:24 — context had been sitting at ~165k since 14:42 with nothing pending; compaction fired only because the user manually sent a message |
The session only survived the night because it happened to keep receiving subagent completion notifications and had self-scheduled wakeups. When the model instead reaches the edge mid-turn, we've previously lab-verified the turn is silently cut (stop_reason: tool_use, no error surfaced) and compaction defers to the next query — in an autonomous session with nothing else inbound, that next query never comes and the session appears "crashed" until a human types something.
Agent SDK reproduction
Our application embeds the Agent SDK to run many concurrent sessions. Same behavior: sessions that approach the window edge stall and never resume on their own. We attempted to mitigate by forcing the autocompact setting on for every session that isn't on a 1M-context model — no effect; the stall still occurs.
Expected
Compaction should fire proactively when the threshold is crossed — either immediately (in-place, which the CLI demonstrably can do since it sometimes compacts mid-turn) or at latest at the end of the current turn — rather than waiting for a future input that may never arrive.
Ask
- Trigger autocompact eagerly at threshold-crossing (or end-of-turn), not lazily at next-turn start.
- If the turn is cut at the edge, surface it (error/event) instead of a silent
stop_reason: tool_usecut. - Clarify which layer (CLI core / desktop app / Agent SDK) owns the compaction trigger so embedders know where the fix lands and whether an SDK-side workaround exists today.
3 Comments
The lazy compaction behavior you documented is a real gap for anyone running overnight or CI-style autonomous sessions - the silent park-until-next-input is the worst failure mode because there is no error to act on, the session just stops making progress.
A few things worth noting in the meantime:
stop_reason: tool_usecut mid-turn is the harder problem - surfacing that as an error rather than a quiet stop would at least make the failure observable so you can act on it.The timestamp data in your issue is really clear - six compact_boundary events, all lazy. That should make it reproducible in a shorter test session by watching for the event in transcript logs.
Thanks @kcarriedo — that matches our data exactly. We can confirm your first point empirically: in the transcript from the issue, every one of the six compactions was triggered by an incoming input, and several of those inputs were scheduled self-wakeups. A session that habitually arms a periodic wakeup effectively self-mitigates; the overnight session in question only survived because of that.
We also run your second suggestion in our SDK-embedded app: a boundary-cut detector (abnormal
stop_reason: tool_usewhile over the autocompact threshold) that injects a synthetic continuation prompt. It works when the signature matches, but agreed that your third point is the real fix — the mid-turn cut is silent today, so any watchdog is heuristic. Surfacing the cut as an explicit event/error (or just compacting eagerly at the threshold) would make all of these workarounds unnecessary.Appreciate the confirmation that this repros beyond our setup.
Same root cause, opposite symptom, with numbers from a different setup.
We hit this interactively on macOS. The lazy trigger means compaction runs at the start of the next turn, so where a headless session parks forever, an interactive user instead types a message and then waits with nothing rendered while compaction runs. One mechanism, two failure shapes depending on whether an input source exists.
Measured across 256 local transcripts (224 unique
compact_boundaryrecords, 204 withtrigger: auto). Records were deduplicated onuuidfirst, which matters: transcripts re-append prior history on resume, and one boundary can appear up to 30 times in a single file. Depth counts assistant records between the boundary and the nearest preceding human-typed message, excluding tool results, hook injections and system reminders.| depth when compaction fires | share |
|---|---|
| 11+ assistant turns deep | 84.8% |
| 4-10 | 4.9% |
| 1-3 | 5.4% |
| 0, before any assistant reply | 4.9% |
Median 15.6 minutes since the last human message.
preTokensclusters tightly around 168k.The honest read on our own complaint: the interactive case is rare, not common. Of the 11 depth-0 boundaries, 8 are in
agent-*subagent transcripts where nobody is waiting. Only 3 are sessions where a person actually typed and sat there, so the rate that matches the interactive complaint is 3/224, about 1.3%. It feels far more frequent than that because of what it costs when it happens: those three waits were 160s, 166s and 203s with nothing on screen. Salience, not frequency.That said, the 84.8% row is the load-bearing one, and it argues for the same fix from the other side. If compaction already runs mid-turn in the overwhelming majority of cases, then running it at end-of-turn is not a behavior change for most sessions, and it is strictly better for the two tails: the headless session gets its compaction without waiting for an input that never comes, and the interactive user reads the reply while compaction happens behind it.
So we would support Ask 1's end-of-turn half specifically, with one refinement. End-of-turn compaction should be the scheduled path, not the only path. A turn that grows unusually fast can still cross the threshold mid-turn, and in that case compacting immediately is correct. The bug is not that compaction can happen mid-turn, it is that mid-turn is the only time it happens today. Keep it as the safety valve, make end-of-turn the normal case.
One caveat on our data: it is a single machine and a single workflow, heavy on subagents, so the depth distribution is skewed by that and the subagent/interactive split above should be read as specific to this setup rather than general.