You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(agent): expose spilled tool output as an agent-callable read tool #13754
Follows #13692 step 1 (PR #13753), which spills oversized tool output to a run-scoped artifact and leaves a bounded excerpt plus an anchor in context.
Gap
agent_loop/tool_output_spill.read_spilled(anchor) resolves the full output, and the excerpt injected into context tells the model to use it:
Output truncated to 2000 of 78000 chars. Read the full output with the
read_spilled_output tool using anchor 'autobot:spill:...'.
That tool does not exist yet. The instruction names a capability the agent cannot invoke, which is worse than saying nothing — it invites the model to attempt a call that will fail.
Fix
Register a read_spilled_output(anchor: str) tool that returns the full output for an anchor belonging to the current run.
Two constraints worth stating before implementation:
Scope by run. The anchor embeds a task_id. The tool must refuse an anchor from a different run, or one agent could read another's observations. read_spilled currently validates only the anchor shape.
Re-spill the re-read. Returning 78,000 chars into context defeats the offload. The tool should return a window (offset/limit) rather than the whole artifact, so re-reading is a seek, not an undo.
Acceptance criteria
An agent can call the tool named in the excerpt text and get the full output.
An anchor from a different run is refused; a test proves it.
The tool returns a bounded window, with offset/limit, rather than the entire artifact.
The excerpt's instruction text matches the tool's real name and signature.
Confirmed in base rather than assumed: read_spilled_output present in tools/tool_registry.py, read_spilled_window in agent_loop/tool_output_spill.py.
Acceptance criteria
An agent can call the tool named in the excerpt text and get the output.
An anchor from a different run is refused — proven for both read_spilled and read_spilled_window, with a companion test showing the owning run still reads it, so the check is not simply always-deny.
Returns a bounded window with offset/limit — test_paging_reaches_the_end reassembles the original across two pages and asserts has_more clears.
The excerpt's instruction text matches the tool's real name and signature.
The defect worth remembering
Step 1's excerpt told the model to "read the full output with the read_spilled_output tool" — and that tool did not exist. A prompt naming a capability the agent cannot invoke is worse than one that says nothing: it invites a call that fails and teaches the model the anchor is worthless.
The test I would keep if I could keep only one asserts the note's text and hasattr(ToolRegistry, "read_spilled_output") together, because the defect was a drift between what the prompt promised and what existed.
Two constraints made this more than a wrapper, both identified before writing code:
The anchor is a bearer token.read_spilled validated only its shape, so an anchor leaked into another run — a shared log, a copied prompt, a retry — could read that run's observations.
A re-read must be a seek, not an undo. Returning 78,000 chars would put back exactly what the offload removed.
the _get_tool_handler dispatch map — no readspilledoutput key
get_available_tools(), which feeds the tool list the model is shown
_BUILTIN_TOOL_SCHEMAS
So execute_tool("read_spilled_output", ...) normalises to readspilledoutput, matches no handler, and returns {"status": "error", "result": "Unknown tool: ..."}.
Meanwhile the excerpt that replaces spilled output still instructs the model to call it (tool_output_spill.py:127):
Read the full output with the read_spilled_output tool using anchor ...
That is exactly the condition this issue was filed to remove — an excerpt naming a capability the agent cannot invoke. State after #13763 is unchanged from before it.
hasattr on the class passes for any unwired method. The assertion has to be membership in get_available_tools() plus a non-error execute_tool dispatch.
Acceptance criteria (revised)
read_spilled_output is dispatchable through execute_tool and returns a non-error envelope for a valid anchor
it appears in get_available_tools(), so the model is actually offered it
a test asserts dispatch, not hasattr
a test asserts the excerpt note names only tools that are dispatchable
Found by a retrospective review of merges that went in without a review pass.
Delivered in PR #13918, merged 2026-08-10T16:47:45Z. Verified in origin/Dev_new_gui rather than from the merge status:
$ git show origin/Dev_new_gui:autobot-backend/tools/tool_registry.py | grep -c '"readspilledoutput"'
1 # the dispatch-table key execute_tool normalises to
$ git show origin/Dev_new_gui:autobot-backend/tools/tool_registry.py | grep -c 'read_spilled_output'
4 # method, dispatch lambda, envelope, get_available_tools entry
Acceptance criteria:
dispatchable through execute_tool, returning a non-error envelope for a valid anchor
present in get_available_tools()
the test asserts dispatch, not hasattr — the original assertion passed for any unwired method, which is why this issue was closed once before without the capability working
the excerpt note names only dispatchable tools
Scope note — closing on what this issue asked for, not more
ToolRegistry.execute_tool has no production callers; the live seam is chat_workflow/tool_handler.py::_dispatch_tool_call, which still has no branch for this tool. That gap is tracked as #13919 and is deliberately not claimed here.
Closing this rather than leaving it open for #13919 because the acceptance criteria above are met and verified. Saying otherwise would repeat the original mistake in reverse — the first closure claimed a capability that did not exist; leaving it open now would obscure that the registry half genuinely works.
Follow-up defects found in the same mechanism after merge: #13933 (PR #13934).
Activity
feat(agent): expose spilled tool output as a run-scoped, windowed rea…
feat(agent): expose spilled tool output as a run-scoped, windowed rea…
github-actions commentedon Aug 8, 2026
PR #13763 (merged to
Dev_new_gui) references this issue with a close keyword.If this issue is fully resolved, close it manually. If work remains, no action is needed.
mrveiss commentedon Aug 8, 2026
Closed by #13763, squash-merged to
Dev_new_guiasd2386463d.Verification
All 12 required checks green, zero failures — not even the npm-audit suite that has been red on base.
Confirmed in base rather than assumed:
read_spilled_outputpresent intools/tool_registry.py,read_spilled_windowinagent_loop/tool_output_spill.py.Acceptance criteria
read_spilledandread_spilled_window, with a companion test showing the owning run still reads it, so the check is not simply always-deny.test_paging_reaches_the_endreassembles the original across two pages and assertshas_moreclears.The defect worth remembering
Step 1's excerpt told the model to "read the full output with the
read_spilled_outputtool" — and that tool did not exist. A prompt naming a capability the agent cannot invoke is worse than one that says nothing: it invites a call that fails and teaches the model the anchor is worthless.The test I would keep if I could keep only one asserts the note's text and
hasattr(ToolRegistry, "read_spilled_output")together, because the defect was a drift between what the prompt promised and what existed.Two constraints made this more than a wrapper, both identified before writing code:
read_spilledvalidated only its shape, so an anchor leaked into another run — a shared log, a copied prompt, a retry — could read that run's observations.mrveiss commentedon Aug 10, 2026
Reopening — closed on 2026-08-08 by PR #13763, but the capability was never delivered.
Evidence
read_spilled_outputexists only as a method definition:It is absent from every registration point:
_get_tool_handlerdispatch map — noreadspilledoutputkeyget_available_tools(), which feeds the tool list the model is shown_BUILTIN_TOOL_SCHEMASSo
execute_tool("read_spilled_output", ...)normalises toreadspilledoutput, matches no handler, and returns{"status": "error", "result": "Unknown tool: ..."}.Meanwhile the excerpt that replaces spilled output still instructs the model to call it (
tool_output_spill.py:127):That is exactly the condition this issue was filed to remove — an excerpt naming a capability the agent cannot invoke. State after #13763 is unchanged from before it.
Why the test did not catch it
hasattron the class passes for any unwired method. The assertion has to be membership inget_available_tools()plus a non-errorexecute_tooldispatch.Acceptance criteria (revised)
read_spilled_outputis dispatchable throughexecute_tooland returns a non-error envelope for a valid anchorget_available_tools(), so the model is actually offered ithasattrFound by a retrospective review of merges that went in without a review pass.
fix(agent): make the spill safe to enable and the read tool reachable (…
fix(agent): make the tool-output spill safe to enable, and actually d…
mrveiss commentedon Aug 10, 2026
Delivered in PR #13918, merged 2026-08-10T16:47:45Z. Verified in
origin/Dev_new_guirather than from the merge status:Acceptance criteria:
execute_tool, returning a non-error envelope for a valid anchorget_available_tools()hasattr— the original assertion passed for any unwired method, which is why this issue was closed once before without the capability workingScope note — closing on what this issue asked for, not more
ToolRegistry.execute_toolhas no production callers; the live seam ischat_workflow/tool_handler.py::_dispatch_tool_call, which still has no branch for this tool. That gap is tracked as #13919 and is deliberately not claimed here.Closing this rather than leaving it open for #13919 because the acceptance criteria above are met and verified. Saying otherwise would repeat the original mistake in reverse — the first closure claimed a capability that did not exist; leaving it open now would obscure that the registry half genuinely works.
Follow-up defects found in the same mechanism after merge: #13933 (PR #13934).