feat(agent): actuate CUA clicks as touch on mobile sessions

Closed
AL
2mo ago
+25-17

Problem

The computer-use agent actuates every pointer action through Input.dispatchMouseEvent (via page.click(x, y) in V3CuaAgentHandler). On a mobile session the browser renders the site's touch-gated mobile layout, whose handlers listen for touch/pointer events (pointerType: "touch") — a synthesized mouse click doesn't register there.

Concrete repro on adidas.co.il (Browserbase browserSettings.verified: true, os: "mobile"), same product/size, empty cart:

ActuationResult
trusted touch (Input.dispatchTouchEvent)added to cart ✅
mouse click (Input.dispatchMouseEvent)blocked — "אנא בחרו מידה" / "please choose a size"
CUA hybrid agent (mouse)blocked — size highlights but never registers; add-to-cart errors

The size visually highlights but is never registered, so add-to-cart fails. This blocks mobile e-commerce flows (size selectors, add-to-cart) on any touch-gated layout.

Fix

  • Add a coordinate page.tap(x, y) to the understudy Page — a trusted touch tap via Input.dispatchTouchEvent, mirroring the existing coordinate page.click.
  • In V3CuaAgentHandler, route the click action to page.tap when the session presents as mobile, detected once per run via navigator.userAgentData.mobile. Desktop sessions are unchanged (still page.click).

Detection note: Browserbase os: "mobile" reports maxTouchPoints: 0 and pointer: fine, but navigator.userAgentData.mobile === true — so userAgentData.mobile is the reliable signal (verified on live sessions). The trusted CDP touch works even though the context doesn't advertise touch.

Scope / open questions for maintainers

  • Only the click action is routed. double_click (and drag) could get the same treatment if desired.
  • Detection is a runtime heuristic (userAgentData.mobile). Happy to switch to an explicit config flag (e.g. agentConfig option) if you'd prefer opt-in over auto-detect.
  • Recording/replay for taps: the touch path returns no xpath (unlike page.click(returnXpath)), so a tapped step isn't captured for replay. Can extend page.tap to resolve+return the xpath if that matters.

Test plan

  • pnpm --filter @browserbasehq/stagehand typecheck — clean
  • prettier --check / eslint on changed files — clean
  • Manual: on a verified + os:"mobile" Browserbase session, the CUA agent selects a size and adds to cart on adidas.co.il (previously blocked with mouse).

🤖 Generated with Claude Code


Summary by cubic

Actuate CUA clicks as real touch on mobile sessions so touch-gated UIs register correctly. Routes a single left click to a trusted tap and records it for replay; other click types are unchanged.

  • New Features

    • Added page.tap(x, y, { returnXpath }) and locator.tap(); registered tap in the act method map. tap updates the cursor overlay and returns XPath for deterministic replay.
    • On mobile (navigator.userAgentData.mobile, cached), only a single left click (count = 1) becomes tap; right/middle/multi-clicks stay mouse. Recording stores a tap step with XPath.
  • Bug Fixes

    • Pipelined touchStart + touchEnd via Promise.all to avoid long-press on slow sessions.
    • tap errors now throw UnderstudyCommandException (not StagehandClickError) for correct classification.