Skip to content

Comments

[fix]: support <frame> elements in XPath frame detection#1697

Merged
seanmcguire12 merged 3 commits intomainfrom
shrey/frame-regex-fix-check
Feb 22, 2026
Merged

[fix]: support <frame> elements in XPath frame detection#1697
seanmcguire12 merged 3 commits intomainfrom
shrey/frame-regex-fix-check

Conversation

@shrey150
Copy link
Contributor

@shrey150 shrey150 commented Feb 18, 2026

Summary

  • IFRAME_STEP_RE regex only matched iframe steps, causing act() to fail on legacy <frameset> pages where observe() returns XPaths containing frame[N] (e.g. /html[1]/frameset[1]/frame[4]/html[1]/body[1]/...)
  • Changed regex from /^iframe(?:\[\d+])?$/i to /^i?frame(?:\[\d+])?$/i so both <iframe> and <frame> are recognized as frame boundaries
  • Consolidated the duplicate IFRAME_STEP_RE constant (was defined in both deepLocator.ts and focusSelectors.ts) into a single shared export

Test plan

  • Scratchpad repro confirms current regex misses frame, frame[4], FRAME — all pass with fix
  • 10 new unit tests added to snapshot-focus-selectors-utils.test.ts covering:
    • Regex matches iframe, iframe[N], frame, frame[N] (case-insensitive)
    • Regex rejects frameset, frameset[N], div, body, html
    • Full XPath parse + walk for frameset page XPaths detects correct frame boundary
    • Standard iframe XPaths still detected correctly
    • frameset is not false-positive detected as a frame boundary
  • All 15 tests pass, build + typecheck pass

🤖 Generated with Claude Code


Summary by cubic

Adds support for in XPath frame boundary detection so navigation works on legacy pages. iframe and frame steps are treated as frame boundaries.

  • Bug Fixes

    • Updated regex from /^iframe(?:[\d+])?$/i to /^i?frame(?:[\d+])?$/i to match iframe and frame (case-insensitive) without matching frameset.
    • Added focused tests for iframe/frame and frameset XPaths, including parse + boundary detection; trimmed low-value cases.
    • Added a changeset for a patch release.
  • Refactors

    • Consolidated IFRAME_STEP_RE into a shared export from focusSelectors and used it in deepLocator.

Written for commit 4d09b02. Summary will update on new commits. Review in cubic

@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

🦋 Changeset detected

Latest commit: 4d09b02

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 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

Copy link
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 3 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant Agent as Stagehand Agent
    participant DL as DeepLocator
    participant FS as focusSelectors (Utils)
    participant Browser as Browser/DOM Context

    Note over Agent,Browser: Cross-Frame Element Resolution Flow

    Agent->>DL: act/observe(selector/xpath)
    
    DL->>FS: parseXPathToSteps(xpath)
    FS-->>DL: Array of Steps (e.g. html, frameset, frame[4], body)

    loop For each XPath Step
        DL->>DL: CHANGED: Match step name against shared IFRAME_STEP_RE
        
        alt Step matches /^i?frame(?:\[\d+])?$/i
            Note over DL: NEW: Now matches legacy <frame> <br/>in addition to <iframe>
            DL->>Browser: Resolve FrameLocator for boundary
            Browser-->>DL: New Frame Context
        else Step is standard element (e.g. div, frameset, body)
            DL->>Browser: Continue path resolution in current context
        end
    end

    alt Element Found
        DL-->>Agent: Return ElementHandle
    else Frame Detection Failed (Old behavior for <frame>)
        DL-->>Agent: Throw StagehandInvalidArgumentError
    end
Loading

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 18, 2026

Greptile Summary

Fixed frame detection regex to support legacy <frame> elements in addition to <iframe> elements. Changed IFRAME_STEP_RE from /^iframe(?:\[\d+])?$/i to /^i?frame(?:\[\d+])?$/i, making the "i" prefix optional so both element types are recognized as frame boundaries in XPath parsing.

  • Updated regex in focusSelectors.ts and exported it for shared use
  • Removed duplicate regex definition in deepLocator.ts, now imports from single source
  • Added 10 comprehensive unit tests covering frame, frame[N], case-insensitivity, and negative cases (frameset, other elements)
  • Fixes act() failures on legacy <frameset> pages where observe() returns XPaths like /html[1]/frameset[1]/frame[4]/html[1]/body[1]/...

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The change is surgical and well-tested - only modifies a regex pattern to add support for legacy <frame> elements. The regex change is minimal (adding i? to make the "i" optional), consolidates duplicate code, and includes comprehensive test coverage with 10 new tests covering all edge cases. No breaking changes or risky logic modifications.
  • No files require special attention

