fix(proxy/interceptors): validate truncation window length, not absolute end index

_is_plausible_truncation_range compared end_line directly against the
payload's line count, which only holds when a read starts at line 1. For
an offset read (e.g. "showing lines 100-148 of 300"), the payload only
contains that window's ~49 lines, so the check silently rejected a
genuinely truncated, correctly-formatted banner.

Validate the inclusive window length (end_line - start_line + 1) instead,
which is invariant to where the window starts. Also collapses
test_astgrep_flags_truncated_read onto the _read_result_messages() helper
introduced later in the same file, removing a leftover duplicate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
slxng1758 2026-08-26 20:16:21 -04:00
parent 2432a43d85
commit ed67f5bd9f
2 changed files with 34 additions and 18 deletions

View file

@ -111,7 +111,8 @@ def _is_plausible_truncation_range(
# end_line == total_lines means the whole file was shown, not truncated.
if start_line < 1 or end_line < start_line or end_line >= total_lines:
return False
return end_line <= source_line_count
window_length = end_line - start_line + 1
return window_length <= source_line_count
def _detect_truncation(source: str) -> tuple[int, int] | None:

View file

@ -221,23 +221,7 @@ def test_astgrep_flags_truncated_read(tokenizer):
"showing lines 1-42 of 90 total (26031 tokens, cap 25000). "
"Call Read with offset=43 to see more.]\n"
)
messages = [
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "abc",
"name": "Read",
"input": {"file_path": "/repo/payments.py"},
}
],
},
{
"role": "user",
"content": [{"type": "tool_result", "tool_use_id": "abc", "content": truncated_source}],
},
]
messages = _read_result_messages(truncated_source)
result = apply_to_messages(messages, tokenizer)
assert len(result.spans) == 1
new_content = result.messages[1]["content"][0]["content"]
@ -343,6 +327,37 @@ def test_astgrep_accepts_truncation_at_start_equals_end(tokenizer):
assert "showing through line 42 of 90 total" in new_content
def test_astgrep_flags_truncated_read_offset_window(tokenizer):
"""A legitimate offset read (not starting at line 1) must still be
recognized -- window length, not the absolute end index, is what's
checked against the visible payload."""
truncated_source = (
_PY_FIXTURE + "\n\n[Truncated: PARTIAL view — /repo/payments.py: "
"showing lines 100-148 of 300 total (26031 tokens, cap 25000). "
"Call Read with offset=149 to see more.]\n"
)
messages = _read_result_messages(truncated_source)
result = apply_to_messages(messages, tokenizer)
new_content = result.messages[1]["content"][0]["content"]
assert "truncated upstream" in new_content
assert "showing through line 148 of 300 total" in new_content
def test_astgrep_ignores_offset_window_exceeding_visible_payload(tokenizer):
"""An offset window is still checked for plausibility -- a claimed
window far longer than what's actually visible must not trigger."""
truncated_source = (
_PY_FIXTURE + "\n\n[Truncated: PARTIAL view — /repo/payments.py: "
"showing lines 100-5148 of 9999 total (26031 tokens, cap 25000). "
"Call Read with offset=5149 to see more.]\n"
)
messages = _read_result_messages(truncated_source)
result = apply_to_messages(messages, tokenizer)
new_content = result.messages[1]["content"][0]["content"]
assert "outlined by ast-grep" in new_content
assert "truncated upstream" not in new_content
def test_astgrep_skips_small_files(tokenizer):
small = "def foo(): return 1\n"
messages = [