Skip to content

[BUG] Ensure context truncation always makes measurable progress #1254

Open

Description

Problem

Fallback context truncation can report success without reducing context. With a short history such as an assistant tool_use followed by one oversized user tool_result, the current 50% message calculation rounds down to zero removable messages. Zoo Code still emits a truncation event, leaves the oversized history unchanged, and retries into the same failure indefinitely.

The reproduced signal is Context Truncated with 0 messages removed while the recalculated context remains above the allowed input budget.

Scope

Make context recovery monotonic and bounded.

  • Treat recovery as successful only when the recalculated model-facing token count decreases.
  • When message-level truncation cannot remove a valid turn/tool pair, shrink the largest eligible textual tool-result block while preserving its tool identifier and result shape.
  • Recount after degradation and verify the result fits the target or makes measurable progress.
  • If protected content makes the target impossible, return a distinct actionable error instead of emitting another successful truncation event or retrying indefinitely.
  • Keep retries bounded.

Acceptance criteria

  • One-to-four-message histories cannot emit a successful zero-progress truncation result.
  • An oversized tool_use/tool_result pair is reduced without orphaning either side.
  • newContextTokensAfterTruncation is lower than the pre-recovery count whenever recovery reports success.
  • An impossible budget produces a controlled error and does not loop.
  • Focused tests reproduce the 4 MB tool-result case and verify bounded recovery.

Out of scope

  • Preventing individual tools from producing oversized results
  • Condensation-input sanitization
  • Full-output spill files or resource references
  • Aggregate budgeting across parallel tool calls

Estimate

1 story point. Independently mergeable and non-stacked.

Parent tracking issue: #648

Activity

  1. ghost added
    bugSomething isn't working
    on Aug 15, 2026
  2. yetuge commented on Sep 12, 2026

    @yetuge

    Confirmed on current main (745656a): the fallback branch in manageContext (src/core/context-management/index.ts) calls truncateConversation(messages, 0.5), and for short histories the Math.floor((visibleCount - 1) * fracToRemove) + even-rounding calculation yields messagesToRemove = 0. truncateConversation then returns the original messages with messagesRemoved: 0 but a fresh truncationId, so manageContext reports a success-shaped recovery, Task.ts emits the sliding_window_truncation event with messagesRemoved: 0, the oversized history is left unchanged, and the next request retries into the same over-budget failure — a zero-progress loop.

    I'd like to take this. Plan:

    1. In manageContext's fallback branch, treat messagesRemoved === 0 as zero progress: degrade by shrinking the largest eligible textual tool_result block (keeping its tool_use_id and block shape, so the tool_use/tool_result pair is never orphaned), then recount and report success only when newContextTokensAfterTruncation actually decreases.
    2. When nothing eligible can shrink further (protected content makes the budget impossible), return a controlled error/errorDetails result instead of emitting another successful truncation event.
    3. Add focused vitest coverage: a short history with an oversized tool_result that reproduces the zero-progress result on current code, plus bounded-recovery and impossible-budget cases.
  3. yetuge commented on Sep 12, 2026

    @yetuge

    Claiming per the contribution guide.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions