Skip to content

Auto-compaction is only evaluated at turn boundaries, so a long tool loop reaches the context limit unchecked #11072

Description

@vincenzopalazzo

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:

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions