feat(agent): actuate CUA clicks as touch on mobile sessions
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:
| Actuation | Result |
|---|---|
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 understudyPage— a trusted touch tap viaInput.dispatchTouchEvent, mirroring the existing coordinatepage.click. - In
V3CuaAgentHandler, route theclickaction topage.tapwhen the session presents as mobile, detected once per run vianavigator.userAgentData.mobile. Desktop sessions are unchanged (stillpage.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
clickaction 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.agentConfigoption) 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 extendpage.tapto resolve+return the xpath if that matters.
Test plan
pnpm --filter @browserbasehq/stagehand typecheck— cleanprettier --check/eslinton 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 })andlocator.tap(); registeredtapin the act method map.tapupdates the cursor overlay and returns XPath for deterministic replay. - On mobile (
navigator.userAgentData.mobile, cached), only a single left click (count = 1) becomestap; right/middle/multi-clicks stay mouse. Recording stores atapstep with XPath.
- Added
-
Bug Fixes
- Pipelined
touchStart+touchEndviaPromise.allto avoid long-press on slow sessions. taperrors now throwUnderstudyCommandException(notStagehandClickError) for correct classification.
- Pipelined