Skip to content

Commit 3da310a

Browse files
committed
fix conflict
2 parents 4eb2af1 + 08b19bd commit 3da310a

File tree

17 files changed

+478
-39
lines changed

17 files changed

+478
-39
lines changed

.changeset/khaki-numbers-fly.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# @browserbasehq/stagehand
22

3+
## 3.0.0
4+
5+
### Major Changes
6+
7+
- Removes internal Playwright dependency
8+
- A generous 20-40% speed increase across `act`, `extract`, & `observe` calls
9+
- Compatibility with Playwright, Puppeteer, and Patchright
10+
- Automatic action caching (agent, stagehand.act). Go from CUA → deterministic scripts w/o inference
11+
- A suite of non AI primitives:
12+
- `page`
13+
- `locator` (built in closed mode shadow root traversal, with xpaths & css selectors)
14+
- `frameLocator`
15+
- `deepLocator` (crosses iframes & shadow roots)
16+
- bun compatibility
17+
- Simplified extract schemas
18+
- CSS selector support (id-based support coming soon)
19+
- Targeted extract and observe across iframes & shadow roots
20+
- More intuitive type names (observeResult is now action, act accepts an instruction string instead of an action string, solidified ModelConfiguration)
21+
22+
Check the [migration guide](https://docs.stagehand.dev/v3/migrations/v2) for more information
23+
24+
325
## 2.5.0
426

527
### Minor Changes

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ At a high level, we're focused on improving reliability, speed, and cost in that
129129

130130
## Acknowledgements
131131

132-
This project heavily relies on [Playwright](https://playwright.dev/) as a resilient backbone to automate the web. It also would not be possible without the awesome techniques and discoveries made by [tarsier](https://github.com/reworkd/tarsier), [gemini-zod](https://github.com/jbeoris/gemini-zod), and [fuji-web](https://github.com/normal-computing/fuji-web).
133-
134132
We'd like to thank the following people for their major contributions to Stagehand:
135133
- [Paul Klein](https://github.com/pkiv)
136134
- [Sean McGuire](https://github.com/seanmcguire12)

packages/core/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# @browserbasehq/stagehand
2+
3+
## 3.0.1
4+
5+
### Patch Changes
6+
7+
- [#1207](https://github.com/browserbase/stagehand/pull/1207) [`55da8c6`](https://github.com/browserbase/stagehand/commit/55da8c6e9575cbad3246c55b17650cf6b293ddbe) Thanks [@miguelg719](https://github.com/miguelg719)! - Fix broken links to quickstart docs
8+
9+
- [#1200](https://github.com/browserbase/stagehand/pull/1200) [`0a5ee63`](https://github.com/browserbase/stagehand/commit/0a5ee638bde051d109eb2266e665934a12f3dc31) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - log info when scope narrowing selector fails
10+
11+
- [#1205](https://github.com/browserbase/stagehand/pull/1205) [`ee76881`](https://github.com/browserbase/stagehand/commit/ee7688156cb67a9f0f90dfe0dbab77423693a332) Thanks [@miguelg719](https://github.com/miguelg719)! - Update README.md, add Changelog for v3
12+
13+
- [#1209](https://github.com/browserbase/stagehand/pull/1209) [`9e95add`](https://github.com/browserbase/stagehand/commit/9e95add37eb30db4f85e73df7760c7e63fb4131e) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix circular import in exported aisdk example client
14+
15+
- [#1211](https://github.com/browserbase/stagehand/pull/1211) [`98e212b`](https://github.com/browserbase/stagehand/commit/98e212b27887241879608c6c1b6c2524477a40d7) Thanks [@miguelg719](https://github.com/miguelg719)! - Add an example for passing custom tools to agent
16+
17+
- [#1206](https://github.com/browserbase/stagehand/pull/1206) [`d5ecbfc`](https://github.com/browserbase/stagehand/commit/d5ecbfc8e419a59b91c2115fd7f984378381d3d0) Thanks [@miguelg719](https://github.com/miguelg719)! - Export example AISdkClient properly from the stagehand package

packages/core/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<div id="toc" align="center" style="margin-bottom: 0;">
2+
<ul style="list-style: none; margin: 0; padding: 0;">
3+
<a href="https://stagehand.dev">
4+
<picture>
5+
<source media="(prefers-color-scheme: dark)" srcset="media/dark_logo.png" />
6+
<img alt="Stagehand" src="media/light_logo.png" width="200" style="margin-right: 30px;" />
7+
</picture>
8+
</a>
9+
</ul>
10+
</div>
11+
<p align="center">
12+
<strong>The AI Browser Automation Framework</strong><br>
13+
<a href="https://docs.stagehand.dev">Read the Docs</a>
14+
</p>
15+
16+
<p align="center">
17+
<a href="https://github.com/browserbase/stagehand/tree/main?tab=MIT-1-ov-file#MIT-1-ov-file">
18+
<picture>
19+
<source media="(prefers-color-scheme: dark)" srcset="media/dark_license.svg" />
20+
<img alt="MIT License" src="media/light_license.svg" />
21+
</picture>
22+
</a>
23+
<a href="https://join.slack.com/t/stagehand-dev/shared_invite/zt-38khc8iv5-T2acb50_0OILUaX7lxeBOg">
24+
<picture>
25+
<source media="(prefers-color-scheme: dark)" srcset="media/dark_slack.svg" />
26+
<img alt="Slack Community" src="media/light_slack.svg" />
27+
</picture>
28+
</a>
29+
</p>
30+
31+
<p align="center">
32+
<a href="https://trendshift.io/repositories/12122" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12122" alt="browserbase%2Fstagehand | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
33+
</p>
34+
35+
<p align="center">
36+
If you're looking for the Python implementation, you can find it
37+
<a href="https://github.com/browserbase/stagehand-python"> here</a>
38+
</p>
39+
40+
<div align="center" style="display: flex; align-items: center; justify-content: center; gap: 4px; margin-bottom: 0;">
41+
<b>Vibe code</b>
42+
<span style="font-size: 1.05em;"> Stagehand with </span>
43+
<a href="https://director.ai" style="display: flex; align-items: center;">
44+
<span>Director</span>
45+
</a>
46+
<span> </span>
47+
<picture>
48+
<img alt="Director" src="media/director_icon.svg" width="25" />
49+
</picture>
50+
</div>
51+
52+
## What is Stagehand?
53+
54+
Stagehand is a browser automation framework used to control web browsers with natural language and code. By combining the power of AI with the precision of code, Stagehand makes web automation flexible, maintainable, and actually reliable.
55+
56+
## Why Stagehand?
57+
58+
Most existing browser automation tools either require you to write low-level code in a framework like Selenium, Playwright, or Puppeteer, or use high-level agents that can be unpredictable in production. By letting developers choose what to write in code vs. natural language (and bridging the gap between the two) Stagehand is the natural choice for browser automations in production.
59+
60+
1. **Choose when to write code vs. natural language**: use AI when you want to navigate unfamiliar pages, and use code when you know exactly what you want to do.
61+
62+
2. **Go from AI-driven to repeatable workflows**: Stagehand lets you preview AI actions before running them, and also helps you easily cache repeatable actions to save time and tokens.
63+
64+
3. **Write once, run forever**: Stagehand's auto-caching combined with self-healing remembers previous actions, runs without LLM inference, and knows when to involve AI whenever the website changes and your automation breaks.
65+
66+
## Getting Started
67+
68+
Start with Stagehand with one line of code, or check out our [Quickstart Guide](https://docs.stagehand.dev/v3/first-steps/quickstart) for more information:
69+
70+
```bash
71+
npx create-browser-app
72+
```
73+
74+
## Example
75+
76+
Here's how to build a sample browser automation with Stagehand:
77+
78+
```typescript
79+
// Stagehand's CDP engine provides an optimized, low level interface to the browser built for automation
80+
const page = stagehand.context.pages()[0];
81+
await page.goto("https://github.com/browserbase");
82+
83+
// Use act() to execute individual actions
84+
await stagehand.act("click on the stagehand repo");
85+
86+
// Use agent() for multi-step tasks
87+
const agent = stagehand.agent();
88+
await agent.execute("Get to the latest PR");
89+
90+
// Use extract() to get structured data from the page
91+
const { author, title } = await stagehand.extract(
92+
"extract the author and title of the PR",
93+
z.object({
94+
author: z.string().describe("The username of the PR author"),
95+
title: z.string().describe("The title of the PR"),
96+
}),
97+
);
98+
```
99+
100+
## Documentation
101+
102+
Visit [docs.stagehand.dev](https://docs.stagehand.dev) to view the full documentation.
103+
104+
### Build and Run from Source
105+
106+
```bash
107+
git clone https://github.com/browserbase/stagehand.git
108+
cd stagehand
109+
pnpm install
110+
pnpm run build
111+
pnpm run example # run the blank script at ./examples/example.ts
112+
```
113+
114+
Stagehand is best when you have an API key for an LLM provider and Browserbase credentials. To add these to your project, run:
115+
116+
```bash
117+
cp .env.example .env
118+
nano .env # Edit the .env file to add API keys
119+
```
120+
121+
## Contributing
122+
123+
> [!NOTE]
124+
> We highly value contributions to Stagehand! For questions or support, please join our [Slack community](https://join.slack.com/t/stagehand-dev/shared_invite/zt-38khc8iv5-T2acb50_0OILUaX7lxeBOg).
125+
126+
At a high level, we're focused on improving reliability, extensibility, speed, and cost in that order of priority. If you're interested in contributing, **bug fixes and small improvements are the best way to get started**. For more involved features, we strongly recommend reaching out to [Miguel Gonzalez](https://x.com/miguel_gonzf) or [Paul Klein](https://x.com/pk_iv) in our [Slack community](https://join.slack.com/t/stagehand-dev/shared_invite/zt-38khc8iv5-T2acb50_0OILUaX7lxeBOg) before starting to ensure that your contribution aligns with our goals.
127+
128+
<!-- For more information, please see our [Contributing Guide](https://docs.stagehand.dev/examples/contributing). -->
129+
130+
## Acknowledgements
131+
132+
We'd like to thank the following people for their major contributions to Stagehand:
133+
134+
- [Paul Klein](https://github.com/pkiv)
135+
- [Sean McGuire](https://github.com/seanmcguire12)
136+
- [Miguel Gonzalez](https://github.com/miguelg719)
137+
- [Sameel Arif](https://github.com/sameelarif)
138+
- [Thomas Katwan](https://github.com/tkattkat)
139+
- [Filip Michalsky](https://github.com/filip-michalsky)
140+
- [Anirudh Kamath](https://github.com/kamath)
141+
- [Jeremy Press](https://x.com/jeremypress)
142+
- [Navid Pour](https://github.com/navidpour)
143+
144+
## License
145+
146+
Licensed under the MIT License.
147+
148+
Copyright 2025 Browserbase, Inc.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* This example shows how to pass custom tools to stagehand agent (both CUA and non-CUA)
3+
*/
4+
import { z } from "zod/v3";
5+
import { tool } from "ai";
6+
import { Stagehand } from "../lib/v3";
7+
import chalk from "chalk";
8+
9+
// Mock weather API, replace with your own API/tool logic
10+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
11+
const fetchWeatherAPI = async (location: string) => {
12+
return {
13+
temp: 70,
14+
conditions: "sunny",
15+
};
16+
};
17+
18+
// Define the tool in an AI SDK format
19+
const getWeather = tool({
20+
description: "Get the current weather in a location",
21+
inputSchema: z.object({
22+
location: z.string().describe("The location to get weather for"),
23+
}),
24+
execute: async ({ location }) => {
25+
// Your custom logic here
26+
const weather = await fetchWeatherAPI(location);
27+
return {
28+
location,
29+
temperature: weather.temp,
30+
conditions: weather.conditions,
31+
};
32+
},
33+
});
34+
35+
async function main() {
36+
console.log(
37+
`\n${chalk.bold("Stagehand 🤘 Computer Use Agent (CUA) Demo")}\n`,
38+
);
39+
40+
// Initialize Stagehand
41+
const stagehand = new Stagehand({
42+
env: "LOCAL",
43+
verbose: 2,
44+
experimental: true, // You must enable experimental mode to use custom tools / MCP integrations
45+
model: "anthropic/claude-sonnet-4-5",
46+
});
47+
await stagehand.init();
48+
49+
try {
50+
const page = stagehand.context.pages()[0];
51+
52+
// Create a computer use agent
53+
const agent = stagehand.agent({
54+
cua: true,
55+
model: {
56+
modelName: "anthropic/claude-sonnet-4-5-20250929",
57+
apiKey: process.env.ANTHROPIC_API_KEY,
58+
},
59+
systemPrompt: `You are a helpful assistant that can use a web browser.
60+
You are currently on the following page: ${page.url()}.
61+
Do not ask follow up questions, the user will trust your judgement. Today's date is ${new Date().toLocaleDateString()}.`,
62+
tools: {
63+
getWeather, // Pass the tools to the agent
64+
},
65+
});
66+
67+
// const agent = stagehand.agent({
68+
// systemPrompt: `You are a helpful assistant that can use a web browser.
69+
// You are currently on the following page: ${page.url()}.
70+
// Do not ask follow up questions, the user will trust your judgement. Today's date is ${new Date().toLocaleDateString()}.`,
71+
// // Pass the tools to the agent
72+
// tools: {
73+
// getWeather: getWeather,
74+
// },
75+
// });
76+
77+
// Navigate to the Browserbase careers page
78+
await page.goto("https://www.google.com");
79+
80+
// Define the instruction for the CUA
81+
const instruction = "What's the weather in San Francisco?";
82+
console.log(`Instruction: ${chalk.white(instruction)}`);
83+
84+
// Execute the instruction
85+
const result = await agent.execute({
86+
instruction,
87+
maxSteps: 20,
88+
});
89+
90+
console.log(`${chalk.green("✓")} Execution complete`);
91+
console.log(`${chalk.yellow("⤷")} Result:`);
92+
console.log(chalk.white(JSON.stringify(result, null, 2)));
93+
} catch (error) {
94+
console.log(`${chalk.red("✗")} Error: ${error}`);
95+
if (error instanceof Error && error.stack) {
96+
console.log(chalk.dim(error.stack.split("\n").slice(1).join("\n")));
97+
}
98+
} finally {
99+
// Close the browser
100+
await stagehand.close();
101+
}
102+
}
103+
104+
main().catch((error) => {
105+
console.log(`${chalk.red("✗")} Unhandled error in main function`);
106+
console.log(chalk.red(error));
107+
});

packages/core/examples/external_clients/aisdk.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import type { LanguageModelV2 } from "@ai-sdk/provider";
1313
import {
1414
CreateChatCompletionOptions,
1515
LLMClient,
16-
AvailableModel,
17-
} from "../../lib/v3";
16+
} from "../../lib/v3/llm/LLMClient";
17+
import { AvailableModel } from "../../lib/v3/types/public";
1818
import { ChatCompletion } from "openai/resources";
1919

2020
export class AISdkClient extends LLMClient {

packages/core/lib/v3/tests/v3.dynamic.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const v3DynamicTestConfig: V3Options =
2727
env: "BROWSERBASE",
2828
apiKey: process.env.BROWSERBASE_API_KEY!,
2929
projectId: process.env.BROWSERBASE_PROJECT_ID!,
30+
disableAPI: true,
3031
}
3132
: {
3233
...baseConfig,

packages/core/lib/v3/types/public/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ export * from "./model";
77
export * from "./options";
88
export * from "./page";
99
export * from "./sdkErrors";
10+
// Exporting the example AISdkClient for backwards compatibility
11+
// Note added for revisiting this scaffold for an improved version based on llm/aisdk.ts
12+
export { AISdkClient } from "../../../../examples/external_clients/aisdk";

packages/core/lib/v3/v3.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,25 +1363,34 @@ export class V3 {
13631363
} {
13641364
this.logger({
13651365
category: "agent",
1366-
message: "Creating v3 agent instance with options:",
1366+
message: `Creating v3 agent instance with options: ${JSON.stringify(options)}`,
13671367
level: 1,
13681368
auxiliary: {
13691369
cua: { value: options?.cua ? "true" : "false", type: "boolean" },
1370-
model:
1371-
typeof options?.model === "string"
1370+
model: options?.model
1371+
? typeof options?.model === "string"
13721372
? { value: options.model, type: "string" }
1373-
: { value: options.model.modelName, type: "string" },
1373+
: { value: options.model.modelName, type: "string" }
1374+
: { value: this.llmClient.modelName, type: "string" },
13741375
systemPrompt: { value: options?.systemPrompt ?? "", type: "string" },
13751376
tools: { value: JSON.stringify(options?.tools ?? {}), type: "object" },
1376-
integrations: {
1377-
value: JSON.stringify(options?.integrations ?? []),
1378-
type: "object",
1379-
},
1377+
...(options?.integrations && {
1378+
integrations: {
1379+
value: JSON.stringify(options.integrations),
1380+
type: "object",
1381+
},
1382+
}),
13801383
},
13811384
});
13821385

13831386
// If CUA is enabled, use the computer-use agent path
13841387
if (options?.cua) {
1388+
if ((options?.integrations || options?.tools) && !this.experimental) {
1389+
throw new Error(
1390+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options.",
1391+
);
1392+
}
1393+
13851394
const modelToUse = options?.model || {
13861395
modelName: this.modelName,
13871396
...this.modelClientOptions,
@@ -1499,9 +1508,9 @@ export class V3 {
14991508
return {
15001509
execute: async (instructionOrOptions: string | AgentExecuteOptions) =>
15011510
withInstanceLogContext(this.instanceId, async () => {
1502-
if (options?.integrations && !this.experimental) {
1511+
if ((options?.integrations || options?.tools) && !this.experimental) {
15031512
throw new Error(
1504-
"MCP integrations are experimental. Enable experimental: true in V3 options.",
1513+
"MCP integrations and custom tools are experimental. Enable experimental: true in V3 options.",
15051514
);
15061515
}
15071516

0 commit comments

Comments
 (0)