Skip to content

fix(agent): tell the model that navigating a page is not reading it#5765

Open
MummIndia wants to merge 1 commit into
odysseus-dev:devfrom
MummIndia:fix-browser-read-guidance
Open

fix(agent): tell the model that navigating a page is not reading it#5765
MummIndia wants to merge 1 commit into
odysseus-dev:devfrom
MummIndia:fix-browser-read-guidance

Conversation

@MummIndia

Copy link
Copy Markdown

Summary

browser_navigate returns the page title and a snapshot reference. It never returns
the page text. Asked to open a URL and summarise it, the agent called
browser_navigate, saw exit_code=0, answered "the page loaded successfully" and
stopped — having read nothing. The turn looks successful and the tool log agrees, so
nothing flags it.

browser_snapshot has the same trap one level down: its optional filename argument
is documented as "Save snapshot to markdown file instead of returning it in the
response"
, and the model volunteers one. It then receives a path it cannot open —
175 characters of reference where omitting the argument returns 1634 of content.

This adds a note covering both. Navigation is never the last step, follow it with
browser_snapshot or browser_find; call snapshot with no arguments; and prefer
web_fetch when the task is only to read a page, since it does the whole thing in
one call rather than three round trips — which matters on local hardware where each
round trip is a full generation.

The note is injected only when browser tools are in that turn's selection, and is
suppressed alongside the other local-context blocks, so it costs nothing on unrelated
requests.

Target branch

  • This PR targets dev, not main.

Linked Issue

Fixes #5764

Type of Change

  • Bug fix (non-breaking — fixes a confirmed issue)

Checklist

  • I searched open issues and open PRs — this is not a duplicate.
  • This PR targets dev
  • My changes are limited to the scope described above — no unrelated refactors or whitespace changes mixed in.
  • I actually ran the app (docker compose up) and verified the change works end-to-end.

How to Test

  1. Run with the built-in browser MCP server connected, in agent mode.

  2. Send: Open https://example.com and summarise what this page is about.

  3. Before the change — the agent calls browser_navigate, then ends the turn with
    a message along the lines of "the page loaded successfully", without answering the
    question. The round summary reads 0 native calls, 0 tool blocks.

  4. After the change — the agent follows browser_navigate with browser_snapshot
    and answers from the page content. Confirmed in the logs:

    Tool executed: mcp__builtin_browser__browser_navigate -> exit_code=0
    Tool executed: mcp__builtin_browser__browser_snapshot -> exit_code=0
    
  5. To check the conditional injection, build the system prompt with and without a
    browser tool in relevant_tools. The READING A WEB PAGE block is present in the
    first case and absent in the second.

Visual / UI changes

None. This PR only touches src/agent_loop.py — prompt text assembled server-side.
No HTML, CSS, SVG or static/js/ module is modified.

browser_navigate returns the page title and a snapshot reference. It
never returns the page text. Asked to open a URL and summarise it, the
agent called browser_navigate, saw exit_code=0, answered "the page
loaded successfully" and stopped -- having read nothing. It looks like
a successful turn, and the tool log agrees, so nothing flags it.

browser_snapshot has the same trap one level down. Its optional
filename argument is documented as "Save snapshot to markdown file
instead of returning it in the response". The model volunteered a
filename and so received a path it had no way to open: 175 characters
of reference where omitting the argument returns 1634 of content.

Adds a note covering both. Navigate is never the last step, follow it
with browser_snapshot or browser_find; call snapshot with no arguments;
and prefer web_fetch when the task is only to read a page, since it
does the whole thing in one call rather than three round trips -- which
matters on local hardware where each round trip is a full generation.

Injected only when browser tools are in that turn's selection, and
suppressed with the other local-context blocks, so it costs nothing on
unrelated requests. Verified present with browser tools in the set and
absent without.

Observed after the change: the agent that previously stopped at
"the page loaded" went on to call browser_snapshot on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the ready for review Description complete — ready for maintainer review label Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Description complete — ready for maintainer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent stops after browser_navigate without reading the page

1 participant