fix(browser): add compositor-level coordinate click to browser_click (fixes React onChange/onClick) - #62991
Conversation
…(fixes React onChange/onClick) browser_click only supported ref-based clicks, which dispatch synthetic DOM events that React SPAs do not observe for onChange/onClick handlers (NousResearch#55714). Ref-based clicks driven through agent-browser send programmatic .click()/.fill() events that never fire the native low-level input events React's event system listens for. Add an optional (x, y) viewport-coordinate mode that dispatches real compositor-level mouse events via CDP Input.dispatchMouseEvent. Chrome then performs its own hit-testing and emits genuine native events, so React state updates the same way a real user click does. Closes NousResearch#55714. - browser_click signature now accepts ref OR (x + y), mutually exclusive - CDP path: single WS connection, cached page session, pipelined mousePressed + mouseReleased - agent-browser mouse move/down/up fallback when no CDP endpoint exists - input validation: rejects ref+coords, lopsided coords, or neither - 24 new tests covering validation, CDP dispatch, and fallback Prior art: NousResearch#19189 (closed-unmerged) implemented the same mechanism; this re-lands it on current main minus the supervisor-WS optimization path.
…lback - Key _CDP_SESSION_CACHE by (ws_url, task_id) so concurrent tasks sharing one browser/CDP endpoint don't collide on a cached session id - Resolve CDP endpoint before the websockets availability check, so the agent-browser mouse fallback still works when no CDP endpoint is configured (previously errored out if 'websockets' was absent even without CDP) - Await both mousePressed and mouseReleased acks in the pipelined CDP click so a failed press is surfaced instead of silently masked - Update cache-key tests to the new (ws_url, task_id) shape
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused reproduction and coverage. The coordinate path is not mergeable as written.
Problems
tools/browser_tool.py:3337-3346returns the coordinate dispatch before_blocked_private_page_action. Current main applies that guard tobrowser_clickattools/browser_tool.py:3035-3038; its helper (tools/browser_tool.py:3253-3267) blocks input on private/internal cloud pages. Coordinate clicks would bypass that boundary.- The added model-facing
x/yschema (tools/browser_tool.py:1851-1865) conflicts with the prior maintainer direction in #19189, which explicitly declined coordinate UX for models. This needs a maintainer design decision before salvage.
Suggested changes
- Put both click modes behind the existing private-page action guard and add a regression test proving a blocked coordinate click sends no CDP or agent-browser input.
- Confirm the intended public API before extending the model schema.
Automated hermes-sweeper review.
| "error": "Provide either 'ref' (element reference) or 'x'+'y' (viewport coordinates).", | ||
| }, ensure_ascii=False) | ||
|
|
||
| # --- Coordinate-based click (compositor-level) -------------------------- |
There was a problem hiding this comment.
This early coordinate branch bypasses the private-page action guard used by the existing ref path below (_blocked_private_page_action at lines 3360-3363). Move the guard before either dispatch mode and add a test where the private-page guard is active so no CDP or agent-browser input is sent.
…NousResearch#62991 review) The maintainer review rejected the model-facing x/y coordinate schema (NousResearch#19189 already declined coordinate UX) and flagged that the coordinate path bypassed the private-page action guard. Rework browser_click to: - ref-only model API (no x/y schema) — matches the maintainer's stated direction (port always-CDP-able tools, not a coordinate interface) - dispatch a real compositor-level (native, trusted) click so React onChange/onClick fire: resolve the ref's bounding box via agent-browser get box, then Input.dispatchMouseEvent at the center over CDP, falling back to agent-browser mouse move/down/up when no CDP endpoint is configured - keep the existing plain ref click as a graceful fallback when the box cannot be resolved (no regression) - run the private-page action guard FIRST, wrapping all click dispatch (fixes the review's Finding 1) Updates tests: schema is ref-only; adds a guard-blocks-click regression test; native CDP + agent-browser mouse paths verified at the element center; box-resolution failure degrades to plain ref click.
|
@teknium1 — reworked per the review. Both findings addressed: Finding 1 (private-page guard bypass): Fixed. Finding 2 (coordinate UX declined): Agreed — dropped the model-facing What the rework actually does (fixes #55714): a
The previously-merged supervisor-WS pattern (#23226) isn't required for this — it works with the existing per-call CDP connect (same session-cache + stale-session-reattach logic). If you'd prefer this routed through the supervisor WS once that path is established, happy to follow that up, but it's not a prerequisite for the native-click fix. Tests: 100 passing across the browser click / camofox / private-page-guard / lightpanda suites. (The 5 failing I also updated the linked issue #55714's comment to point at this revised approach once it lands. Happy to adjust if you'd rather the native dispatch live behind a different seam. |
Address two SHOULD-FIXes from cross-vendor review (Flash + GPT-OSS): - schema: mark 'ref' as required so the model can't call browser_click with no arguments at the API layer (matches the runtime check that already existed inside the function) - _resolve_ref_box: return None if width or height is missing or <= 0, or if x/y keys are absent in the dict/string payload — so a malformed bounding box falls back to the plain 'click <ref>' path instead of dispatching a click to the top-left corner (or worse, x=0, y=0) Adds test_missing_size_falls_back covering the zero-size case. Updates the schema 'required' test to assert ['ref'].
|
Update — pushed a follow-up commit addressing two SHOULD-FIXes from a cross-vendor pass (Gemini 3.5 Flash + GPT-OSS 120B), both reviewers agreed:
No other findings. The earlier finding that the
|
Summary
browser_clickonly supported ref-based clicks, which dispatch synthetic DOM events that React SPAs do not observe foronChange/onClickhandlers. Ref-based clicks driven through agent-browser send programmatic.click()/.fill()events that never raise the native low-level input events React's event system listens for — so checkboxes don't toggle and submit buttons don't fire. This is the root cause reported in #55714.This PR adds an optional
(x, y)viewport-coordinate mode that dispatches real compositor-level mouse events via CDPInput.dispatchMouseEvent. Chrome performs its own hit-testing and emits genuine native events, so React state updates exactly as it does for a real user click.Closes #55714
Prior art: #19189 implemented the same mechanism but was closed without merging (2026-05-10). This re-lands it on current
main, minus the supervisor-WS optimization path (that method has since diverged onmain; omitted to keep this fix minimal and conflict-free — can follow up as a separate perf PR).Changes
browser_clicksignature now acceptsrefOR(x + y), mutually exclusiveTarget.getTargets+Target.attachToTarget), pipelinedmousePressed+mouseReleased, stale-session self-healmouse move/down/upwhen no CDP endpoint is configuredx/yparams documented;requiredremoved (one of ref/coords must be supplied)Test plan
tests/tools/test_browser_coordinate_click.pycovering input validation, CDP dispatch via a mock CDP server, and the agent-browser fallback — all passtest_browser_camofox.py,test_browser_private_page_action_guard.py,test_browser_lightpanda.py(83 passed)x/yand validation returns correct errorsNotes / Open question
This fix is opt-in: the agent must be steered to click by coordinates (via
browser_visionwithannotate=trueorbrowser_consolegetBoundingClientRect()) rather than by ref. The default ref-based path remains synthetic and still won't fire React handlers. A fuller fix for the reported flow would make the ref-based click itself resolve the element's bounding box and dispatch a native CDP click — but that's a larger behavioral change (and risks breaking non-React sites that rely on the synthetic path). I scoped this PR to the proven coordinate mechanism so #55714 has a concrete, tested fix. Recommend we decide in review whether to also upgrade the ref path.