Skip to content

evals: add the stagehand_code code-mode exposure - #2591

Merged
miguelg719 merged 1 commit into
evals-v4-rootfrom
stg-2671-v4-code-exposure
Aug 9, 2026
Merged

evals: add the stagehand_code code-mode exposure#2591
miguelg719 merged 1 commit into
evals-v4-rootfrom
stg-2671-v4-code-exposure

Conversation

@shriyatheunicorn

@shriyatheunicorn shriyatheunicorn commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

The agent-facing half of the stagehand_code code-mode surface:
prepareLLMExposure initializes a v4 Stagehand client via initStagehand
and declares a code_handles agent mount — stagehand/page/z in the
snippet scope, surface-owned prompt instructions and run-tool copy,
final-state evidence capture (screenshot + URL + aria tree), and cleanup
that closes the browser as well as the client (stagehand.close() alone
leaves the browser running). Nothing consumes the mount here; the
claude_code adapter does in #2596 and codex in #2609.

Bottom of the nondeterministic-evals stack (#2591#2611, with the
merge-readiness fixes in #2649 and the MCP tool surfaces in #2650
stacked on top).

⚠️ Base housekeeping before merge: this PR still targets
evals-v4-root, which has since merged (via #2570#2494) into
v4-spike. Rebase onto current v4-spike and retarget. One known
reconciliation: v4-spike removed the EVAL_VERIFIER_MODEL override
from verifierAdapter (2af557b) as redundant with V3Evaluator's own
constructor options; this stack's later PRs still carry it — drop it
during the rebase rather than reintroducing it.


Summary by cubic

Adds the stagehand_code code-mode tool and registers it in the core tool registry, wiring a v4 @browserbasehq/stagehand client with scoped handles, run-tool execution, artifact capture, and full teardown. Enables the v4 code-mode surface for STG-2671 with a fixed SDK model and local/Browserbase startup profiles.

  • New Features
    • New StagehandCodeTool (id: stagehand_code) with session, navigation, evaluation, screenshot, viewport, wait, click, hover, scroll, type, press, tabs, and representation.
    • Targets: selector, coords, focused.
    • Agent mount via handles: stagehand, page, and z; scoped prompt instructions; run-tool description, code param description, and deny message. Snippets also get startUrl, task, and console in scope.
    • Captures screenshot, URL, and ARIA tree; cleanup closes both the Stagehand client and the browser.
    • Profiles: tool_launch_local and tool_create_browserbase; connection mode derived; model fixed to openai/gpt-4.1-mini.
    • Registered in listCoreTools/getCoreTool; tests assert stagehand_code is retrievable and that prompt guidance includes awaited locator actions.

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

Review in cubic

@changeset-bot

changeset-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e579140

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

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

All reported issues were addressed across 1 file

Architecture diagram
sequenceDiagram
    participant HAR as External Harness
    participant PREP as prepareLLMExposure()
    participant INIT as initStagehand()
    participant SDK as Stagehand v4 SDK
    participant BROWSER as Local/Browserbase Browser
    participant AGENT as Agent (LLM)

    Note over HAR,AGENT: NEW: v4 Code-Mode Exposure Flow

    HAR->>PREP: prepareLLMExposure(plan, env, logger, profile?)
    alt profile not provided
        PREP->>PREP: Resolve profile based on env (LOCAL or BROWSERBASE)
    else invalid profile
        PREP->>PREP: Throw EvalsError
    end
    PREP->>INIT: initStagehand(modelName, environment)
    INIT-->>PREP: Return {stagehand, page}
    PREP->>PREP: Extract stagehand from v4

    Note over PREP: Build LLMExposure with code_handles kind

    PREP-->>HAR: Return LLMExposure
    HAR->>AGENT: Pass exposure to agent

    loop Agent uses run tool
        AGENT->>AGENT: Execute code against SDK (page.goto, stagehand.act, etc.)
        SDK->>BROWSER: Browser actions
        BROWSER-->>SDK: Results
        SDK-->>AGENT: Return values
    end

    opt Final state capture
        HAR->>HAR: captureFinalState()
        HAR->>HAR: Take screenshot (best-effort)
        HAR->>HAR: Get current URL (best-effort)
    end

    opt Cleanup
        HAR->>HAR: cleanup()
        HAR->>HAR: stagehand.close() (RPC client)
        HAR->>HAR: browser.close() (browser process)
    end

    Note over PREP: Surface model pinned to openai/gpt-4.1-mini
Loading

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

Re-trigger cubic

Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
Comment thread packages/evals/core/tools/v4_code.ts Outdated
Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
Comment thread packages/evals/core/tools/stagehand_code.ts Outdated
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from 6191a09 to 3c4764c Compare August 5, 2026 07:14
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from 3c4764c to 3633309 Compare August 5, 2026 08:11
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from 3633309 to 75c3d64 Compare August 5, 2026 08:28
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch 2 times, most recently from 64bf897 to f728be5 Compare August 5, 2026 19:06
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from f728be5 to 7a41c66 Compare August 5, 2026 22:40
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from 7a41c66 to d52e9cf Compare August 6, 2026 02:16
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from d52e9cf to d750c9c Compare August 6, 2026 02:46
@miguelg719
miguelg719 force-pushed the stg-2671-v4-code-exposure branch from d750c9c to 2c5b548 Compare August 6, 2026 22:18
Base automatically changed from stg-2671-llm-exposure-contract to evals-v4-root August 6, 2026 23:12
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from 2c5b548 to 49c3c34 Compare August 6, 2026 23:12
@miguelg719
miguelg719 force-pushed the stg-2671-v4-code-exposure branch from 49c3c34 to a152da0 Compare August 6, 2026 23:17
@shriyatheunicorn shriyatheunicorn self-assigned this Aug 8, 2026
@shriyatheunicorn
shriyatheunicorn force-pushed the stg-2671-v4-code-exposure branch from a152da0 to e579140 Compare August 8, 2026 04:49
@miguelg719 miguelg719 changed the title evals: add the v4_code code-mode exposure evals: add the stagehand_code code-mode exposure Aug 9, 2026
@miguelg719
miguelg719 merged commit af74e90 into v4-spike Aug 9, 2026
21 checks passed
miguelg719 added a commit that referenced this pull request Aug 9, 2026
## Summary

Corrects the base-target mistake from #2590 by landing its merged
agent-mount contract on v4-spike.

This branch is exactly current v4-spike plus the #2590 squash commit:
- AgentMount is the lifecycle-free agent delivery binding
- ToolStartResult owns the single runtime lifecycle and captureEvidence
- contract tests cover independent surface/mount modalities

After merge, #2591 and the remaining stack will be rebased directly onto
v4-spike.

## Verification

- pnpm --filter @browserbasehq/stagehand-evals typecheck
- contract and downstream stack suites validated during restack

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Promotes the agent-mount tool-surface contract to `v4-spike`, adding
type-safe agent delivery bindings and best-effort evidence capture,
independent of `CoreTool.surface`. Types and tests only; no runtime
changes.

- **New Features**
- `ToolStartResult` adds optional `agentMount` and `captureEvidence():
Promise<ProbeEvidence>`.
- New `AgentMount` union (`via: handles | mcp | cli`) with
`promptInstructions`; plus `AgentRunToolSpec`.
- Constants: `AGENT_RUN_TOOL_SERVER`, `AGENT_RUN_TOOL_NAME`,
`AGENT_RUN_TOOL_RESERVED_HANDLES`. Enums: `ToolSurface` adds
`stagehand_code`; `CoreTool.family` adds `stagehand`.
- Tests (`packages/evals/tests/core/tool-contract.test.ts`) validate
surface/delivery independence, handle mounts, and evidence capture.

<sup>Written for commit fd74210.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2635?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: shriyatheunicorn <shriavj@gmail.com>
miguelg719 added a commit that referenced this pull request Aug 9, 2026
The surface now owns its agent mount: prompt instructions (carried over
verbatim from the per-surface block inside the claude_code tool
adapter), a code_handles declaration, and captureEvidence() returning
the terminal screenshot, URL, and aria tree. Behavior is unchanged until
the adapter consumes mounts generically in #2596.

Part of the nondeterministic-evals stack (#2591#2611).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Make `playwright_code` agent-mountable so the v4 harness can consume it
generically, with handle exposure, prompt wiring, runTool config, and
best-effort evidence capture. No behavior change; supports STG-2671’s
non-deterministic suite.

- **New Features**
- Added `agentMount` with handle access (`page`, `context`, `browser`),
prompt instructions (incl. first-action guidance), and `runTool` config
(async snippet scope: `page`, `context`, `browser`, `startUrl`, `task`,
`console`; return JSON-serializable values; deny message references
`AGENT_RUN_TOOL_NAME`).
- Added `captureEvidence` typed as `ProbeEvidence` (screenshot, URL,
`ariaTree` via `page.represent({ includeIframes: true })` with
best-effort per modality); track active page on `"page"` events; ensured
initial page exists; unified cleanup.

<sup>Written for commit 9cf16fb.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2592?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
miguelg719 added a commit that referenced this pull request Aug 9, 2026
Same shape as #2592 for the CDP surface: prompt instructions carried
over verbatim, code_handles declaration, captureEvidence() for the
terminal screenshot, URL, and aria tree. Behavior is unchanged until the
adapter consumes mounts generically in #2596.

Part of the nondeterministic-evals stack (#2591#2611).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Expose `cdp_code` as an `LLMExposure` with an agent-mountable `cdp`
runtime, clearer prompt instructions, and richer final-state evidence.
Linked to Linear STG-2671 (part 5/6). Requires the `LLMExposure`
contract (1/6).

- **New Features**
- Agent mount: `handles.cdp`; prompt includes `AGENT_RUN_TOOL_NAME`, a
first navigate/load recipe, run-tool description and code param, and a
deny message; `captureEvidence` collects screenshot, URL, and
`ariaTree`.
- Runtime: `send`/`browser` plus `on`/`off`/`once`/`waitForEvent`/`wait`
with session scoping, timeouts, safe teardown, and unhandled-rejection
guards; exposes `targetId`/`sessionId`; `createAgentRuntime(logger)`
initializes it.
- Representation: adds `representation` capability; `page.represent()`
returns the accessibility tree with metadata; evidence includes
`ariaTree`.
- Page management: `syncPages` keeps session pages current before
`listPages`/`activePage`; bootstrap only creates a target if none exist.

<sup>Written for commit e641edf.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2595?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
miguelg719 added a commit that referenced this pull request Aug 9, 2026
…er (#2596)

Replaces the per-surface logic inside the adapter with one generic
mount consumer: any surface declaring an agent mount gets its handles
mounted as the single local-MCP run tool, with the surface's own prompt
instructions. Net deletion — the bespoke playwright_code/cdp_code blocks
move behind the contract, and adding a surface no longer means editing
the adapter.

Part of the nondeterministic-evals stack (#2591#2611). Requires the
three mount declarations below it.

Verified across the stack: tsc and oxlint per part, evals vitest, live
core runs on Browserbase for --tool stagehand_code, playwright_code, and
cdp_code (3/3 each, prompt exit), one long-horizon row (webvoyager x
claude_code x stagehand_code, 3/3), and the paired webvoyager acceptance
run (stagehand_code 2/3 vs playwright_code 3/3, both cells traced to
Braintrust).

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Consume LLM exposure mounts generically in the `claude_code` adapter so
all code surfaces expose their handles as a single MCP run tool. This
aligns with STG-2671 and adds optional evidence capture while removing
surface-specific logic.

- **New Features**
- Generic agent mount consumption across `playwright_code`, `cdp_code`,
and `stagehand_code`; prompt text, run-tool copy/deny messages, and
`task`/`console` bindings come from the mount.
- Optional `captureEvidence()` for grading (timeout
`EVAL_CAPTURE_EVIDENCE_TIMEOUT_MS`); standardized identifiers via
`AGENT_RUN_TOOL_NAME` and `AGENT_RUN_TOOL_SERVER`.

- **Refactors**
- One mount consumer replaces bespoke surface code; snippets run with
handle names plus `startUrl`/`task`/`console`. Re-exports
`waitForCdpEvent`.
- Shared tool/target lifecycle via `startAgentToolRuntime` with
idempotent cleanup (timeout `EVAL_AGENT_MOUNT_CLEANUP_TIMEOUT_MS`);
startup profiles: `browse_cli`/`stagehand_code` own the browser,
`playwright_code`/`cdp_code` expect runner-provided CDP. Improved
`browse_cli` build-artifacts error.

<sup>Written for commit 5a1a4ea.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2596?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
miguelg719 added a commit that referenced this pull request Aug 9, 2026
## Summary

The remaining merge blockers for the #2591#2611 stack, implemented per
the review conclusions (verifier fallback accepted on empirical
evidence; GAIA removal decided; Odysseys rubric item dropped as
irrelevant).

- **Gate insurance for the verifier fallback**: verifier-failed rows
(`verifierError` set, `_success` self-reported) now count as **ungraded
runs** on their arm. The per-arm verifiability line reports them, and
when `EVAL_MAX_UNVERIFIABLE_CRITERIA` is configured, *any* ungraded run
fails the batch. This changes no grading behavior — it converts "the
fallback hasn't fired in hundreds of runs" into "if it ever fires, the
batch fails loudly instead of publishing self-graded rows."
- **GAIA fully removed** (dataset, suite builder, `index.eval.ts`
fan-out, category overrides, legacy-only sets, help text, README).
`b:gaia` → "Unknown benchmark", instead of a half-present suite pointing
at a broken `--legacy` path.
- **Planner**: a stagehand-harness selection that reduces to *nothing
runnable* (suite name, agent category, dataset shorthand) errors with
harness guidance instead of planning zero cases; broad targets still
omit suites and plan the rest.
- **Codex step budget**: tool steps (command executions + MCP tool
calls) capped via stream abort — `EVAL_CODEX_MAX_STEPS`, falling back to
the pre-migration `AGENT_EVAL_MAX_STEPS`, default 50. Runs get a
citable, reproducible action budget.
- **Per-dataset prompt constraints restored** from the retired suite
modules: OnlineMind2Web's stay-on-site rule (validity: tasks were
passable by googling the answer) and the navigate guidance for
WebTailBench/Odysseys.
- **Tag hygiene**: `dom/hybrid/cua` stripped from external-harness
testcases so Braintrust tag filters don't pool them with historical
Stagehand agent rows.
- **stagehand_code screenshot fix** (ported from the facade worktree):
v4 SDK returns `Uint8Array`; `Buffer.isBuffer` consumers silently
dropped screenshots. Now normalized to `Buffer`.

## Deliberately not here

- **Pre-navigation to `startUrl`**: pending an explicit protocol
decision (self-navigation as the documented norm vs. pre-goto for
cross-era comparability).
- **The facade's `>=` relaxation of the codex observation guard**: kept
strict `===` — the relaxation reintroduces positional misattribution;
the underlying trailing-capture undercount needs an attempted-run count
in the recorder (follow-up with the facade work).
- MCP tool surfaces and the Playwright facade: follow-up stack.

## Verification

typecheck clean · 416/416 unit tests (3 GAIA-era tests updated to the
new behavior, 1 new gate test) · oxfmt clean

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Hardens external-harness evals for merge: enforces strict verifier
gating, removes GAIA and the legacy runner/flag, adds a Codex step
budget, restores dataset prompt guidance, fixes screenshot handling, and
bounds setup-failure cleanup timeouts. Also cleans up docs/payloads,
fixes a Codex abort-listener leak in long batches, tightens a dry-run
test assertion, and removes the `--provider` flag (provider now derived
from the model id).

- **New Features**
- Verifier gate: verifier-failed rows now count as ungraded; any
ungraded run fails a gated batch. Per-arm report shows ungraded counts.
Respects EVAL_MAX_UNVERIFIABLE_CRITERIA.
- Codex step budget: cap tool steps via EVAL_CODEX_MAX_STEPS (fallback
AGENT_EVAL_MAX_STEPS, default 50) with stream abort and clear stop
reason.
- Restored dataset guidance in prompts: OnlineMind2Web stay-on-site
rule; navigation hints for WebTailBench and Odysseys.
- Planner: selecting `stagehand` harness that leaves nothing runnable
now errors with guidance instead of planning zero cases.

- **Refactors**
- GAIA fully removed; `b:gaia` now reports “Unknown benchmark.”
`b:osworld` also reports “Unknown benchmark.”
- Removed legacy runner and `--legacy` flag; deleted legacy files and
commands. Dry-run payload drops skippedTasks; preview/help text,
`ARCHITECTURE.mmd`, and README updated.
- Removed `--provider` and provider-based model filtering; provider
metadata is inferred from `provider/model`. Dropped EVAL_PROVIDER env,
config/help surfaces, and related tests.
- Tag hygiene: strip `dom`, `hybrid`, `cua` from external-harness
testcases to keep Braintrust filters separated from legacy Stagehand
runs.
- `stagehand_code` screenshot bytes normalized to `Buffer` to fix
dropped screenshots with the v4 SDK.
- Codex runner: detach the abort forwarder after each run to avoid
accumulating listeners in long batches.
- External-harness adapters: apply the normal teardown timeout
(EVAL_AGENT_MOUNT_CLEANUP_TIMEOUT_MS) to setup-failure cleanup to
prevent wedging rows.
- Docs: dry-run diagram lists the actual payload fields (target,
normalizedTarget, tasks, envOverrides, runOptions, matrix, error?).
- Tests: broad dry-run now asserts the unique planned task set to avoid
model-multiplicity noise.

<sup>Written for commit 66bc69d.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2649?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

---

**Update (second commit)**: the legacy runner is removed outright.
`--legacy` could no longer run anything on this branch (a/e/o need the
v4 harness context it never builds; the agent modules and suite targets
it resolves are gone), so the escape hatch was routing users to a
guaranteed failure. Deleted: `cli-legacy.ts`, `index.eval.ts`,
`args.ts`, `tui/commands/legacy.ts`, the `evals:old` script, the
`--legacy` flag, `LEGACY_ONLY_BENCHMARKS` (`b:osworld` now reports
"Unknown benchmark"), and the legacy-skip plumbing in the run command,
dry-run payload, and preview. Net −1,885 lines. Gates re-run green
(typecheck, 416/416, oxfmt).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants