Merge #777: auto-compact and auto-clear from the statusLine context size

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-11 20:18:51 +01:00
commit 6fa2dfa549
16 files changed
+579 -45

No files matched your search

+21 -6
View File
@@ -153,7 +153,7 @@ import {
buildMuxAttachEnv,
buildShellEnv,
} from './session-cli-builder.js';
import { SessionAutoOps } from './session-auto-ops.js';
import { SessionAutoOps, type ContextReport } from './session-auto-ops.js';
import { detectUsageLimitPause } from './usage-limit-patterns.js';
import { SessionTaskCache } from './session-task-cache.js';
import { InteractivePtyExitBreaker } from './session-pty-exit-breaker.js';
@@ -675,6 +675,7 @@ export class Session extends EventEmitter {
private _watching: string | null = null;
/** Last `prompt_cache` report from the statusline exporter; null until Claude sends one. */
private _promptCache: PromptCacheStatus | null = null;
private _contextReport: ContextReport | null = null;
/** Prompt-cache keep-warm opt-in; undefined = follow the app-wide default. Persisted. */
private _keepWarmEnabled: boolean | undefined;
/** Last status the keep-warm service published for this session; ephemeral. */
@@ -1290,6 +1291,7 @@ export class Session extends EventEmitter {
isWorking: () => this._isWorking,
isStopped: () => this._isStopped,
getTotalTokens: () => this._totalInputTokens + this._totalOutputTokens,
getContextReport: () => this._contextReport,
getSessionId: () => this.id,
});
this._autoOps.on('autoCompact', (data) => this.emit('autoCompact', data));
@@ -1485,6 +1487,7 @@ export class Session extends EventEmitter {
// rather than show the previous conversation's "warm until" against the new one; the
// next statusline render repopulates. A same-id reattach returned above, untouched.
this._promptCache = null;
this._contextReport = null;
}
/**
@@ -1833,6 +1836,17 @@ export class Session extends EventEmitter {
return changed;
}
/**
* Take the context size from a statusline report and run the threshold checks on it. The
* token paths above cannot do this: they only run when the counter moves, and the counter
* does not see the cached context.
*/
noteContextReport(report: ContextReport): void {
this._contextReport = report;
this._autoOps.checkAutoCompact();
this._autoOps.checkAutoClear();
}
/** Prompt-cache keep-warm opt-in; undefined means the app-wide default applies. */
get keepWarmEnabled(): boolean | undefined {
return this._keepWarmEnabled;
@@ -2479,6 +2493,7 @@ export class Session extends EventEmitter {
autoResumeEnabled: this._autoOps.autoResumeEnabled,
autoResumeAt: this._autoOps.autoResumeAt ?? undefined,
promptCache: this._promptCache ?? undefined,
contextWindowSize: this._contextReport?.windowSize,
statusLineExporter: this.statusLineExporter,
keepWarmEnabled: this._keepWarmEnabled,
keepWarm: this._keepWarm ?? undefined,
@@ -5006,9 +5021,9 @@ export class Session extends EventEmitter {
// Matches patterns like "123.4k tokens", "5234 tokens", "1.2M tokens"
//
// SAFETY LIMITS:
// - Max tokens per session: 500k (Claude's context is ~200k)
// - Max tokens per session: MAX_SESSION_TOKENS (the largest context Claude reports is 1M)
// - Max delta per update: 100k (prevents sudden jumps from parsing errors)
// - Rejects "M" suffix values > 0.5 (500k) to prevent false matches
// - Rejects "M" suffix values above that maximum to prevent false matches
private parseTokensFromStatusLine(cleanData: string): void {
// Quick pre-check: skip expensive regex if "token" not present (performance optimization)
if (!cleanData.includes('token')) return;
@@ -5026,9 +5041,8 @@ export class Session extends EventEmitter {
if (suffix === 'k') {
tokenCount *= 1000;
} else if (suffix === 'm') {
// Safety: Reject M values that would result in > 500k tokens
// Claude's context window is ~200k, so anything claiming millions is likely a false match
if (tokenCount > 0.5) {
// Safety: anything claiming more than the largest context is likely a false match
if (tokenCount * 1000000 > MAX_SESSION_TOKENS) {
console.warn(
`[Session ${this.id}] Rejected suspicious M token value: ${tokenMatch[0]} (would be ${tokenCount * 1000000} tokens)`
);
@@ -5064,6 +5078,7 @@ export class Session extends EventEmitter {
this._totalOutputTokens += Math.round(delta * 0.4);
// Check if we should auto-compact or auto-clear
this._autoOps.noteAbsoluteCounter();
this._autoOps.checkAutoCompact();
this._autoOps.checkAutoClear();
}