Feature request: auto-compact on idle timeout to prevent cache expiry cost

Status Open
Maintainer reply None cached
Activity 5 comments · opened Jun 7, 2026

Problem

When running a long operation (e.g. a test suite), users often step away from their Claude Code session. If they return after more than ~5 minutes, the prompt cache has expired. The full conversation context then has to be re-processed on the next turn, which is slower and more expensive than if the cache were still warm.

Requested feature

A configurable idle timeout that automatically triggers /compact before the prompt cache expires (i.e. before the ~5 minute TTL is up). This would reduce the context size proactively, so that when the user returns, re-processing the compacted summary is cheap — rather than re-processing the full conversation history cold.

Example configuration (sketch)

{
  "autoCompactOnIdleSeconds": 240
}

When the session has been idle for the configured duration, Claude Code would trigger compaction automatically — similar to how auto-compaction works when approaching context limits, but time-triggered instead of token-triggered.

Why this matters

  • Long CI/test runs are a common workflow where users step away
  • Cache expiry during these pauses silently increases cost and latency on return
  • Manual /compact before stepping away works but requires the user to remember to do it

View original on GitHub ↗

5 Comments

yurukusa · 2 months ago

Good request, and the mechanism is real — I measured it on my own sessions to be sure. Across a sample of real session JSONL, the cached context re-read (cache_read_input_tokens) is ~95% of all input token volume (cache_creation ~4.8%, genuinely new/uncached input ~0.2%). So the thing you're paying to rebuild after an idle gap really is the dominant cost, not a rounding error — when the ~5min TTL lapses, that ~95% gets re-established at the cache-write rate (~1.25× base) instead of the cache-read rate (~0.1× base) on the return turn. That's a real spike.

Two design nuances worth flagging before this is built, because they change whether it actually saves money:

  1. /compact is itself a billed model call, and it's lossy. It summarizes the conversation through the model, so it costs tokens every time it fires, and it permanently drops detail. The "stepped away during a long test run" case is exactly where you may go idle repeatedly — a naive autoCompactOnIdleSeconds could fire several times in one sitting, paying the compaction cost each time and shredding context you still needed. To be a net win it'd need guards: fire at most once per idle period, only when the context is large enough that the saving exceeds the compaction cost, and ideally only when the cache would otherwise actually expire.
  1. The saving is bounded. Compacting at 240s doesn't keep the cache warm — once you go idle again it still expires. The benefit is only that the eventual cold re-read is of the smaller compacted context, so the saving ≈ (full_size − compacted_size) × cache_write_rate. Real, but it's not "cache stays warm," it's "the cold read is cheaper."

On the workaround today: there isn't a native idle trigger to self-implement this — the hook events are PreToolUse/PostToolUse/Stop/etc., none of which fire on a time-based idle — so the manual /compact before stepping away that you mentioned is genuinely the only current lever. That gap is a fair argument for the feature.

One more, since cost is the motivation: because cache_read is ~95% of volume, the first-order cost driver is context size × turns, and idle-expiry is a secondary spike on top of that. If you want to quantify your own exposure before betting on this feature, sum message.usage.cache_creation_input_tokens vs cache_read_input_tokens across your session's JSONL — the cache_creation share is what jumps right after an idle gap, so you can see exactly how many tokens (and dollars) the expiry actually costs you in your workflow.

junaidtitan · 2 months ago

The idle-timeout compaction framing is interesting because it's really about the same root problem as threshold-based compaction, just triggered by time instead of token count: you want the session to be in a lean state before an expensive re-processing event (cache expiry in your case, context wall in the threshold case).

cozempic's guard daemon (github.com/Ruya-AI/cozempic) addresses the overlap: it prunes the session JSONL proactively before the cache expires or the threshold is hit, using strategies that strip aged tool outputs, metadata, and thinking blocks — so the context that gets re-processed after your test suite finishes is already trimmed. That's not the same as your proposed autoCompactOnIdleSeconds: 240 (it doesn't specifically trigger on idle), but the effect on cache-expiry cost is similar: a smaller context means cheaper re-processing on return, whether or not the session was idle.

The gap in cozempic vs your proposal: it doesn't know about your workflow's natural idle boundaries (you stepped away for a CI run). A proper idle-timeout feature would let CC align the compaction event with when you intend to be away, which is more precise than a continuous threshold watcher. That's a real native feature worth having.

Efs-O · 2 months ago

I ran into the same idle-expiry problem and ended up trying a different workaround than auto-compact: keeping the active Claude Code session’s prompt cache warm before the 5-minute TTL expires.

The approach I built is here, in case it helps anyone experimenting with this problem:

It is a VS Code extension rather than a Claude Code core feature. The basic flow is:

  • install transparent Stop / UserPromptSubmit hooks into ~/.claude/settings.json
  • when Claude finishes a turn, start a per-session countdown
  • shortly before the cache TTL expires, resume that session headlessly in a throwaway fork
  • send an intentionally inert keep-alive turn
  • delete the fork transcript afterward
  • show the active sessions in a VS Code sidebar/status bar with countdown, pause/resume, dismiss, and ping-streak caps

The keep-alive message is deliberately constrained:

[AW_TURN_TYPE: keep-alive]
This is a cache keep-alive maintenance turn.
Do not use tools.
Do not post to the board.
Do not inspect or edit files.
Do not emit natural-language prose.
If the CLI requires a reply, emit only the inert marker [AW_KEEPALIVE_OK].

This does not replace the feature being requested here. Auto-compact before expiry would still be useful, especially for users who prefer reducing replay cost instead of sending keep-alive turns.
But for the specific “I stepped away / waited on tests / reviewed a diff for more than ~5 minutes and came back to a cold cache” case, this has been a practical workaround. It is pre-release and mostly Windows-tested so far, but the repo explains exactly what it changes on disk and how to remove it.

ariccio · 1 month ago

Alternatively, for the longer subscription TTL, it would be nice if Claude code auto compacted before the hour expires, and then users could pick if they want to pay the cost of resuming the full idle session, or resuming the compacted session.

There are open issues that are related:
https://github.com/anthropics/claude-code/issues/78245
https://github.com/anthropics/claude-code/issues/54822
https://github.com/anthropics/claude-code/issues/52002

dthinkr · 9 hours ago

The idle trigger is the right idea. The problem is that a single threshold like 240 can fire after the prompt cache has already expired, and at that point compacting costs more than doing nothing.

The costs break down like this. Resuming an untouched session costs 2C (full cold read). Compacting while the cache is warm costs 0.1C for the summary plus 2C' for the new smaller prefix. But if the cache expired before compaction ran, you pay the full 2C for summarization and 2C' on top. That loses on the first return.

| scenario | cost to resume |
|---|---|
| leave it alone | 2C |
| compact warm | 0.1C + 2C' |
| compact cold | 2C + 2C' |

Cold compaction isn't a total write-off. Each later warm turn saves 0.1(C - C'), so you break even after about six turns. But an idle session gives no signal whether the user is coming back for one quick thing or settling in. A late-firing default takes that gamble on their behalf.

Cache TTL varies by account. My account is on a 1-hour tier (check your transcripts for ephemeral_1h_input_tokens vs ephemeral_5m_input_tokens). On that tier, 240s is unnecessarily early: it gives up conversation detail roughly 46 minutes before the cache would expire, and if the user walks back within that window the compaction threw away detail they could have resumed for free. If they stay away past expiry the compaction can still pay off over later turns, but that is the break-even case above, and it costs the loss of detail either way.

A context floor matters too. I backtested 168 sessions over 45 days on my machine. Sessions holding under 100k when they went idle came back 68% of the time; above 500k, 98%. In this dataset, context size was enough on its own to rank reuse likelihood, and compacting small idle sessions offered little upside. Across 142 real compactions the measured ratio was C'/C = 0.203.

Suggested config shape:

"autoCompactOnIdle": { "afterSeconds": "auto", "minContextTokens": 450000 }

"auto" would read from the account's observed cache tier. An internal upper bound would make a late tick a no-op instead of a cost.

For anyone who wants this before it ships: it works today without new features. The VS Code extension runs the CLI over --input-format stream-json on a socketpair, and a user message arriving that way has no origin field, so Claude Code treats it as human. /compact sent through the extension's claudeCode.claudeProcessWrapper expands normally. I documented the mechanism and the eight injection routes that don't work here: https://github.com/dthinkr/claude-code-auto-compactor