Auto-compaction is only evaluated at turn boundaries, so a long tool loop reaches the context limit unchecked
- Vorherrschende Sprache
- Rust
- Sterne
- 54.2k
- Forks
- 6.2k
- Ø Merge
- 3 T. 2 Std.
- Gemergte PRs (30 T.)
- 262
Beschreibung
**Describe the bug**
Auto-compaction is only evaluated at a user-turn boundary, so tool output accumulated inside a turn is never re-examined. A turn that begins under the threshold can run all the way to the provider's context limit without a single check.
| path | site | gate |
|---|---|---|
| legacy | `crates/goose/src/agents/agent.rs:2049` | `check_if_compaction_needed` runs once, before the reply stream |
| state machine | `crates/goose/src/agents/state_machine/ops_compaction.rs:235` | `not_applicable()` unless `last_effective_role == EffectiveRole::User` |
This was noted as a secondary observation in #11049 and deferred. It is not theoretical — it is reproducible in a live session, and it survives a correct context limit.
**Observed**
A session with a correctly resolved `context_limit` of 49,152 and `GOOSE_AUTO_COMPACT_THRESHOLD=0.35` (trigger ≈ 17,203). Auto-compaction fired six times at turn boundaries and worked. Then one tool-heavy turn, from `usage_ledger`:
```
16:09:29 17,513 in is_compaction=1 -> compacted to 6,912
16:29:29 17,189 in past the 17,203 trigger, nothing fires
16:32:50 24,560 in
16:42:15 40,783 in
16:45:14 49,120 in + 32 out = 49,152 at the server's hard ceiling
```
Thirty-plus consecutive provider calls in a single turn, no compaction check in any of them. Note the last row's 32 output tokens: at that point the model has almost no room left to answer, and the next request is rejected.
**To Reproduce**
1. Configure any provider whose context limit is small enough to reach in one turn.
2. Set `GOOSE_AUTO_COMPACT_THRESHOLD` well below 1.0.
3. Ask for work that drives a long sequence of tool calls without returning to the user.
4. Watch `usage_ledger`: tokens climb past `threshold * context_limit` and keep going; `is_compaction` stays 0 until the turn ends or the provider rejects the request.
**Expected behavior**
The threshold is re-evaluated during a turn, once tool responses have landed, so a long tool loop compacts instead of walking into the context limit.
The safe point matters: compacting between a tool request and its response would hide the request from the model and orphan it. `EffectiveRole::Tool` — a user-role message carrying tool responses — is the natural boundary, and `Assistant` must stay excluded.
**Please provide the following information**
- **OS & Arch:** macOS 15 (Darwin 25.5.0) arm64
- **Interface:** Desktop (ACP) and CLI
- **Version:** 1.45.0
- **Provider & Model:** custom OpenAI-compatible (llama.cpp-derived, `--ctx 49152`)
**Additional context**
Related but distinct:
- #11048 / #11050 — classifying the resulting 400 so it is recoverable. That makes the overflow survivable; it does not stop it happening.
- #11049 — resolving the context limit correctly. Necessary, and not sufficient: the session above already had the right limit.
`AGENTS.md` requires both agent paths to change together, which is achievable in one edit per path. I have a working branch and will attach it as an RFC PR; it needs a test that exercises a mid-turn compaction end to end before it should be considered mergeable.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.