diff --git a/server/src/session/claude.rs b/server/src/session/claude.rs index b118d01..a459e0d 100644 --- a/server/src/session/claude.rs +++ b/server/src/session/claude.rs @@ -172,6 +172,25 @@ impl Queue { /// the attach path must agree about which file is which; if they drift, /// a reattached session reads a file nothing is writing and simply looks /// idle forever. +/// The context size the CLI compacts at, rather than letting it choose. +/// +/// Left to `auto` a phone session drifts: these run for hours, nobody +/// closes them, and the transcript carries screenshots. One measured here +/// reached 491,562 tokens before the CLI compacted it, and *every* API +/// call re-reads the whole context -- so at that size a single tool call +/// bills about 49k tokens before it does anything, and one ordinary +/// request ("can you make it so you can rename a session?") cost 4.2 +/// million across its 130 calls. +/// +/// 100k is the smallest window the CLI accepts and roughly the cheapest: +/// per-call cost falls with the cap, while the compaction it forces costs +/// about the same in total either way -- a smaller window compacts more +/// often but each pass is proportionally smaller. What it trades is how +/// much detail survives a compaction, which is a real cost to the work +/// and the reason this is one named constant rather than a computed +/// value. Raise it if sessions start losing the thread. +const AUTOCOMPACT_WINDOW: &str = "100k"; + const STDIN_FIFO: &str = "stdin.fifo"; const STDOUT_LOG: &str = "stdout.log"; const STDERR_LOG: &str = "stderr.log"; @@ -327,6 +346,7 @@ impl ClaudeDriver { }; push("--input-format", "stream-json"); push("--output-format", "stream-json"); + push("--autocompact", AUTOCOMPACT_WINDOW); // Hidden but load-bearing: without it the CLI resolves permissions // itself and nothing ever reaches the phone. push("--permission-prompt-tool", "stdio");