Skip to content

Repository files navigation

PlayJev golden retriever logo

PlayJev

Stagehand, but with Jev.
Natural-language browser automation for Playwright, powered by Jev.

CI License: MIT Status: experimental Node 20+ TypeScript

Documentation · Quickstart · API · Evals


What is PlayJev?

PlayJev is a TypeScript library for controlling websites with plain-English instructions while keeping the full Playwright API. Give it an existing Playwright page, then ask it to click controls, navigate websites, fill forms, inspect page state, or choose between visible options.

If you know Stagehand, the shortest explanation is: PlayJev is Stagehand, but with Jev making the browser decisions instead of a generative LLM.

await page.act("Open the pricing page");
await page.act("Complete the contact form", { fields });
await page.check("Did the form submit successfully?");

Important

PlayJev is a new, experimental project—not a production-stable release. The API is actively evolving as it is tested against broader browser benchmarks. It currently requires Chromium/CDP and access to the TypeSafe Jev API.

See it in action

PlayJev running public browser automation evaluations

The silent 48-second recording plays above and shows PlayJev running public Browserbase/Stagehand evaluation pages: selecting checkboxes, operating a custom dropdown, filling a cross-origin iframe form, and navigating across multiple pages. Click it or open the video directly for the higher-quality MP4.

Try it now

You need Node.js 20 or newer and a TypeSafe Jev API key. Sign in to the TypeSafe console to get access to Jev, then install PlayJev and Playwright:

npm install @filed/playjev playwright
npx playwright install chromium

Set the required key in your environment:

export TYPESAFE_API_KEY="your-key-here"

Then use the Quickstart below. To run the included example from source instead:

git clone https://github.com/filedcom/playjev.git
cd playjev
npm install
cp .env.example .env
npm run example

The example opens example.com, asks Jev to identify and follow the explanatory link, and verifies that the destination page loaded.

Quickstart

import { chromium } from "playwright";
import { playjev } from "@filed/playjev";

const browser = await chromium.launch();
const page = playjev(await browser.newPage());

await page.goto("https://example.com");
const action = await page.act("Open the documentation link");
const loaded = await page.check("Is the documentation page open?");

console.log({ action: action.success, loaded });
await browser.close();

The wrapped object still exposes Playwright's page and locator APIs:

await page.getByRole("button", { name: "Sign in" }).click();
await page.act("Choose the workspace with the most recent activity");
await page.screenshot({ path: "workspace.png" });

PlayJev's semantic page.check(question) replaces Playwright's legacy page.check(selector) shorthand. Use page.locator(selector).check() for deterministic checkbox interaction.

Why Jev?

PlayJev keeps the control loop bounded: Playwright owns execution; Jev makes decisions. Jev selects from real browser nodes and a fixed operation vocabulary instead of generating selectors, JavaScript, or arbitrary action objects.

  • No raw HTML trees sent to Jev
  • No generated selectors, JavaScript, or arbitrary action JSON
  • No silent LLM fallback
  • Playwright locators, navigation, events, screenshots, and assertions stay available
  • Shadow DOM, cross-origin iframes, styled controls, and multiple tabs covered by public evals
  • Bulk forms use one batched target-selection request and one verification

Four browser primitives

check() — browser-aware Noul

Ask a yes-or-no question and receive both the answer and probability.

const paid = await page.check("Is this invoice marked paid?");
// { answer: true, probability: 0.97 }

choose() — browser-aware Choice

Choose only among possibilities defined by your code.

const plan = await page.choose("Which plan is selected?", {
  starter: "Starter is selected",
  pro: "Pro is selected",
  enterprise: "Enterprise is selected",
});

rate() — browser-aware Score

Place browser state on an explicit ordered scale.

const completion = await page.rate("How complete is this form?", [
  "Empty",
  "Partially complete",
  "Complete",
]);

act() — composed browser control

Score targets, choose a node and operation, execute it through Playwright, then verify the outcome.

const result = await page.act("Click the Medium pizza-size option", {
  minTargetConfidence: 0.7,
  minVerificationProbability: 0.6,
});

Caller-supplied values stay separate from natural-language instructions:

await page.act("Fill the search box", { value: "climate attribution" });
await page.act("Press Enter in the search box", { key: "Enter" });

Bulk form filling

PlayJev can match and fill a whole form in one target-selection round trip:

await page.act("Complete the contact form", {
  fields: {
    "First Name": "Nunya",
    "Last Name": "Business",
    Email: "test@example.com",
    "Preferred Contact Method": "Phone",
    Message: "Hello from PlayJev",
  },
});

Every value comes from the caller. Jev selects controls; deterministic code performs fills, checks, unchecks, and native option selection.

How it works

┌──────────────────┐     ┌────────────────────┐     ┌─────────────────┐
│ Playwright page  │ ──▶ │ Sparse YAML state  │ ──▶ │ Jev decisions   │
│ AX + frame nodes │     │ numbered + scoped  │     │ Score / Choice  │
└──────────────────┘     └────────────────────┘     └────────┬────────┘
                                                             │
┌──────────────────┐     ┌────────────────────┐              │
│ Verified result  │ ◀── │ Playwright action  │ ◀────────────┘
│ fresh snapshot   │     │ fixed vocabulary   │
└──────────────────┘     └────────────────────┘

Model-facing state looks like this:

page:
  url: https://example.test/contact
  title: Contact
nodes:
  - n: 4
    parent: 2
    type: textbox
    text: First Name
    actionable: true
  - n: 5
    parent: 2
    type: radio
    text: Phone
    state:
      - checked:false
    actionable: true

Internal selectors and browser node IDs never appear in that YAML. They remain in the local snapshot map used to resolve Jev's numbered choice.

Evals

The repository includes outcome-based browser evaluations adapted from public Browserbase Stagehand fixtures, plus a fixed-answer smoke suite drawn from the official WebVoyager dataset.

Evaluation What it covers Result
Checkboxes Labelled native checks ✅
Styled radio Pointer-intercepting control ✅
Hidden input Dynamic reveal and fill ✅
Custom dropdown Multi-step interaction ✅
Shadow DOM Semantic target resolution ✅
Cross-origin iframe Five independent fields ✅
Bulk iframe form Five controls, batched selection ✅
Multiple tabs Popup adoption and navigation ✅

Latest complete local run: 8/8 passed. On that run, the bulk iframe form completed in 0.91s, versus 7.41s for five separate actions. These are observed timings, not guaranteed benchmarks.

The latest WebVoyager golden smoke run passed 4/4 live tasks across Cambridge Dictionary, ArXiv, GitHub, and Wolfram Alpha. Each case requires exact evidence from the live page and a positive Jev check(). This is an integration gate using official tasks, not a score on the full 643-task benchmark.

npm run eval:stagehand
EVAL_FILTER=bulk npm run eval:stagehand
npm run eval:webvoyager:golden

Repository layout

playjev/
├── src/
│   ├── api/            # Playwright Page integration
│   ├── browser/        # capture and sparse browser normalization
│   ├── core/           # Jev-driven control loop
│   ├── errors/         # public error classes
│   ├── jev/            # TypeSafe API client
│   ├── serialization/  # model-facing YAML
│   └── types/          # public TypeScript contracts
├── docs/               # VitePress docs and landing page
├── evals/stagehand/    # public browser outcome evaluations
├── evals/webvoyager/   # official fixed-answer golden smoke
├── examples/           # focused runnable examples
└── tests/unit/         # deterministic unit tests

Development

npm install
npm run validate       # types, unit tests, package build, docs build
npm run docs:dev       # local documentation site
npm run eval:stagehand # live Jev + browser evaluations
npm run eval:webvoyager:golden # official WebVoyager golden smoke

See CONTRIBUTING.md for the development workflow and SECURITY.md for private vulnerability reporting.

Acknowledgements

PlayJev is built on Playwright and Jev by TypeSafe. Its API design is inspired by the clarity of Stagehand, but the decision architecture and public primitives are Jev-native. PlayJev is independent and is not affiliated with Browserbase or Stagehand.

License

MIT

About

Fast, typed browser automation powered by Jev and Playwright

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages