Skip to content

fix: size-aware emergency truncation (billion-context-pi#12) - #133

Open
ranxianglei wants to merge 4 commits into
masterfrom
2026-08-23_truncate-size-aware
Open

fix: size-aware emergency truncation (billion-context-pi#12)#133
ranxianglei wants to merge 4 commits into
masterfrom
2026-08-23_truncate-size-aware

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Problem

The emergency truncation (src/truncate-tools.ts) hard-protected the last N messages (protectRecentMessages: 3). This was counterproductive when the most recent tool-results were the largest — e.g. decompress inline:true results.

In the 2026-08-22 overflow (billion-context-pi#12), the model inlined 6 blocks (574 msgs) in one turn at 21% usage. The 6 inlined tool-results were the newest messages, so the last 3 (~100K tokens) fell in the protected zone and were never truncated; only the older 3 were, which was insufficient to bring the context under the limit.

Fix

All tool-results are now candidates for truncation (including recent ones), sorted largest-first. The size-based sort naturally preserves small recent messages and truncates large ones regardless of recency.

protectRecentMessages is kept in the API for backward compatibility but no longer hard-excludes recent messages from candidacy.

Tests

  • Updated tests/truncate-keep-filter.test.ts: the "protects recent messages" test now verifies a large recent message IS truncated (size-aware).
  • All 463 tests pass.

Related

  • billion-context-pi#12 — pi 低上下文溢出, subagent调用失败
  • Companion: billion-context-pi inline-cap fix (decompressInlineMaxChars config)

The emergency truncation hard-protected the last N messages
(protectRecentMessages: 3), which was counterproductive when the most
recent tool-results were the largest — e.g. decompress inline results.
The 6 inlined blocks in the 2026-08-22 overflow were the newest
messages, so the last 3 (~100K tokens) fell in the protected zone and
were never truncated; only the older 3 were, which was insufficient.

Now ALL tool-results are candidates (including recent ones), sorted
largest-first. The size-based sort naturally preserves small recent
messages and truncates large ones regardless of recency.
protectRecentMessages is kept in the API for backward compatibility but
no longer hard-excludes recent messages from candidacy.
…-08-23)

A single 50,358-char bash toolResult (~31,475 tokens, ~24% of the
effective 131,072-token window) entered context whole: it sat under every
byte cap, and the usage-gated valves never fired because the kernel's
own estimate read 51.8% while real usage was >100%. No percentage-gated
valve can catch a single-message spike.

Add truncate.maxToolResultTokens (null = auto: min(10% of
modelContextLimit, 16384); 0 disables) and a tool-result-cap pipeline
node that runs on EVERY turn and ignores recency protection: any
tool-result estimated above the cap (CJK-aware tokenizer, same basis as
the rest of the kernel) is rewritten head+tail with a
`[acp: tool-result truncated, original ~N tokens]` marker. Placed after
prune and before recommend/nudge so downstream token estimates reflect
the capped sent view. Emergency truncation now skips already-capped
texts so the two valves never stack markers.
…mergency re-truncation

Review findings from del_mt5xrwcl_sb29 (2026-08-23):
1. [major] A legitimate tool-result merely QUOTING the cap-marker string
   escaped the hard cap entirely. Skip on marker only while the text still
   fits (<= cap * 1.25 margin for tokenizer drift).
2. [major] Non-finite maxToolResultTokens / modelContextLimit produced a NaN
   cap that replaced EVERY tool-result with the bare marker. Sanitized to
   fall back to auto.
3. [major] Auto cap min(10% x limit, 16384) moved with the learned limit ->
   prefix-cache break per change. Quantized down to powers of two (>=1024
   step), so the truncation point moves only across power-of-two boundaries.
4. [major->minor] Emergency truncation skipped cap-marked messages, making
   them permanently untouchable at >=95% usage. Now re-truncates them
   (stacked markers acceptable: the prefix is broken by design at emergency).
5. toolResultCappedCount now surfaces on ProcessTurnResult for host
   observability; char prefilter skips re-tokenizing small tool-results.
Fractional explicit caps clamp to >=1 (0.5 no longer silently disables).

482/482 tests (18 in toolresult-token-cap, +5 regression tests).
fix: hard per-tool-result token cap (billion-context-pi incident 2026-08-23)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant