Skip to content

feat(pi): compact snapshot output to cut context - #2926

Open
QuentinDanblon wants to merge 1 commit into
browserbase:mainfrom
QuentinDanblon:pi-compact-snapshot
Open

QuentinDanblon wants to merge 1 commit into
browserbase:mainfrom
QuentinDanblon:pi-compact-snapshot

Conversation

@QuentinDanblon

@QuentinDanblon QuentinDanblon commented Sep 12, 2026

Copy link
Copy Markdown

What

snapshot in the pi integration returns a reduced accessibility tree by default, with compact: false to opt back into the raw tree and maxChars to hard-cap the payload.

Why

The raw tree is dominated by nodes a caller cannot use:

  • anonymous layout containers (div, listitem, tbody, scrollable, LayoutTable*, ...) that cannot be clicked or filled,
  • StaticText lines that are single-token fragments ({, ,, =) already folded into an ancestor's accessible name,
  • indentation that grows without bound with nesting depth.

In an agent loop that payload is replayed on every later request, so it is paid for repeatedly. On github.com/browserbase/stagehand the raw tree was 63 080 characters, roughly 15.8k tokens, for a single look at the page.

Change

  • extensions/snapshot-compaction.ts (new): the pure filter. Keeps semantic/actionable roles (link, button, textbox, heading, image, combobox, ...) plus StaticText of 12+ characters, truncates names at 160 characters, and re-indents kept lines by their kept ancestors (two spaces per level). Lines that carry no node (continuation lines of a multi-line accessible name) are dropped.
  • snapshot gains compact (default true) and maxChars. compact: false returns the byte-identical raw tree. maxChars appends an explicit truncation marker.
  • The tool snippet and the run/snapshot prompt guidelines now steer callers toward returning extracted values instead of dumping page content.

IDs are copied verbatim and only whole lines are removed, so IDs stay valid for run actions: the facade builds and stores the full xpath map regardless of what the tool returns, and filtering only changes the text handed back to the model.

Measurements

snapshot result size, raw vs compact, from real runs in the pi integration (local Chrome/Edge, no model in the loop for the sizes):

page raw compact ratio
news.ycombinator.com 34 027 7 314 21%
github.com/browserbase/stagehand 63 080 20 274 32%
books.toscrape.com 21 553 7 436 34%
example.com 316 218 69%

Tool latency is unchanged (a warm snapshot is 27-39 ms; the cold browser launch that dominates the first call is untouched).

Verification

  • vitest run in packages/integrations/pi: 16 tests pass, including the filter unit tests and an assertion that every ID in a compact tree exists in the raw tree.
  • tsc --noEmit, oxlint, and oxfmt --check are clean for the touched files.
  • Manual, against a live browser: clicking by an ID taken from a compact tree still navigates (books.toscrape.com -> a category page), so run actions are not broken by filtering.

I ran the TypeScript checks scoped to packages/integrations/pi rather than just check / just test, because just, Go 1.26, and uv are not installed in my environment. No changeset: packages/integrations/pi is private and this is not a public SDK, extension, or protocol change.

Follow-up

The same filter would help every integration rather than only pi, so it may belong in the facade contract (packages/integrations/core) once the behaviour is settled here. I kept it pi-local to keep the blast radius small.


Developed and verified with an AI coding agent in a local checkout; the numbers and test results above come from that checkout.


Summary by cubic

Makes snapshot in the pi integration return a compact accessibility tree by default, dropping anonymous layout containers and fragmented text so agent context costs far less. Pass compact: false to get the raw tree, and maxChars to hard-cap the payload.

New Features

  • Adds extensions/snapshot-compaction.ts to keep semantic/actionable roles and StaticText lines 12+ characters, truncating names at 160 characters.
  • Re-indents kept lines by kept ancestors; node IDs are copied verbatim, so run actions still work.
  • Adds compact (default true) and maxChars parameters to the snapshot tool.
  • Updates run and snapshot prompts to steer callers toward returning only needed values instead of dumping page content.
  • On real pages, the compact tree is 21–34% of the raw size (e.g. 63k to 20k chars on github.com/browserbase/stagehand).
  • All 16 pi integration tests pass, including checks that every compact ID exists in the raw tree.

Written for commit 346b3f8. Summary will update on new commits.

Review in cubic

The pi integration returned the raw accessibility tree from `snapshot`.
On real pages most of it is noise: anonymous layout containers (`div`,
`listitem`, `tbody`, `scrollable`, ...) that cannot be clicked or filled,
`StaticText` lines that are single-token fragments already aggregated into
an ancestor's name, and indentation that grows with nesting depth. In an agent
loop that payload is replayed on every later request, so it is paid for
repeatedly.

`snapshot` now keeps only semantic/actionable roles and re-indents by the
kept ancestors. `compact: false` returns the raw tree and `maxChars` hard-caps
the payload. Node IDs are copied verbatim and the facade keeps the full xpath
map, so `run` actions against compact IDs keep working.

Measured on real pages, the compact tree is 21-34% of the raw tree:
news.ycombinator.com 34027 -> 7314 chars, github.com/browserbase/stagehand
63080 -> 20274, books.toscrape.com 21553 -> 7436, example.com 316 -> 218.
@changeset-bot

changeset-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 346b3f8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@github-actions

Copy link
Copy Markdown
Contributor

This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run.
Approving the latest commit mirrors it into an internal PR owned by the approver.
If new commits are pushed later, the internal PR stays open but is marked stale until someone approves the latest external commit and refreshes it.

@github-actions github-actions Bot added external-contributor Tracks PRs mirrored from external contributor forks. external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. labels Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. external-contributor Tracks PRs mirrored from external contributor forks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant