← Back to PRs

#20076: feat(tool-truncation): use head+tail strategy to preserve errors during truncation

by jlwestsr open 2026-02-18 13:48 View on GitHub →
agents size: S
## Problem When tool results exceed the character limit, `truncateToolResultText` keeps the head and discards everything after the cutoff. Error messages, stack traces, and result summaries typically appear at the **end** of tool output — exactly what gets truncated. This means the model loses the most actionable part of the output (the error) and is left with only the preamble. ## Solution Introduced a head+tail truncation strategy: 1. **Detect important tails** — checks the last ~2000 chars for error-like patterns (`error`, `exception`, `traceback`, `panic`, etc.), JSON closing structure, or summary keywords 2. **Split the budget** — when important tail content is detected, allocates 70% to head and 30% to tail (capped at 4000 chars) 3. **Clean boundaries** — cuts at newline boundaries to avoid mid-line breaks 4. **Clear marker** — inserts a visible `⚠️ [... middle content omitted ...]` marker between head and tail Falls back to the existing head-only strategy when the tail doesn't contain important content. ## Example Before (head-only): ``` $ npm test PASS src/foo.test.ts PASS src/bar.test.ts ... [truncated — 50000 chars] ``` After (head+tail): ``` $ npm test PASS src/foo.test.ts PASS src/bar.test.ts ... ⚠️ [... middle content omitted — showing head and tail ...] FAIL src/baz.test.ts Error: expected 42 but got undefined at Object.<anonymous> (src/baz.test.ts:15:5) Tests: 47 passed, 1 failed ``` ## Testing Added two test cases: - Verifies error content at tail is preserved with the head+tail split - Verifies normal content without errors uses simple head truncation Existing e2e tests pass without modification. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR adds a head+tail truncation strategy to `truncateToolResultText` so that error messages, stack traces, and result summaries at the end of tool output are preserved during truncation instead of being discarded. - Introduces `hasImportantTail()` to detect error-like patterns, JSON structure, or summary keywords in the last ~2000 chars of text - When important tail content is detected, allocates 70% of the budget to head and 30% to tail (capped at 4000 chars), with newline-boundary cuts and a visible omission marker - Falls back to the existing head-only truncation when the tail doesn't contain important content - The size arithmetic is correct — output stays within `maxChars` through proper budget accounting of both the middle omission marker and the truncation suffix - Two new test cases cover the head+tail and head-only paths; existing tests pass unchanged - The `hasImportantTail` detection heuristic is fairly broad (words like "done", "result", "complete" and any trailing `}` will trigger it), which means the head+tail strategy may fire more often than intended, reducing head content by 30% for false positives — this is a non-critical trade-off worth considering tightening over time <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — the size arithmetic is correct, edge cases are guarded, and it falls back gracefully to the existing behavior. - Score of 4 reflects that the implementation is logically sound with correct budget math, proper fallback to head-only truncation, and adequate test coverage. The only concerns are non-critical: the tail-detection heuristic may produce false positives due to broad keyword patterns, which could unnecessarily reduce head content. - No files require special attention — both changed files are straightforward and self-contained. <sub>Last reviewed commit: 75f8e0f</sub> <!-- greptile_other_comments_section --> <sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub> <!-- /greptile_comment -->

Most Similar PRs