SIGN IN SIGN UP

AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.

0 0 73 Python

fix(backend/copilot): make SDK tool-result reads navigable + redirect wrong-tool access (#13179)

## Why

Production session
[`3304a1df-30ab-446d-8841-bce1a41ab8fa`](https://dev-builder.agpt.co/copilot?sessionId=3304a1df-30ab-446d-8841-bce1a41ab8fa)
hit a post-execution analysis dead-end. After `run_agent` came back with
a failed Orchestrator node, the model:

1. Called `read_tool_result` and got the file contents back
successfully.
2. Tried to `bash_exec` `cat
/root/.claude/projects/.../tool-results/toolu_*.json | python3 -c '...'`
to parse the nested `node_executions[*].error` field — got `Permission
denied` because the bash sandbox can't reach the SDK's host-side
tool-results dir.
3. Tried `read_workspace_file(path="tool-outputs/toolu_*.json")` — got
`Path not allowed` because that's not workspace storage.
4. Gave up and shipped the user a guess about what failed.

Underneath, three problems compounded:

- The file on disk is the MCP envelope
`[{"type":"text","text":"<minified-json>"}]`. `read_tool_result`'s
line-based `offset`/`limit` sliced the envelope wrapper (≈5 lines), not
the payload inside (one giant minified line) — so even when the model
used the right tool, it couldn't *slice* the right tool's output.
- `bash_exec` had no awareness of host-side SDK paths, so the model
could only learn "wrong tool" via repeated `Permission denied` retries.
- `read_workspace_file` returned a generic `Path not allowed` that
didn't point at the actual right tool.

## What

Fix all three from different angles in one PR:

- **`read_tool_result`** auto-unwraps `[{"type":"text","text":...}]` and
pretty-prints inner JSON so line-based `offset`/`limit` slice the
*payload*. Also adds `char_offset`/`char_limit` for byte-precise reads
when even pretty-printed lines are too long (base64 blobs, token-dump
strings).
- **`bash_exec`** pre-scans the command for SDK tool-result paths and
short-circuits with a redirect error pointing at `read_tool_result`
*and* the existing `@@agptfile:<abs-path>[<s>-<e>]` reference protocol —
so the model can pipe a slice through bash without ever touching the
host path.
- **`read_workspace_file`** returns the same redirect hint when the path
looks like an SDK tool-result shorthand (e.g. `tool-outputs/<id>.json`).

## How

- `copilot/context.py` — new `looks_like_sdk_tool_result_path()`
substring-detector (matches `/.claude/projects/`, `tool-results/`,
`tool-outputs/`) and `sdk_tool_result_redirect_hint()` which builds the
consistent redirect message used by both wrong-tool sites.
- `copilot/sdk/tool_adapter.py::_read_file_handler` — read the whole
file, call new `_navigable_tool_result_text()` to unwrap+pretty-print,
then slice either line- or char-mode. `_navigable_tool_result_text` is
defensive: non-envelope JSON, non-JSON inner text, and multi-block
envelopes all fall through to the raw content untouched.
- `copilot/tools/bash_exec.py` — pre-flight check before
`get_current_sandbox()`; emits
`ErrorResponse(error="sdk_tool_result_path_in_bash_command")` so the
failure is observable in metrics.
- `copilot/tools/workspace_files.py` — second branch in the existing
`_resolve_file → ErrorResponse` fallback.

Tool description for `read_tool_result` updated to advertise both the
unwrap behavior and the `@@agptfile:` alternative.

## Test plan

- [x] `TestNavigableToolResultText` (3 cases) — envelope + JSON gets
pretty-printed; envelope + non-JSON gets unwrapped; non-envelope inputs
returned unchanged.
- [x] `TestBashExecSdkToolResultRedirect` (3 cases) — absolute SDK path
redirects; relative `tool-outputs/` shorthand redirects; normal commands
still execute against the sandbox.
- [x] `pytest backend/copilot/sdk/tool_adapter_test.py
backend/copilot/tools/bash_exec_test.py
backend/copilot/tools/workspace_files_test.py` — all green
- [ ] `/pr-test` against this branch to confirm the production session
shape (run_agent → view failed → slice payload) no longer churns

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Z
Zamil Majdy committed
7c23c878ffc9256cf13a56b63afcbc111b2f73f7
Parent: 6a6f2d2
Committed by GitHub <noreply@github.com> on 5/21/2026, 4:38:33 PM