Skip to content

agent-runtime: stub old tool results in history instead of carrying them forever - #1586

Merged
obadakhalili merged 1 commit into
CodebuffAI:mainfrom
Salhi-zh:runtime/simplify-old-tool-results
Oct 30, 2026
Merged

obadakhalili merged 1 commit into
CodebuffAI:mainfrom
Salhi-zh:runtime/simplify-old-tool-results

Conversation

@Salhi-zh

Copy link
Copy Markdown
Contributor

What

read_files and run_terminal_command results (up to 100k/50k chars each) stayed verbatim in messageHistory and were re-sent to the model on every step until compaction at ~80% context. Cost grows quadratically with session length: a 30-step session with 20 reads re-ships megabytes of untouched content per step.

The simplifier helpers for this already existed (simplifyReadFileResults / simplifyTerminalCommandResults, the same omit stubs fitToolResults and getPreviouslyReadFiles already consume) but had zero production callers — only tests. This PR wires them into the main loop.

Change

runAgentStep now runs simplifyOldToolResults over the expired, orphan-free history each step (right next to expireMessages). Results older than the newest keep window (5 terminal / 3 read_files) become the omit stubs: { path, contentOmittedForLength: true } / { command, stdoutOmittedForLength: true, exitCode? }.

Safety:

  • Pure copy-on-write: stubbed messages are new objects; everything else keeps reference identity, and the input array is returned by reference when nothing stubbed — so the inputHistory.every((m, i) => history[i] === m) extension check and SDK snapshots (which share message objects) see exactly the pass's edits.
  • Never stubs: the newest window, terminal background/error results, already-stubbed entries, media-carrying reads. Malformed results log and stay verbatim.
  • Savings floor (20k chars): the pass only stubs once past-window volume crosses 20k chars. Below that, verbatim results keep fitToolResults' graded prefix-clip (with offset/limit re-read hints) available on overflow — a stub has nothing left to clip.

Quality: stubbed reads can be re-read exactly via offset/limit (the stub tells the model so — same phrasing fitToolResults uses); re-running stubbed commands is never suggested.

Tests

8 new unit tests (window semantics, per-tool independence, passthrough classes, reference identity, savings floor, override) + a loop-level test proving three oldest bulky reads are stubbed in both the request and the stored history while newest-3 stay verbatim. Suite: 975/976 (one pre-existing base failure, verified unrelated), typecheck clean.

…hem forever

read_files and run_terminal_command results (up to 100k/50k chars each)
stayed verbatim in messageHistory and were re-sent to the model on every
step until compaction at ~80% context. Cost grew quadratically with
session length; a 30-step session with 20 reads reshipped megabytes of
untouched content per step.

runAgentStep now runs simplifyOldToolResults over the expired, orphaned-
call-free history each step (next to expireMessages). Results older than
the newest keep window (5 terminal, 3 read_files) become the omit stubs
fitToolResults and getPreviouslyReadFiles already consume:
{ path, contentOmittedForLength: true } / { command, stdoutOmittedForLength:
true, exitCode? }. Terminal background-process and error results, already-
stubbed entries, and media-carrying read results never stub and never
consume window slots, matching simplifyTerminalHelper's baseline in
util/messages.ts.

Safety: the pass is pure copy-on-write. Stubbed messages are new objects;
everything else keeps reference identity, and the input array is returned
unchanged (same reference) when nothing stubbed, so the
inputHistory.every((m, i) => history[i] === m) extension check in
runAgentStep and shared history snapshots see exactly the pass's edits.
Newest results and anything the in-flight step appends later are
untouched. Malformed results log and stay verbatim.

Quality: the pass waits until past-window results carry at least 20k
chars before stubbing anything. Below that the savings don't justify
dropping content the model may still re-read, and a verbatim result keeps
fitToolResults' graded prefix-clip (with re-read offset/limit hints)
available on overflow, while a stub has nothing left to clip. The stubs
themselves preserve every read path, and a stubbed read can be re-read
exactly with offset/limit; a re-run of a stubbed command is never
suggested.
@obadakhalili
obadakhalili merged commit 7e83dbd into CodebuffAI:main Oct 30, 2026
1 of 2 checks passed
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.

2 participants