fix(auto-compact): decide again when the command goes out, and send nothing into a usage-limit pause (#777)

The review items of #777, finished here so the PR can land.

A statusline report fires the trigger in the middle of a turn, and the
command then waits for that turn to end. Until now it went out whatever
had happened meanwhile: with auto-clear at 140k, a report of 150k fired
while the session worked, Claude Code compacted by itself, the report
fell to 20k, the session went idle and /clear wiped the conversation.
Both actions now ask again at that moment and return unsent when the
context is no longer at the threshold, when the option was switched off,
or when its threshold was raised past the context. The skipped command
stamps nothing, so the next report is judged like any other, and the
autoCompact and autoClear events carry the size at the moment of sending.

With auto-resume on, a trigger that fired before a usage limit hit sent
/clear once the pane went idle, and auto-resume then typed continue into
an empty conversation. The checks start nothing from the limit message
until the resume prompt went out, and an action that waited through the
start of a pause returns unsent.

Also: the threshold cap never drops below the 1,000 minimum (a reported
window of 500 stored a threshold of 500), a test pins that a conversation
switch drops the report, and docs/api-reference.md names the new
contextWindowSize session field.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-11 20:23:09 +01:00
1 parent 6fa2dfa549
commit 68e010b887
4 files changed
+267 -11

No files matched your search

+8
View File
@@ -1152,6 +1152,14 @@ that sends the report, decided when the pane starts: `injected` (it does), `coll
`.claude/settings.local.json` sets its own `statusLine`, which Codeman never replaces) or
`remote` (remote and Docker sessions). Absent when unknown.
`contextWindowSize` is the model's context window in tokens from the same report (200000,
or 1000000 with extended context), absent until a report carries it and dropped, like
`promptCache`, on a post-`/clear` conversation switch. It is the upper bound for
`autoCompactThreshold` and `autoClearThreshold` (1000000 while it is absent): a higher value
is stored as the window, and Session Options stops its two inputs there. The thresholds
themselves are compared with the report's `context_window.total_input_tokens`, cache reads
included, and with the session's token counter for a launch without the exporter.
## Placing a new session in a tab group (`tabGroupId`)
`POST /api/v1/sessions` and `POST /api/v1/quick-start` accept an optional
+5 -1
View File
@@ -189,7 +189,11 @@ Further detail: the handheld OFF default comes from the mobile block in `getDefa
**After a /compact or /clear, the report from before the command is ignored by value, not by time.** A report carries the usage of the last API response, so that number stays the newest one until the command finishes. The first report that differs came from the finished command (0 on Claude Code 2.1.319) or from a later response. If the command was swallowed, that report is higher and the threshold fires again, so nothing can lock the automation off. A threshold below what a compact leaves behind fires again on the next response, like the counter always did. A counter summed from JSON usage never drops after a compact, so it counts from its value at the last one. The pane parser's counter is the absolute number on screen and only moves past its highest value, so any move clears that baseline (`noteAbsoluteCounter()`). Neither value survives a restart.
**Thresholds go up to the reported window** (`_maxThreshold()`, 1M when unknown), and a higher value stops at the window when it is set. A threshold set before the window was known can sit above it and never fire; Claude Code's own compaction still does.
**The decision is made again when the command goes out** (`_decideAtSend()`). A report fires the trigger in the middle of a turn, and the command then waits for that turn to end (`executeWhenIdle`). What was true at the trigger can be false by then: Claude Code compacted by itself and the report fell (a `/clear` at 140k would otherwise wipe a conversation that is back at 20k), the option was switched off or its threshold raised, or the turn ended on a usage limit. So each action first asks again and returns without sending when the answer is no. That return stamps nothing (neither the report's value nor the counter's baseline), the cooldown releases the flag, and the next report is judged like any other. The counter never falls, so on the fallback the second answer repeats the first.
**Nothing is sent into a usage-limit pause** (`_heldByLimit()`: from the limit message until auto-resume's `continue` went out, the gap after its Escape included). A `/clear` sent there is followed by that `continue`, typed into an empty conversation; the respawn controller blocks its own `/clear` for the same reason (`isLimitPaused` in `onIdleDetected`). The checks start nothing while it holds, and an action that waited through the start of one returns unsent. Only auto-resume watches for the limit message, so with auto-resume off nothing is held.
**Thresholds go up to the reported window** (`_maxThreshold()`, 1M when unknown, never below the 1,000 minimum), and a higher value stops at the window when it is set. A threshold set before the window was known can sit above it and never fire; Claude Code's own compaction still does.
### Prompt-cache keep-warm