Skip to content

Contrib/2384#2385

Draft
miguelg719 wants to merge 8 commits into
mainfrom
contrib/2384
Draft

Contrib/2384#2385
miguelg719 wants to merge 8 commits into
mainfrom
contrib/2384

Conversation

@miguelg719

@miguelg719 miguelg719 commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

why

what changed

test plan


Summary by cubic

Route both CUA and hybrid coordinate clicks to trusted touch taps on touch-capable sessions to fix touch-gated mobile UIs, with a config-driven useTouch toggle and deterministic tap record/replay.

  • New Features

    • Added useTouch option and v3.usesTouch resolution; explicit override or derived from Browserbase browserSettings.os ("mobile"/"tablet") or local hasTouch.
    • Added page.tap(x, y) and locator.tap() using trusted Input.dispatchTouchEvent; supports { returnXpath }, updates the cursor overlay, and tap is registered in the act handler map for replay.
    • CUA: route a single left click to tap on touch sessions; Hybrid tools click, type, and fillFormVision now use tap for coordinate focus/activation. Right/middle/multi-click, hover, drag remain mouse-based.
    • Recording/replay: coordinate clicks actuated as touch are recorded as tap; typing tools still record type. Added unit tests for touch resolution and tool routing, plus an integration test validating tap activation.
  • Bug Fixes

    • CLI: fixed lint in reduce-logs by adding typed CDP payload (CdpParams) and formatting; no behavior change.

Written for commit c77ad1e. Summary will update on new commits.

Review in cubic

alonle and others added 3 commits July 23, 2026 10:34
The computer-use agent actuates every click via `Input.dispatchMouseEvent`
(page.click). On a mobile session the browser renders the site's touch-gated
mobile layout, whose handlers only respond to touch/pointer events — so a
synthesized mouse click does not register (e.g. an SFCC size selector keeps
showing "please choose a size" and add-to-cart fails).

Add a coordinate `page.tap(x, y)` (trusted `Input.dispatchTouchEvent`, mirroring
`page.click`) and route the CUA `click` action to it when the session presents as
mobile (`navigator.userAgentData.mobile`, detected once per run). Desktop sessions
are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…or replay

- Route only a single LEFT click to tap; right/middle/multi-click keep the mouse
  path (preserves context-menu and double-click behavior).
- On a recording (replay) mobile run, record the tap as a deterministic "tap"
  step (page.tap gains returnXpath) and add a replayable tap method: locator.tap()
  + METHOD_HANDLER_MAP.tap. Not added to SupportedUnderstudyAction — replay
  dispatches by map lookup, so the LLM is not offered tap during live inference.
- page.tap now updates the cursor overlay like other coordinate actions.
- Add an integration test for tap actuation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c77ad1e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch
@browserbasehq/stagehand-server-v3 Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@miguelg719
miguelg719 marked this pull request as draft July 23, 2026 08:05
Comment thread .changeset/touch-tap-actuation.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found and verified against the latest diff

Confidence score: 3/5

  • In packages/core/lib/v3/handlers/v3CuaAgentHandler.ts, the agent cache can replay a mobile tap in a later desktop session, which can send the wrong pointer type through gated flows and cause behavior divergence without a cache miss — include session/device mode in the cache key (or otherwise partition cache entries by pointer context).
  • Coverage is thin around tap/click dispatch across packages/core/lib/v3/understudy/page.ts and packages/core/lib/v3/handlers/v3CuaAgentHandler.ts, so a broken Page.tap path or incorrect mobile/non-mobile routing/recording could ship silently — add focused integration and handler-level regression tests asserting both dispatch (page.tap vs page.click) and recorded action type for each mode.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/core/lib/v3/understudy/page.ts">

<violation number="1" location="packages/core/lib/v3/understudy/page.ts:1909">
P3: Coordinate taps have no focused regression coverage, so a broken `Page.tap` dispatch path or a mouse-typed event would ship unnoticed. Add an integration test that calls `page.tap(x, y)` and asserts the target receives touch/pointer-touch events.

(Based on your team's feedback about adding unit tests for new behavior.) [FEEDBACK_USED]</violation>
</file>

<file name="packages/core/lib/v3/handlers/v3CuaAgentHandler.ts">

<violation number="1" location="packages/core/lib/v3/handlers/v3CuaAgentHandler.ts:358">
P3: This new CUA dispatch branch has no handler-level regression coverage for mobile/non-mobile selection or recording. Add focused mocks asserting `page.tap`/`page.click` and recorded `tap`/`click` behavior for both paths.

(Based on your team's feedback about unit tests for new behavior.) [FEEDBACK_USED]</violation>

<violation number="2" location="packages/core/lib/v3/handlers/v3CuaAgentHandler.ts:388">
P2: A local cache entry recorded on mobile replays as a touch action in a later desktop session, so pointer-type-gated flows can diverge without a cache miss. Include session/device mode in the agent-cache key, or choose tap versus click at replay time.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const stagehandAction: Action = {
selector: normalized,
description: this.describePointerAction("tap", x, y),
method: "tap",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A local cache entry recorded on mobile replays as a touch action in a later desktop session, so pointer-type-gated flows can diverge without a cache miss. Include session/device mode in the agent-cache key, or choose tap versus click at replay time.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/handlers/v3CuaAgentHandler.ts, line 388:

<comment>A local cache entry recorded on mobile replays as a touch action in a later desktop session, so pointer-type-gated flows can diverge without a cache miss. Include session/device mode in the agent-cache key, or choose tap versus click at replay time.</comment>

<file context>
@@ -344,6 +347,59 @@ export class V3CuaAgentHandler {
+                const stagehandAction: Action = {
+                  selector: normalized,
+                  description: this.describePointerAction("tap", x, y),
+                  method: "tap",
+                  arguments: [],
+                };
</file context>

* origin (top-left). Does not scroll. Requires a touch-capable (e.g. mobile) session.
*/
@FlowLogger.wrapWithLogging({ eventType: "PageTap" })
async tap(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Coordinate taps have no focused regression coverage, so a broken Page.tap dispatch path or a mouse-typed event would ship unnoticed. Add an integration test that calls page.tap(x, y) and asserts the target receives touch/pointer-touch events.

(Based on your team's feedback about adding unit tests for new behavior.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/understudy/page.ts, line 1909:

<comment>Coordinate taps have no focused regression coverage, so a broken `Page.tap` dispatch path or a mouse-typed event would ship unnoticed. Add an integration test that calls `page.tap(x, y)` and asserts the target receives touch/pointer-touch events.

(Based on your team's feedback about adding unit tests for new behavior.) </comment>

<file context>
@@ -1897,6 +1897,49 @@ export class Page {
+   * origin (top-left). Does not scroll. Requires a touch-capable (e.g. mobile) session.
+   */
+  @FlowLogger.wrapWithLogging({ eventType: "PageTap" })
+  async tap(
+    x: number,
+    y: number,
</file context>

// (context menu) and multi-click stay on the mouse path unchanged. Detected
// once via userAgentData (Browserbase os:"mobile" reports maxTouchPoints:0
// but userAgentData.mobile:true).
const isPrimarySingleClick =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This new CUA dispatch branch has no handler-level regression coverage for mobile/non-mobile selection or recording. Add focused mocks asserting page.tap/page.click and recorded tap/click behavior for both paths.

(Based on your team's feedback about unit tests for new behavior.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/handlers/v3CuaAgentHandler.ts, line 358:

<comment>This new CUA dispatch branch has no handler-level regression coverage for mobile/non-mobile selection or recording. Add focused mocks asserting `page.tap`/`page.click` and recorded `tap`/`click` behavior for both paths.

(Based on your team's feedback about unit tests for new behavior.) </comment>

<file context>
@@ -344,6 +347,59 @@ export class V3CuaAgentHandler {
+        // (context menu) and multi-click stay on the mouse path unchanged. Detected
+        // once via userAgentData (Browserbase os:"mobile" reports maxTouchPoints:0
+        // but userAgentData.mobile:true).
+        const isPrimarySingleClick =
+          button === "left" && ((clickCount as number) ?? 1) === 1;
+        if (isPrimarySingleClick) {
</file context>

Replace the lazy per-run probe in the CUA click handler (a page.evaluate of
navigator.userAgentData.mobile, cached on first click) with a `usesTouch` getter
resolved from configuration:

- explicit `useTouch` wins, so callers can force either mode;
- otherwise derived from Browserbase `browserSettings.os` ("mobile"/"tablet") or
  a local session's `localBrowserLaunchOptions.hasTouch`.

The probe had three problems the config signal does not: it could not run before
the first click, `userAgentData` is absent on insecure origins and on about:blank
(so a first click there cached a false negative for the whole run), and
`userAgentData.mobile` is false for tablets. It also cost a round trip per run
and made behavior depend on which page happened to be loaded first.

Note `os` is unavailable when resuming via browserbaseSessionID; `useTouch` is
the explicit opt-in for that case.

Adds unit coverage for the resolution matrix, which needs no browser.
The CUA handler was the only path actuating touch, but hybrid mode goes through
V3AgentHandler and its own tools, so it still sent mouse clicks. Hybrid is the
auto-selected mode for Claude / GPT-5.4 / Gemini 3, i.e. the default most callers
get -- and "CUA hybrid agent (mouse): blocked" was a row in the original bug
report, so the reported failure survived the first fix.

Route on v3.usesTouch in:
- clickTool, and record the step as "tap" so a cached mobile run does not replay
  as a mouse click;
- typeTool and fillFormVisionTool, whose click-to-focus has the same problem --
  an unregistered focus click means the text goes nowhere. Their recorded step
  stays a "type", which replays via the selector rather than a pointer event.

Deliberately unchanged: double/triple click, right/middle click, drag and hover.
Real touch drag needs touchMove interpolation rather than a routing switch.

Adds unit coverage asserting each tool picks tap vs click and records the
matching method.
Format with prettier and drop the three `any`s, which were failing the Lint job
and -- because lint failure cancels the rest of the workflow -- taking every
other check on the branch down with it. Inherited from #2373 on main; the eslint
errors were latent there because `format:check` runs first and exited before
eslint ever saw the file.

Types the CDP payload the reducer actually reads instead of indexing an `any`
record. Behavior is unchanged: the added `?? ""` only feeds `includes()`, where
`undefined` and `""` are both misses, and the new optional chains sit behind
guards that already established the object exists.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants