Skip to content

Conversation

@seanmcguire12
Copy link
Member

@seanmcguire12 seanmcguire12 commented Jan 21, 2026

why

  • to give users a way to exclude components of the hybrid tree returned by page.snapshot()
  • for now, this only adds support for exclusion of iframes, but it is designed to be extensible with the anticipation that there will be other element/node types that users may want to exclude in the future

what changed

  • added an optional PageSnapshotOptions parameter with includeIframes?: boolean and updated page.snapshot() to accept and forward it. Internally SnapshotOptions now mirrors that flag downstream in captureHybridSnapshot()
  • the internal captureHybridSnapshot() function now computes framesInScope, and passes that filtered list to buildSessionIndexes(), collectPerFrameMaps(), computeFramePrefixes(), and mergeFramesIntoSnapshot(), so each stage only processes the intended frames while scoped snapshots (tryScopedSnapshot()) still run before the filter is applied
  • updated various helpers to adhere to the filtered list of frames:
    • collectPerFrameMaps() iterates only on the provided frames
    • computeFramePrefixes() skips any children which should be ignored
    • mergeFramesIntoSnapshot() only merges the frames that are in scope
  • page.snapshot() defaults to current behaviour (iframe inclusion)

test plan

  • added additional unit tests:
    • snapshot-capture-orchestration.test.ts now asserts that collectPerFrameMaps() ignores omitted frames, & scoped snapshots still succeed with includeIframes: false, & the full capture path drops iframe data when requested
    • snapshot-frame-merge.test.ts now exercises the helper functions with filtered frame sets to check that prefixes, merges, and per-frame payloads are suppressed for excluded IDs
    • added a new page-snapshot.test.ts to check that the public page.snapshot() API forwards the option (both explicit false and default true) into captureHybridSnapshot()

Summary by cubic

Add an optional includeIframes flag to page.snapshot() so users can exclude iframe subtrees from the hybrid snapshot. Defaults to current behavior; use page.snapshot({ includeIframes: false }) to drop iframe content. Addresses Linear STG-1190.

  • New Features
    • page.snapshot(options?: { includeIframes?: boolean }) now forwards the flag to captureHybridSnapshot.
    • SnapshotOptions gains includeIframes (default true).
    • Internals compute framesInScope and only process those frames; scoped snapshots still run before filtering.
    • Updated helpers to respect filtering: collectPerFrameMaps, computeFramePrefixes, mergeFramesIntoSnapshot.

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

@changeset-bot
Copy link

changeset-bot bot commented Jan 21, 2026

🦋 Changeset detected

Latest commit: a61c30b

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

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 21, 2026

Greptile Summary

This PR adds an includeIframes option to page.snapshot() that allows users to exclude iframe content from the hybrid snapshot tree. The implementation correctly filters frames at the capture entry point and threads the filtered list through all helper functions (buildSessionIndexes, collectPerFrameMaps, computeFramePrefixes, and mergeFramesIntoSnapshot). The option defaults to true (current behavior), and false limits the snapshot to only the root frame.

Key changes:

  • Added PageSnapshotOptions type with includeIframes?: boolean to public API
  • Updated internal SnapshotOptions to mirror the flag
  • Modified captureHybridSnapshot() to compute framesInScope based on the option
  • Updated all downstream helpers to respect the filtered frame list
  • Scoped snapshots (via focusSelector) continue to work correctly regardless of iframe inclusion setting

Test coverage:

  • Unit tests verify that collectPerFrameMaps() skips excluded frames
  • Tests confirm scoped snapshots work with includeIframes: false
  • Tests validate full capture path drops iframe data when requested
  • Tests check prefix computation and merge logic ignore excluded frames

Minor discrepancy:

  • PR description mentions adding page-snapshot.test.ts but the file is not in the changes

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The implementation is clean, well-tested, and follows good engineering practices. The filtering logic is applied at the right level and consistently threaded through all affected functions. The default behavior preserves backward compatibility. Comprehensive unit tests cover the main code paths and edge cases.
  • No files require special attention

Important Files Changed

Filename Overview
packages/core/lib/v3/understudy/page.ts Updates page.snapshot() to accept options and forward includeIframes to internal capture function
packages/core/lib/v3/understudy/a11y/snapshot/capture.ts Implements iframe exclusion by filtering frames before processing, correctly threading framesInScope through all helper functions
packages/core/tests/snapshot-capture-orchestration.test.ts Adds comprehensive tests for iframe exclusion in collectPerFrameMaps and full capture path, including scoped snapshot edge case
packages/core/tests/snapshot-frame-merge.test.ts Adds tests for prefix computation and merge logic with filtered frame sets to ensure excluded frames are not processed

Sequence Diagram

sequenceDiagram
    participant Client
    participant Page
    participant captureHybridSnapshot
    participant buildSessionIndexes
    participant collectPerFrameMaps
    participant computeFramePrefixes
    participant mergeFramesIntoSnapshot

    Client->>Page: snapshot({includeIframes: false})
    Page->>captureHybridSnapshot: {pierceShadow: true, includeIframes: false}
    
    Note over captureHybridSnapshot: includeIframes = options?.includeIframes !== false
    
    alt includeIframes is false
        captureHybridSnapshot->>captureHybridSnapshot: framesInScope = [context.rootId]
    else includeIframes is true (default)
        captureHybridSnapshot->>captureHybridSnapshot: framesInScope = [...context.frames]
    end
    
    captureHybridSnapshot->>captureHybridSnapshot: Ensure rootId is in framesInScope
    
    captureHybridSnapshot->>buildSessionIndexes: (page, framesInScope, pierce)
    buildSessionIndexes-->>captureHybridSnapshot: sessionToIndex Map
    
    captureHybridSnapshot->>collectPerFrameMaps: (page, context, sessionToIndex, options, pierce, framesInScope)
    Note over collectPerFrameMaps: Only iterates frameIds in framesInScope
    collectPerFrameMaps-->>captureHybridSnapshot: {perFrameMaps, perFrameOutlines}
    
    captureHybridSnapshot->>computeFramePrefixes: (page, context, perFrameMaps, framesInScope)
    Note over computeFramePrefixes: Skips children not in framesInScope
    computeFramePrefixes-->>captureHybridSnapshot: {absPrefix, iframeHostEncByChild}
    
    captureHybridSnapshot->>mergeFramesIntoSnapshot: (context, perFrameMaps, perFrameOutlines, absPrefix, iframeHostEncByChild, framesInScope)
    Note over mergeFramesIntoSnapshot: Only merges frames in framesInScope
    mergeFramesIntoSnapshot-->>captureHybridSnapshot: HybridSnapshot
    
    captureHybridSnapshot-->>Page: HybridSnapshot
    Page-->>Client: SnapshotResult
Loading

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.

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@seanmcguire12 seanmcguire12 merged commit 56c0d24 into main Jan 22, 2026
31 checks passed
@github-actions github-actions bot mentioned this pull request Jan 21, 2026
seanmcguire12 pushed a commit that referenced this pull request Jan 22, 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.0.8

### Patch Changes

- [#1514](#1514)
[`40ce5cc`](40ce5cc)
Thanks [@tkattkat](https://github.com/tkattkat)! - Rename the close tool
in agent to "done"

- [#1574](#1574)
[`5506f41`](5506f41)
Thanks [@tkattkat](https://github.com/tkattkat)! - fix(server): pass
cdpUrl to localBrowserLaunchOptions when launchOptions absent

- [#1521](#1521)
[`84c05ca`](84c05ca)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix: get
agent cache working in API mode

- [#1486](#1486)
[`692ffa0`](692ffa0)
Thanks [@tkattkat](https://github.com/tkattkat)! - improve logging in
agent

- [#1551](#1551)
[`1ef8901`](1ef8901)
Thanks [@miguelg719](https://github.com/miguelg719)! - move extract
handler response log to after URL injection

- [#1495](#1495)
[`72ac775`](72ac775)
Thanks [@tkattkat](https://github.com/tkattkat)! - export tool function
& type to simplify defining custom tools

- [#1481](#1481)
[`3d5af07`](3d5af07)
Thanks [@tkattkat](https://github.com/tkattkat)! - add waitForTimeout to
page

- [#1423](#1423)
[`40e1d80`](40e1d80)
Thanks [@miguelg719](https://github.com/miguelg719)! - Improve benchmark
handling and add metadata

- [#1588](#1588)
[`56c0d24`](56c0d24)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add
SnapshotOptions to page.snapshot()

- [#1483](#1483)
[`16d72fb`](16d72fb)
Thanks [@tkattkat](https://github.com/tkattkat)! - Optimize screenshot
handling in agent hybrid mode

- [#1498](#1498)
[`088c4cc`](088c4cc)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix:
replaying cached actions (for agent & act) now uses the originally
defined model, (instead of default model) when action fails and
rerunning inference is needed

- [#1575](#1575)
[`4276f4a`](4276f4a)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - expose port
param in localBrowserLaunchOptions

- [#1544](#1544)
[`6005786`](6005786)
Thanks [@tkattkat](https://github.com/tkattkat)! - Recommend hybrid mode
over DOM mode in agent, which is now considered legacy

- [#1505](#1505)
[`6fbf5fc`](6fbf5fc)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add structured output
to agent result + ensure close tool is always called

- [#1511](#1511)
[`704cf18`](704cf18)
Thanks [@shrey150](https://github.com/shrey150)! - Fix ControlOrMeta
keypress event

- [#1480](#1480)
[`091296e`](091296e)
Thanks [@tkattkat](https://github.com/tkattkat)! - Update agent to only
calculate xpath when caching is enabled

- [#1509](#1509)
[`e56c6eb`](e56c6eb)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add support
for page.waitForSelector()

- [#1478](#1478)
[`2cb78d0`](2cb78d0)
Thanks [@tkattkat](https://github.com/tkattkat)! - update agent message
handling

- [#1518](#1518)
[`5dad639`](5dad639)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add
page.snapshot() for capturing a stringified DOM snapshot of the page,
including an xpath map & url map

- [#1576](#1576)
[`b7c2571`](b7c2571)
Thanks [@tkattkat](https://github.com/tkattkat)! - utilize
waitForSelector when running agent cache

- [#1560](#1560)
[`4c69117`](4c69117)
Thanks [@tkattkat](https://github.com/tkattkat)! - Update coordinate
handling in cua and hybrid

## @browserbasehq/stagehand-server@3.5.0

### Minor Changes

- [#1578](#1578)
[`a5074bd`](a5074bd)
Thanks [@monadoid](https://github.com/monadoid)! - /end endpoint no
longer takes an empty object - instead, no request body is required.

### Patch Changes

- Updated dependencies
\[[`40ce5cc`](40ce5cc),
[`5506f41`](5506f41),
[`84c05ca`](84c05ca),
[`692ffa0`](692ffa0),
[`1ef8901`](1ef8901),
[`72ac775`](72ac775),
[`3d5af07`](3d5af07),
[`40e1d80`](40e1d80),
[`56c0d24`](56c0d24),
[`16d72fb`](16d72fb),
[`088c4cc`](088c4cc),
[`4276f4a`](4276f4a),
[`6005786`](6005786),
[`6fbf5fc`](6fbf5fc),
[`704cf18`](704cf18),
[`091296e`](091296e),
[`e56c6eb`](e56c6eb),
[`2cb78d0`](2cb78d0),
[`5dad639`](5dad639),
[`b7c2571`](b7c2571),
[`4c69117`](4c69117)]:
    -   @browserbasehq/stagehand@3.0.8

## @browserbasehq/stagehand-evals@1.1.7

### Patch Changes

- Updated dependencies
\[[`40ce5cc`](40ce5cc),
[`5506f41`](5506f41),
[`84c05ca`](84c05ca),
[`692ffa0`](692ffa0),
[`1ef8901`](1ef8901),
[`72ac775`](72ac775),
[`3d5af07`](3d5af07),
[`40e1d80`](40e1d80),
[`56c0d24`](56c0d24),
[`16d72fb`](16d72fb),
[`088c4cc`](088c4cc),
[`4276f4a`](4276f4a),
[`6005786`](6005786),
[`6fbf5fc`](6fbf5fc),
[`704cf18`](704cf18),
[`091296e`](091296e),
[`e56c6eb`](e56c6eb),
[`2cb78d0`](2cb78d0),
[`5dad639`](5dad639),
[`b7c2571`](b7c2571),
[`4c69117`](4c69117)]:
    -   @browserbasehq/stagehand@3.0.8

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