fix(context): clamp shell output cap and count byte-trimmed dropped lines

Treat tool_output_max_tokens as a ceiling so an explicit model-supplied
cap can't exceed it, and derive the truncation notice's dropped-line
count from the lines actually kept after the byte-trim pass. Also cast
the pygments fallback lexer so it satisfies the resolve_lexer return
type under the pre-commit mypy hook.
This commit is contained in:
Ahmed Allam
2026-07-25 22:32:56 +00:00
parent 3b8980c47b
commit dab93bcc12
5 changed files with 51 additions and 10 deletions
+15 -1
View File
@@ -41,7 +41,7 @@ async def test_wrap_exec_command_defaults_shell_to_bash() -> None:
@pytest.mark.asyncio
async def test_wrap_exec_command_preserves_explicit_output_cap() -> None:
async def test_wrap_exec_command_preserves_smaller_explicit_output_cap() -> None:
captured: dict[str, str] = {}
wrapped = factory._wrap_exec_command(_capturing_exec_tool(captured))
@@ -52,6 +52,20 @@ async def test_wrap_exec_command_preserves_explicit_output_cap() -> None:
assert json.loads(captured["raw_input"])["max_output_tokens"] == 42
@pytest.mark.asyncio
async def test_wrap_exec_command_clamps_oversized_explicit_output_cap() -> None:
captured: dict[str, str] = {}
wrapped = factory._wrap_exec_command(_capturing_exec_tool(captured))
ceiling = load_settings().context.tool_output_max_tokens
await wrapped.on_invoke_tool(
cast("Any", None),
json.dumps({"cmd": "echo hi", "max_output_tokens": ceiling * 100}),
)
assert json.loads(captured["raw_input"])["max_output_tokens"] == ceiling
@pytest.mark.asyncio
@pytest.mark.parametrize("shell", ["/bin/zsh", ""])
async def test_wrap_exec_command_preserves_explicit_shell(shell: str) -> None:
+17
View File
@@ -2,6 +2,8 @@
from __future__ import annotations
import re
from strix.tools.output_store import bound_text
@@ -44,3 +46,18 @@ def test_notice_reports_dropped_counts() -> None:
assert "lines" in bounded
assert "bytes" in bounded
def test_dropped_line_count_accounts_for_byte_trimming() -> None:
# A tight byte budget forces the byte pass to drop whole lines from the
# head/tail slices; the notice must count those, not just the middle.
text = "\n".join(f"line-{i}" for i in range(200))
bounded = bound_text(text, max_lines=20, max_bytes=40)
match = re.search(r"\[\.\.\. (\d+) lines", bounded)
assert match is not None, bounded
dropped = int(match.group(1))
kept = [ln for ln in bounded.splitlines() if ln and "truncated" not in ln]
assert dropped == 200 - len(kept)
# The naive middle-only count (max_lines split evenly) would under-report.
assert dropped > 200 - 20