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?
| Metric | Raw DOM | With Plasmate |
|---|---|---|
| Token usage | ~50,000 | ~3,000 |
| Cost per page | $0.15 | $0.01 |
| Context window | Exceeds limits | Fits 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
| Method | Description |
|---|---|
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