Skip to content
Closed
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
21 changes: 21 additions & 0 deletions .github/workflows/stagehand-codemode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ on:
pull_request:
paths:
- '.github/workflows/stagehand-codemode.yml'
- 'examples/integrations/vercel/stagehand-codemode/**'
- 'packages/stagehand-codemode/**'
- 'pnpm-workspace.yaml'
push:
branches: [main]
paths:
- '.github/workflows/stagehand-codemode.yml'
- 'examples/integrations/vercel/stagehand-codemode/**'
- 'packages/stagehand-codemode/**'
- 'pnpm-workspace.yaml'

permissions:
contents: read
Expand All @@ -30,3 +34,20 @@ jobs:
- run: pnpm install --no-frozen-lockfile
- run: pnpm --filter @browserbasehq/stagehand-codemode run typecheck
- run: pnpm --filter @browserbasehq/stagehand-codemode run build

vercel:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10.9.0
- uses: actions/setup-node@v4
with:
node-version: 24.x

- run: pnpm install --no-frozen-lockfile
- run: pnpm --filter @browserbasehq/stagehand-codemode run build
- run: pnpm --filter stagehand-codemode-ai-sdk-example run check
- run: pnpm --filter stagehand-codemode-ai-sdk-example run smoke
10 changes: 10 additions & 0 deletions examples/integrations/vercel/stagehand-codemode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Vercel AI SDK + Stagehand code mode

This binding hosts `StagehandCodeExecutor` directly in the agent process and exposes it as one AI
SDK tool. It does not launch MCP because the AI SDK already has a native tool contract.

The agent receives the shared Stagehand V4 syntax skill through `system`, while the tool description
contains the same reference for tool-level context. One binding instance must be reused for the
whole `generateText` call and closed afterward.

See [`src/agent.ts`](./src/agent.ts).
18 changes: 18 additions & 0 deletions examples/integrations/vercel/stagehand-codemode/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "stagehand-codemode-ai-sdk-example",
"private": true,
"type": "module",
"scripts": {
"check": "tsc --noEmit",
"smoke": "tsx src/smoke.ts"
},
"dependencies": {
"@browserbasehq/stagehand-codemode": "workspace:*",
"ai": "^7.0.51"
},
"devDependencies": {
"@types/node": "^25.0.9",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}
24 changes: 24 additions & 0 deletions examples/integrations/vercel/stagehand-codemode/src/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { generateText, stepCountIs, type LanguageModel } from 'ai';
import { STAGEHAND_CODEMODE_SKILL } from '@browserbasehq/stagehand-codemode';
import { createStagehandTool } from '@browserbasehq/stagehand-codemode/ai-sdk';

export async function runStagehandAgent(
model: LanguageModel,
prompt: string
): Promise<string> {
const stagehand = createStagehandTool({
browserbaseApiKey: process.env.BROWSERBASE_API_KEY,
});
try {
const result = await generateText({
model,
system: STAGEHAND_CODEMODE_SKILL,
prompt,
tools: { code_execute: stagehand.tool },
stopWhen: stepCountIs(8),
});
return result.text;
} finally {
await stagehand.close();
}
}
28 changes: 28 additions & 0 deletions examples/integrations/vercel/stagehand-codemode/src/smoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { STAGEHAND_CODEMODE_SKILL } from '@browserbasehq/stagehand-codemode';
import { createStagehandTool } from '@browserbasehq/stagehand-codemode/ai-sdk';

const stagehand = createStagehandTool({});
try {
if (typeof stagehand.tool.execute !== 'function') {
throw new Error('AI SDK binding did not expose an executable tool.');
}
const description = stagehand.tool.description;
if (
typeof description !== 'string' ||
!description.includes('Stagehand V4 code-mode syntax')
) {
throw new Error(
'AI SDK tool description did not include the Stagehand syntax skill.'
);
}
if (!STAGEHAND_CODEMODE_SKILL.includes('stagehand.extract')) {
throw new Error(
'AI SDK system instructions did not load the Stagehand syntax skill.'
);
}
process.stdout.write(
'Vercel AI SDK native tool binding PASS: code_execute\n'
);
} finally {
await stagehand.close();
}
11 changes: 11 additions & 0 deletions examples/integrations/vercel/stagehand-codemode/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"types": ["node"],
"noEmit": true
},
"include": ["src/**/*.ts"]
}
21 changes: 21 additions & 0 deletions packages/stagehand-codemode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,24 @@ globals, and in-process SDK state available to that process.
Use Stagehand Code Mode only with trusted agents in a trusted execution environment. Applications
that execute untrusted code must provide a real isolation boundary, such as a restricted container,
virtual machine, or purpose-built code sandbox.

## Vercel AI SDK

```ts
import { generateText } from 'ai';
import { createStagehandTool } from '@browserbasehq/stagehand-codemode/ai-sdk';

const stagehand = createStagehandTool({
browserbaseApiKey: process.env.BROWSERBASE_API_KEY,
});

try {
await generateText({
model,
tools: { code_execute: stagehand.tool },
prompt: 'Open example.com and return its title.',
});
} finally {
await stagehand.close();
}
```
13 changes: 11 additions & 2 deletions packages/stagehand-codemode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
"name": "@browserbasehq/stagehand-codemode",
"version": "0.0.0",
"private": true,
"description": "Local stdio MCP tool for Stagehand code mode",
"description": "Local stdio MCP and agent-tool bindings for Stagehand code mode",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./ai-sdk": {
"types": "./dist/ai-sdk.d.ts",
"import": "./dist/ai-sdk.js"
}
},
"scripts": {
Expand All @@ -19,15 +23,20 @@
"zod": "^4.4.3"
},
"peerDependencies": {
"@browserbasehq/stagehand": "*"
"@browserbasehq/stagehand": "*",
"ai": ">=5.0.0"
},
"peerDependenciesMeta": {
"@browserbasehq/stagehand": {
"optional": true
},
"ai": {
"optional": true
}
},
"devDependencies": {
"@types/node": "^25.0.9",
"ai": "^7.0.51",
"tsdown": "^0.15.4",
"typescript": "^5.9.3"
},
Expand Down
30 changes: 30 additions & 0 deletions packages/stagehand-codemode/src/ai-sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { tool, type Tool } from 'ai';
import {
StagehandCodeExecutor,
type StagehandCodeExecutorOptions,
} from './executor.js';
import {
CODE_EXECUTE_DESCRIPTION,
codeExecuteSchema,
} from './tool-contract.js';

export type StagehandToolBinding = {
tool: Tool;
close(): Promise<void>;
};

export function createStagehandTool(
options: StagehandCodeExecutorOptions
): StagehandToolBinding {
const executor = new StagehandCodeExecutor(options);
const stagehandTool = tool({
description: CODE_EXECUTE_DESCRIPTION,
inputSchema: codeExecuteSchema,
execute: (input, execution) =>
executor.execute(input, execution.abortSignal),
});
return {
tool: stagehandTool,
close: () => executor.close(),
};
}
2 changes: 1 addition & 1 deletion packages/stagehand-codemode/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from 'tsdown';

export default defineConfig({
entry: ['src/index.ts', 'src/stdio-server.ts'],
entry: ['src/index.ts', 'src/ai-sdk.ts', 'src/stdio-server.ts'],
format: ['esm'],
platform: 'node',
target: 'node22',
Expand Down
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
packages:
- 'examples/*'
- 'examples/integrations/vercel/stagehand-codemode'
- 'packages/*'
Loading