Auto-compaction never fires on large-context models (80% of a 1M window = 800k trigger)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 19.9k
- Forks
- 2.3k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 30
Description
Auto-compaction is unreachable on large-context models: token_budget is the model's full context window, so the 80% trigger sits at 800k on a 1M-context model. A session then never compacts and resends its whole transcript on every provider call.
Mechanism
token_budget is set straight from the provider's context window — crates/jcode-app-core/src/agent.rs:458, agent.rs:622, agent/compaction.rs:213, plus conversation_state.rs:249,312 and model_context.rs:656.
Every trigger is then expressed as a fraction of that budget:
// crates/jcode-compaction-core/src/lib.rs:9
pub const COMPACTION_THRESHOLD: f32 = 0.80;
// crates/jcode-base/src/compaction.rs — reactive
self.context_usage_with(all_messages) >= COMPACTION_THRESHOLD
// crates/jcode-base/src/compaction.rs — proactive
let threshold = COMPACTION_THRESHOLD as f64 * budget;
- 200k window → trigger at 160k → compacts normally.
- 1M window → trigger at 800k. Sessions that plateau below that never compact at all.
mode = "proactive" does not help: should_compact_proactively measures against the same 0.80 * token_budget, so it only fires earlier relative to an unreachable ceiling.
There is no config escape hatch. [compaction] has no budget key, and NamedProviderModelConfig::context_window applies only to custom named providers, not the built-in ones.
Impact
This is quiet and expensive. Because each tool call re-sends the full transcript, cost is (context size) x (number of calls) — compaction is what bounds the first term. When it never fires, an agentic session degrades into resending a huge context on every call.
Measured over roughly one day on 0.61.x, same machine, same kind of work:
| session | model | provider calls | compactions | cache-read tokens |
|---|---|---|---|---|
| A | Opus (1M window) | 622 | 0 | 124,224,904 |
| B | 200k window | 559 | 6 | 49,796,153 |
Session A peaked at ~395k context — real usage, but only 40% of a 1M window, so it sat under the 80% trigger for its entire life and compacted zero times. Day total across sessions: 168,106,320 cache-read tokens, 97% on the 1M-window model.
Session B is the control: same codebase, comparable call count, auto-compaction working normally (all outcomes grew=false, e.g. pre_tokens=168790 post_tokens=31107 tokens_saved=137683). Compaction itself is healthy — it just never gets invoked on the large-context model.
Suggested fix
Cap the budget rather than change the threshold, so the trigger stays reachable without misrepresenting the model's real window. A [compaction] max_token_budget: Option<usize> clamped inside set_budget()/with_budget() covers all call sites at one choke point, and None preserves today's behaviour exactly.
fn clamp_budget(&self, budget: usize) -> usize {
match self.compaction_config.max_token_budget {
Some(max) if max > 0 && budget > max => max,
_ => budget,
}
}
I'm running this locally against be15ad68 with tests (default-uncapped, clamping, under-cap untouched, Some(0) ignored, with_budget parity, a TOML key-binding test, and a behavioural one asserting 400k reads as under-threshold at 1M but over-threshold at a 200k cap). Happy to open a PR if you'd like it in this shape — or if you'd rather derive the cap automatically rather than have users configure it, that seems reasonable too.
Environment
- jcode
v0.61.2-dev (be15ad68), macOS 27.0 arm64 [compaction] mode = "reactive"(defaults otherwise)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the compaction budget call sites in crates/jcode-app-core/src/agent.rs, agent/compaction.rs, conversation_state.rs, and model_context.rs, then inspect set_budget/with_budget and the compaction configuration. Run the mentioned default, clamping, TOML binding, parity, and behavioral tests; done means the cap is optional, clamps large budgets, leaves smaller budgets unchanged, and keeps existing behavior when unset.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- ai, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100