Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/lean-browse-snapshot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"browse": patch
---

`browse snapshot` is now lean by default: it prints the formatted accessibility tree only, omitting the `xpathMap`/`urlMap` ref maps (~217KB / ~60K tokens on a content-heavy page) that were previously included on every snapshot.

Ref-based element commands (`click`, `fill`, `select`, etc.) are unaffected — the ref maps are still captured and cached server-side, so refs resolve exactly as before. To get the maps in the output, pass the new `--full` flag. The `--compact` flag is now a deprecated no-op alias of the default (it prints a stderr-only, TTY-gated deprecation notice).
4 changes: 2 additions & 2 deletions packages/cli/skills/browse/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ browse wait selector "#result"
Page state:

```bash
browse snapshot
browse snapshot --compact
browse snapshot # formatted tree only
browse snapshot --full # also include ref maps (xpathMap, urlMap)
browse get url
browse get title
browse get text body
Expand Down
17 changes: 13 additions & 4 deletions packages/cli/src/commands/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ import {

export default class Snapshot extends BrowseCommand {
static override description =
"Print the active page accessibility snapshot and cache refs for element commands.";
"Print the active page accessibility snapshot and cache refs for element commands. Pass --full to also include the ref maps (xpathMap, urlMap), or run `browse refs` to print them.";

static override examples = [
"browse snapshot",
"browse snapshot --compact",
"browse snapshot --full",
"browse snapshot --filter submit",
"browse snapshot --max-depth 4",
];

static override flags = {
...driverCommandFlags,
full: Flags.boolean({
description: "Also include the ref maps (xpathMap, urlMap).",
}),
compact: Flags.boolean({
description: "Print only the formatted tree, without ref maps.",
description:
"Deprecated and has no effect; use --full to include the ref maps.",
}),
filter: Flags.string({
description:
Expand All @@ -35,10 +39,15 @@ export default class Snapshot extends BrowseCommand {

async run(): Promise<void> {
const { flags } = await this.parse(Snapshot);
if (flags.compact && process.stderr.isTTY) {
this.warn(
"`--compact` is deprecated and has no effect; use `--full` to include the ref maps.",
);
}
await runDriverCommandFromFlags(
"snapshot",
{
compact: flags.compact,
full: flags.full,
filter: flags.filter,
maxDepth: flags["max-depth"],
},
Expand Down
19 changes: 9 additions & 10 deletions packages/cli/src/lib/driver/commands/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { DriverCommandHandlers } from "./types.js";

export const snapshotHandlers: DriverCommandHandlers = {
async snapshot(manager, params) {
const { compact, filter, maxDepth } = z
const { full, filter, maxDepth } = z
.object({
compact: z.boolean().optional(),
full: z.boolean().optional(),
filter: z.string().optional(),
maxDepth: z.number().int().nonnegative().optional(),
})
Expand All @@ -20,18 +20,17 @@ export const snapshotHandlers: DriverCommandHandlers = {
});

const tree = formatSnapshotTree(snapshot.formattedTree, {
compact,
filter,
maxDepth,
});
if (compact) {
return { tree };
if (full) {
return {
tree,
urlMap: snapshot.urlMap,
xpathMap: snapshot.xpathMap,
};
}

return {
tree,
urlMap: snapshot.urlMap,
xpathMap: snapshot.xpathMap,
};
return { tree };
},
};
32 changes: 32 additions & 0 deletions packages/cli/tests/driver-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { describe, expect, it, vi } from "vitest";

import { resolveSelector } from "../src/lib/driver/commands/selectors.js";
import { formatSnapshotTree } from "../src/lib/driver/commands/snapshot-format.js";
import { snapshotHandlers } from "../src/lib/driver/commands/snapshot.js";
import { runtimeHandlers } from "../src/lib/driver/commands/runtime.js";
import { tabHandlers } from "../src/lib/driver/commands/tabs.js";
import { DRIVER_COMMAND_NAMES } from "../src/lib/driver/commands/types.js";
Expand Down Expand Up @@ -335,6 +336,37 @@ describe("driver commands", () => {
);
});

it("omits ref maps by default and includes them with --full", async () => {
const snap = {
formattedTree: "[0-1] RootWebArea: Test\n [0-2] button: Go",
urlMap: { "0-2": "https://example.com/go" },
xpathMap: { "0-1": "/", "0-2": "/button[1]" },
};
const setRefMaps = vi.fn();
const manager = {
activePage: async () => ({ snapshot: async () => snap }),
setRefMaps,
} as unknown as Parameters<
NonNullable<(typeof snapshotHandlers)["snapshot"]>
>[0];

const lean = await snapshotHandlers.snapshot!(manager, {});
expect(lean).not.toHaveProperty("xpathMap");
expect(lean).not.toHaveProperty("urlMap");
expect(setRefMaps).toHaveBeenCalledTimes(1);
expect(setRefMaps).toHaveBeenLastCalledWith({
urlMap: snap.urlMap,
xpathMap: snap.xpathMap,
});

const full = await snapshotHandlers.snapshot!(manager, { full: true });
expect(full).toMatchObject({
urlMap: snap.urlMap,
xpathMap: snap.xpathMap,
});
expect(setRefMaps).toHaveBeenCalledTimes(2);
});

it("exposes descriptive help for the new driver command surface", async () => {
const commands = [
["open"],
Expand Down
9 changes: 7 additions & 2 deletions packages/evals/core/tools/browse_cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,13 @@ class BrowseCliPageHandle implements CorePageHandle {
}

async represent(): Promise<PageRepresentation> {
// BROWSE_SNAPSHOT_FULL=1 includes the ref maps in the snapshot.
const full = process.env.BROWSE_SNAPSHOT_FULL === "1";
const snapshot = await this.runCommandAfterSelecting<{
tree: string;
xpathMap?: Record<string, string>;
urlMap?: Record<string, string>;
}>(["snapshot"]);
}>(full ? ["snapshot", "--full"] : ["snapshot"]);
const content = snapshot.tree;

return {
Expand All @@ -632,7 +634,10 @@ class BrowseCliPageHandle implements CorePageHandle {
metadata: {
bytes: Buffer.byteLength(content, "utf8"),
tokenEstimate: Math.ceil(content.length / 4),
refCount: Object.keys(snapshot.xpathMap ?? {}).length,
// Count refs from xpathMap when present, else from the tree.
refCount: snapshot.xpathMap
? Object.keys(snapshot.xpathMap).length
: (content.match(/\[\d+-\d+\]/g)?.length ?? 0),
},
raw: snapshot,
};
Expand Down
Loading