stagehand-plasmate

April 12, 2026 ยท View on GitHub

Plasmate SOM integration for Stagehand. 10-100x token compression for browser automation.

Stagehand is a TypeScript SDK for browser automation with three core primitives: act(), extract(), and observe(). Plasmate provides 10-100x token compression via its Semantic Object Model (SOM).

This integration lets Stagehand use Plasmate's SOM instead of raw DOM, dramatically reducing token costs for LLM-powered browser automation.

Installation

npm install stagehand-plasmate @anthropic-ai/stagehand

You also need Plasmate installed:

# macOS / Linux
curl -fsSL https://plasmate.app/install.sh | sh

# Or with cargo
cargo install plasmate

Quick Start

import { Stagehand } from '@anthropic-ai/stagehand'
import { PlasmatePage } from 'stagehand-plasmate'

const stagehand = new Stagehand()
await stagehand.init()

// Wrap Stagehand's page with PlasmatePage
const page = new PlasmatePage(stagehand.page)

await page.goto('https://example.com')

// Get SOM instead of raw DOM (10-100x smaller)
const som = await page.toSOM()
console.log(`Compression: ${som.meta.html_bytes / som.meta.som_bytes}x`)

// Extract with token-efficient context
const { context, stats } = await page.extract({
  instruction: 'Extract the main heading and navigation links',
})
console.log(`Saved ~${Math.round((1 - 1/stats.ratio) * 100)}% tokens`)

// act() and observe() pass through to Stagehand
await page.act('Click the login button')

Why Plasmate + Stagehand?

MetricRaw DOMWith Plasmate
Token usage~50,000~3,000
Cost per page$0.15$0.01
Context windowExceeds limitsFits easily

Plasmate converts HTML to a Semantic Object Model (SOM) - a structured JSON format that preserves semantic meaning while stripping formatting noise.

Before: Raw DOM (50KB+)

<div class="nav-wrapper flex items-center justify-between px-4 py-2 bg-white shadow-sm">
  <a href="/" class="flex items-center gap-2 font-bold text-xl text-gray-900 hover:text-blue-600">
    <img src="/logo.svg" class="h-8 w-8" alt="Logo">
    <span>Acme Inc</span>
  </a>
  <!-- ... thousands more lines ... -->
</div>

After: Plasmate SOM (3KB)

{
  "regions": [{
    "role": "navigation",
    "elements": [{
      "id": "e1",
      "role": "link",
      "text": "Acme Inc",
      "attrs": { "href": "/" },
      "actions": ["click"]
    }]
  }]
}

API Reference

PlasmatePage

Wraps a Stagehand page with SOM-based methods.

const page = new PlasmatePage(stagehand.page, {
  binary: 'plasmate',      // Path to plasmate binary
  enableCache: true,       // Cache SOM results (default: true)
  cacheTTL: 30000,         // Cache TTL in ms (default: 30s)
  // Or use Plasmate Cloud:
  apiEndpoint: 'https://api.plasmate.app',
  apiKey: 'your-api-key',
})

Methods

MethodDescription
goto(url)Navigate to URL (clears cache)
toSOM()Get page as Plasmate SOM
getCompressionStats()Get compression statistics
extract(options)Extract structured context
getLinks()Get all links from SOM
getInteractiveElements()Get buttons, inputs, etc.
getText()Get page text content
findById(id)Find element by SOM ID
findByRole(role)Find elements by role
findByText(text)Find elements containing text
act(action)Pass-through to Stagehand act()
observe(instruction)Pass-through to Stagehand observe()
clearCache()Clear SOM cache

PlasmateClient

Low-level client for Plasmate CLI or Cloud API.

import { PlasmateClient } from 'stagehand-plasmate'

const client = new PlasmateClient()

// Fetch URL directly
const som = await client.fetchSOM('https://example.com')

// Process HTML
const som = await client.processHTML(html, 'https://example.com')

Extraction Helpers

import {
  getLinks,
  getInteractiveElements,
  extractAllText,
  findElementById,
  findElementsByRole,
  findElementsByText,
} from 'stagehand-plasmate'

const som = await page.toSOM()

// Get all links
const links = getLinks(som)
// [{ id: "e1", text: "Home", href: "/" }, ...]

// Get interactive elements
const interactive = getInteractiveElements(som)
// [{ id: "e5", role: "button", label: "Submit", actions: ["click"] }, ...]

// Find elements
const buttons = findElementsByRole(som, 'button')
const loginButton = findElementsByText(som, 'Login')[0]

SOM Types

interface SOM {
  som_version: string
  url: string
  title: string
  lang: string
  regions: SOMRegion[]
  meta: SOMMeta
  structured_data?: StructuredData
}

interface SOMRegion {
  id: string
  role: 'navigation' | 'main' | 'aside' | 'header' | 'footer' | 'form' | 'dialog' | 'content'
  label?: string
  elements: SOMElement[]
}

interface SOMElement {
  id: string
  role: 'link' | 'button' | 'text_input' | 'textarea' | 'select' | 'checkbox' | 'radio' | 'heading' | 'image' | 'list' | 'table' | 'paragraph' | 'section' | 'separator' | 'details'
  text?: string
  label?: string
  actions?: string[]
  attrs?: Record<string, unknown>
  children?: SOMElement[]
  hints?: string[]
  html_id?: string
}

interface SOMMeta {
  html_bytes: number
  som_bytes: number
  element_count: number
  interactive_count: number
}

Examples

Extract Pricing Data

import { Stagehand } from '@anthropic-ai/stagehand'
import { PlasmatePage } from 'stagehand-plasmate'

const stagehand = new Stagehand()
await stagehand.init()

const page = new PlasmatePage(stagehand.page)
await page.goto('https://stripe.com/pricing')

const { context, stats } = await page.extract({
  instruction: 'Extract pricing tiers with names, prices, and features',
})

console.log(`Compression: ${stats.ratio.toFixed(1)}x`)
console.log(`Token savings: ~${Math.round((1 - 1/stats.ratio) * 100)}%`)
console.log(context)

await stagehand.close()

Form Automation

const page = new PlasmatePage(stagehand.page)
await page.goto('https://example.com/login')

// Find form elements efficiently
const inputs = await page.findByRole('text_input')
const submitButton = (await page.findByText('Submit'))[0]

console.log('Form inputs:', inputs.map(i => i.label))

// Use Stagehand for interactions
await page.act('Type "user@example.com" in the email field')
await page.act('Type "password123" in the password field')
await page.act('Click Submit')

Compare Token Usage

import { estimateTokens, tokenComparison } from 'stagehand-plasmate'

const stats = await page.getCompressionStats()
const comparison = tokenComparison(stats)

console.log(`HTML tokens:  ~${comparison.htmlTokens.toLocaleString()}`)
console.log(`SOM tokens:   ~${comparison.somTokens.toLocaleString()}`)
console.log(`Saved:        ~${comparison.savedTokens.toLocaleString()} (${comparison.savingsPercent}%)`)

Plasmate Cloud

For production use, consider Plasmate Cloud for:

  • No binary installation required
  • Faster processing with edge deployment
  • Built-in caching and rate limiting
  • Usage analytics
const page = new PlasmatePage(stagehand.page, {
  apiEndpoint: 'https://api.plasmate.app',
  apiKey: process.env.PLASMATE_API_KEY,
})

License

Apache-2.0