Important Files Changed

Filename Overview
packages/core/lib/v3/understudy/a11y/snapshot/focusSelectors.ts Changed IFRAME_STEP_RE regex from /^iframe(?:\[\d+])?$/i to /^i?frame(?:\[\d+])?$/i and exported it, now correctly matches both <iframe> and <frame> elements in XPaths
packages/core/lib/v3/understudy/deepLocator.ts Removed duplicate IFRAME_STEP_RE constant definition and imports it from focusSelectors.ts instead, consolidating the regex to a single source of truth
packages/core/tests/snapshot-focus-selectors-utils.test.ts Added 10 comprehensive unit tests covering regex matches for frame/iframe variants, case-insensitivity, negative cases (frameset, other elements), and full XPath parsing for frameset pages

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[XPath with frame elements] --> B[parseXPathToSteps]
    B --> C{For each step}
    C --> D[Extract name by removing index]
    D --> E{Test with IFRAME_STEP_RE}
    E -->|Matches iframe or iframe N| F[Detected as frame boundary]
    E -->|Matches frame or frame N - NEW| F
    E -->|Does not match frameset or other| G[Regular element]
    F --> H[Navigate into child frame]
    G --> J[Add to current frame buffer]
    H --> K[Continue parsing in child frame]
    J --> K
Loading

Last reviewed commit: 80d56e3

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@shrey150 shrey150 force-pushed the shrey/frame-regex-fix-check branch from 3d50339 to c33f3c5 Compare February 20, 2026 02:33
shrey150 and others added 3 commits February 22, 2026 12:29
IFRAME_STEP_RE only matched "iframe" steps, causing act() to fail on
legacy <frameset> pages where observe() returns XPaths with "frame[N]".
Changed regex to /^i?frame(?:\[\d+])?$/i and consolidated the duplicate
constant into a single shared export.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@seanmcguire12 seanmcguire12 force-pushed the shrey/frame-regex-fix-check branch from c33f3c5 to 4d09b02 Compare February 22, 2026 20:29
@seanmcguire12 seanmcguire12 merged commit aac9a19 into main Feb 22, 2026
161 checks passed
miguelg719 pushed a commit that referenced this pull request Feb 24, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @browserbasehq/stagehand@3.1.0

### Minor Changes

