feat(context): bound per-tool output before it enters agent history - #880
Merged
Conversation
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Contributor
Greptile SummaryThe PR restores bounded per-tool output before results enter persistent agent history.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (6): Last reviewed commit: "fix(context): reject tool-output byte ce..." | Re-trigger Greptile |
This was referenced Jul 25, 2026
Cap the size of every tool result so a single verbose command (recursive find, noisy scanner, full page dump) can't pin the conversation near the model's context window for the rest of a scan. - New ContextSettings config group with env-tunable caps. - Default the SDK shell tools' max_output_tokens so exec_command / write_stdin truncate head+tail instead of returning unbounded output. - Bound Strix's own FunctionTool/CustomTool results (line + UTF-8 byte head+tail preview with a truncation notice) and cap error strings.
…ines 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.
devin-ai-integration
Bot
force-pushed
the
agent/tool-output-bounding
branch
from
July 25, 2026 22:33
2b43ccf to
dab93bc
Compare
Contributor
Author
Chat-completions mode converts filesystem CustomTools to FunctionTools (which bounds their result), but the Responses-API path kept them native and unbounded, so a large read_file could still exhaust the context window. Always configure the Filesystem capability to head+tail bound tool output in both modes.
Contributor
Author
The head+tail slices could each take half of max_bytes, then the truncation notice and its separators were appended on top, so the value persisted to history could exceed the configured maximum. Reserve an upper bound for the notice (and separators) out of the byte budget before slicing so the whole joined result stays within max_bytes.
Contributor
Author
Contributor
Author
A configured tool_output_max_bytes smaller than the truncation notice itself can't fit a bounded preview, so a persisted result could exceed the ceiling. Enforce a config floor (ge=1024) so nonsensical values are rejected at load time instead of being worked around at runtime.
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Restores the pre-v1 per-tool-output truncation that was lost in the OpenAI-Agents-SDK migration. Today a single verbose tool result (a recursive
find, a noisy scanner, a full page dump) is appended raw to theSQLiteSessionand replayed on every subsequent turn — pinning the conversation near the model's context window for the rest of the scan (and, once the window is exceeded, causing a hard failure since there's no compaction). This PR caps each result before it enters history.This is layer 1 of a two-layer context-management effort; a follow-up will add provider-agnostic conversation compaction (the summarizer removed in
369fa56). This PR is independently useful and low-risk: caps are generous, so normal outputs are untouched.Three parts:
1. New
ContextSettingsconfig group (strix/config/settings.py) — all env-tunable, with headroom for the upcoming compaction layer:2. Default the SDK shell tools' own cap (
factory.py).exec_command/write_stdinalready acceptmax_output_tokensand truncate head+tail internally, but Strix never set it (⇒None⇒ unbounded). We now inject the configured default when the model omits it, and respect an explicit model-supplied value:3. Bound Strix's own tool results (
strix/tools/output_store.py+factory.py). EveryFunctionTool/CustomToolresult (and capped error strings) passes throughbound_text, which returns the text unchanged when small, else a head+tail preview joined by a notice recording what was dropped:Truncation triggers on whichever limit (line count or UTF-8 byte size) is hit first, and the byte split is multibyte-safe (never cuts a character in half). The base-tool wrapper is idempotent (base tools are shared singletons reused across every agent build).
Test plan
tests/test_output_store.py: passthrough for small output, head+tail on line limit, byte-limit enforcement on a single long line, multibyte safety, dropped-count notice.tests/test_agent_factory_shell.py: asserts the injectedmax_output_tokensdefault and that an explicit value is preserved.uv run pytestgreen (2 pre-existing unrelated failures intest_runner_root_prompt.pyexist onmaintoo — a brokenSimpleNamespacesettings mock). ruff + mypy + bandit pass via pre-commit.Link to Devin session: https://app.devin.ai/sessions/3461e7dce476402aa2091e4af3f55c00
Requested by: @0xallam