- [#1681](#1681)
[`e3db9aa`](e3db9aa)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add cookie management
APIs: `context.addCookies()`, `context.clearCookies()`, &
`context.cookies()`

- [#1672](#1672)
[`b65756e`](b65756e)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add boolean
keepAlive parameter to allow for configuring whether the browser should
be closed when stagehand.close() is called.

- [#1708](#1708)
[`176d420`](176d420)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add
context.setExtraHTTPHeaders()

- [#1611](#1611)
[`8a3c066`](8a3c066)
Thanks [@monadoid](https://github.com/monadoid)! - Using `mode` enum
instead of old `cua` boolean in openapi spec

### Patch Changes

- [#1683](#1683)
[`7584f3e`](7584f3e)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix:
include shadow DOM in .count() & .nth() & support xpath predicates

- [#1644](#1644)
[`1e1c9c1`](1e1c9c1)
Thanks [@monadoid](https://github.com/monadoid)! - Fix unhandled CDP
detaches by returning the original sendCDP promise

- [#1729](#1729)
[`6bef890`](6bef890)
Thanks [@shrey150](https://github.com/shrey150)! - fix: support Claude
4.6 (Opus and Sonnet) in CUA mode by using the correct
`computer_20251124` tool version and `computer-use-2025-11-24` beta
header

- [#1647](#1647)
[`ffd4b33`](ffd4b33)
Thanks [@tkattkat](https://github.com/tkattkat)! - Fix [Agent] - Address
bug causing issues with continuing a conversation from past messages in
dom mode

- [#1614](#1614)
[`677bff5`](677bff5)
Thanks [@miguelg719](https://github.com/miguelg719)! - Enforce
<number>-<number> regex validation on act/observe for elementId

- [#1580](#1580)
[`65ff464`](65ff464)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add unified variables
support across act and agent with a single VariableValue type

- [#1666](#1666)
[`101bcf2`](101bcf2)
Thanks [@Kylejeong2](https://github.com/Kylejeong2)! - add support for
codex models

- [#1728](#1728)
[`0a94301`](0a94301)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - handle
potential race condition on `.close()` when using the Stagehand API

- [#1664](#1664)
[`b27c04d`](b27c04d)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fixes issue
with context.addInitScript() where scripts were not being applied to out
of process iframes (OOPIFs), and popup pages with same process iframes
(SPIFs)

- [#1632](#1632)
[`afbd08b`](afbd08b)
Thanks [@pirate](https://github.com/pirate)! - Remove automatic `.env`
loading via `dotenv`.

If your app relies on `.env` files, install `dotenv` and load it
explicitly in your code:

    ```ts
    import dotenv from "dotenv";
    dotenv.config({ path: ".env" });
    ```

- [#1624](#1624)
[`0e8d569`](0e8d569)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue
where screenshot masks were not being applied to dialog elements

- [#1596](#1596)
[`ff0f979`](ff0f979)
Thanks [@tkattkat](https://github.com/tkattkat)! - Update usage/metrics
handling in agent

- [#1631](#1631)
[`2d89d2b`](2d89d2b)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add right and
middle click support to act and observe

- [#1697](#1697)
[`aac9a19`](aac9a19)
Thanks [@shrey150](https://github.com/shrey150)! - fix: support
`<frame>` elements in XPath frame boundary detection so `act()` works on
legacy `<frameset>` pages

- [#1692](#1692)
[`06de50f`](06de50f)
Thanks [@shrey150](https://github.com/shrey150)! - fix: skip piercer
injection for chrome-extension:// and other non-HTML targets

- [#1613](#1613)
[`aa4d981`](aa4d981)
Thanks [@miguelg719](https://github.com/miguelg719)! -
SupportedUnderstudyAction Enum validation for 'method' on act/observe
inference

- [#1652](#1652)
[`18b1e3b`](18b1e3b)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add support for
gemini 3 flash and pro in hybrid/cua agent

- [#1706](#1706)
[`957d82b`](957d82b)
Thanks [@chrisreadsf](https://github.com/chrisreadsf)! - Add GLM to
prompt-based JSON fallback for models without native structured output
support

- [#1633](#1633)
[`22e371a`](22e371a)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add warning when
incorrect models are used with agents hybrid mode

- [#1673](#1673)
[`d29b91f`](d29b91f)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add multi-region
support for Stagehand API with region-specific endpoints

- [#1695](#1695)
[`7b4f817`](7b4f817)
Thanks [@tkattkat](https://github.com/tkattkat)! - Fix: zod bug when
pinning zod to v3 and using structured output in agent

- [#1609](#1609)
[`3f9ca4d`](3f9ca4d)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add
SupportedUnderstudyActions to observe system prompt

- [#1581](#1581)
[`49ead1e`](49ead1e)
Thanks [@sameelarif](https://github.com/sameelarif)! - **Server-side
caching is now available.**

When running `env: "BROWSERBASE"`, Stagehand automatically caches
`act()`, `extract()`, and `observe()` results server-side — repeated
calls with the same inputs return instantly without consuming LLM
tokens.

Caching is enabled by default and can be disabled via `serverCache:
false` on the Stagehand instance or per individual call. Check out the
[browserbase blog](https://www.browserbase.com/blog/stagehand-caching)
for more details.

- [#1642](#1642)
[`3673369`](3673369)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue
where scripts added via context.addInitScripts() were not being injected
into new pages that were opened via popups (eg, clicking a link that
opens a new page) and/or calling context.newPage(url)

- [#1735](#1735)
[`c465e87`](c465e87)
Thanks [@monadoid](https://github.com/monadoid)! - Supports request
header authentication with connectToMCPServer

- [#1705](#1705)
[`ae533e4`](ae533e4)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - include
error cause in UnderstudyCommandException

- [#1636](#1636)
[`ea33052`](ea33052)
Thanks [@miguelg719](https://github.com/miguelg719)! - Include
executionModel on the AgentConfigSchema

- [#1679](#1679)
[`5764ede`](5764ede)
Thanks [@shrey150](https://github.com/shrey150)! - fix issue where
locator.count() was not working with xpaths that have attribute
predicates

- [#1646](#1646)
[`f09b184`](f09b184)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add user-agent to
CDP connections

- [#1637](#1637)
[`a7d29de`](a7d29de)
Thanks [@miguelg719](https://github.com/miguelg719)! - Improve error and
warning message for legacy model format

- [#1685](#1685)
[`d334399`](d334399)
Thanks [@tkattkat](https://github.com/tkattkat)! - Bump ai sdk & google
provider version

- [#1662](#1662)
[`44416da`](44416da)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue
where locator.fill() was not working on elements that require direct
value setting

- [#1612](#1612)
[`bdd8b4e`](bdd8b4e)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix issue
where screenshot mask was only being applied to the first element that
the locator resolved to. masks now apply to all matching elements.

## @browserbasehq/stagehand-server@3.6.0

### Minor Changes

- [#1611](#1611)
[`8a3c066`](8a3c066)
Thanks [@monadoid](https://github.com/monadoid)! - Using `mode` enum
instead of old `cua` boolean in openapi spec

### Patch Changes

- [#1604](#1604)
[`4753078`](4753078)
Thanks [@miguelg719](https://github.com/miguelg719)! - Enable bedrock

- [#1636](#1636)
[`ea33052`](ea33052)
Thanks [@miguelg719](https://github.com/miguelg719)! - Include
executionModel on the AgentConfigSchema

- [#1602](#1602)
[`22a0502`](22a0502)
Thanks [@miguelg719](https://github.com/miguelg719)! - Include vertex as
a supported provider

- Updated dependencies
\[[`7584f3e`](7584f3e),
[`1e1c9c1`](1e1c9c1),
[`6bef890`](6bef890),
[`ffd4b33`](ffd4b33),
[`677bff5`](677bff5),
[`65ff464`](65ff464),
[`101bcf2`](101bcf2),
[`0a94301`](0a94301),
[`b27c04d`](b27c04d),
[`afbd08b`](afbd08b),
[`e3db9aa`](e3db9aa),
[`0e8d569`](0e8d569),
[`ff0f979`](ff0f979),
[`2d89d2b`](2d89d2b),
[`aac9a19`](aac9a19),
[`06de50f`](06de50f),
[`aa4d981`](aa4d981),
[`18b1e3b`](18b1e3b),
[`957d82b`](957d82b),
[`b65756e`](b65756e),
[`22e371a`](22e371a),
[`d29b91f`](d29b91f),
[`7b4f817`](7b4f817),
[`176d420`](176d420),
[`3f9ca4d`](3f9ca4d),
[`8a3c066`](8a3c066),
[`49ead1e`](49ead1e),
[`3673369`](3673369),
[`c465e87`](c465e87),
[`ae533e4`](ae533e4),
[`ea33052`](ea33052),
[`5764ede`](5764ede),
[`f09b184`](f09b184),
[`a7d29de`](a7d29de),
[`d334399`](d334399),
[`44416da`](44416da),
[`bdd8b4e`](bdd8b4e)]:
    -   @browserbasehq/stagehand@3.1.0

## @browserbasehq/stagehand-evals@1.1.8

### Patch Changes

- Updated dependencies
\[[`7584f3e`](7584f3e),
[`1e1c9c1`](1e1c9c1),
[`6bef890`](6bef890),
[`ffd4b33`](ffd4b33),
[`677bff5`](677bff5),
[`65ff464`](65ff464),
[`101bcf2`](101bcf2),
[`0a94301`](0a94301),
[`b27c04d`](b27c04d),
[`afbd08b`](afbd08b),
[`e3db9aa`](e3db9aa),
[`0e8d569`](0e8d569),
[`ff0f979`](ff0f979),
[`2d89d2b`](2d89d2b),
[`aac9a19`](aac9a19),
[`06de50f`](06de50f),
[`aa4d981`](aa4d981),
[`18b1e3b`](18b1e3b),
[`957d82b`](957d82b),
[`b65756e`](b65756e),
[`22e371a`](22e371a),
[`d29b91f`](d29b91f),
[`7b4f817`](7b4f817),
[`176d420`](176d420),
[`3f9ca4d`](3f9ca4d),
[`8a3c066`](8a3c066),
[`49ead1e`](49ead1e),
[`3673369`](3673369),
[`c465e87`](c465e87),
[`ae533e4`](ae533e4),
[`ea33052`](ea33052),
[`5764ede`](5764ede),
[`f09b184`](f09b184),
[`a7d29de`](a7d29de),
[`d334399`](d334399),
[`44416da`](44416da),
[`bdd8b4e`](bdd8b4e)]:
    -   @browserbasehq/stagehand@3.1.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

3 participants