@browserbasehq/eve 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +117 -0
  2. package/dist/extension/_manifest.json +13 -0
  3. package/dist/extension/extension.d.ts +8 -0
  4. package/dist/extension/extension.mjs +14 -0
  5. package/dist/extension/instructions/browser.md +9 -0
  6. package/dist/extension/lib/browserbase.d.ts +2 -0
  7. package/dist/extension/lib/browserbase.mjs +11 -0
  8. package/dist/extension/lib/disconnect.d.ts +5 -0
  9. package/dist/extension/lib/disconnect.mjs +9 -0
  10. package/dist/extension/lib/release-session.d.ts +14 -0
  11. package/dist/extension/lib/release-session.mjs +38 -0
  12. package/dist/extension/lib/session-lock.d.ts +1 -0
  13. package/dist/extension/lib/session-lock.mjs +22 -0
  14. package/dist/extension/lib/session-state.d.ts +5 -0
  15. package/dist/extension/lib/session-state.mjs +11 -0
  16. package/dist/extension/lib/stagehand.d.ts +10 -0
  17. package/dist/extension/lib/stagehand.mjs +118 -0
  18. package/dist/extension/tools/act.d.ts +4 -0
  19. package/dist/extension/tools/act.mjs +16 -0
  20. package/dist/extension/tools/agent.d.ts +5 -0
  21. package/dist/extension/tools/agent.mjs +24 -0
  22. package/dist/extension/tools/extract.d.ts +5 -0
  23. package/dist/extension/tools/extract.mjs +21 -0
  24. package/dist/extension/tools/fetch.d.ts +9 -0
  25. package/dist/extension/tools/fetch.mjs +37 -0
  26. package/dist/extension/tools/navigate.d.ts +7 -0
  27. package/dist/extension/tools/navigate.mjs +23 -0
  28. package/dist/extension/tools/observe.d.ts +4 -0
  29. package/dist/extension/tools/observe.mjs +16 -0
  30. package/dist/extension/tools/search.d.ts +5 -0
  31. package/dist/extension/tools/search.mjs +22 -0
  32. package/dist/extension/tools/session.d.ts +9 -0
  33. package/dist/extension/tools/session.mjs +29 -0
  34. package/dist/index.d.ts +4 -0
  35. package/dist/index.mjs +7 -0
  36. package/dist/tools/index.d.ts +10 -0
  37. package/dist/tools/index.mjs +14 -0
  38. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # Browserbase for Eve
2
+
3
+ An [Eve extension](https://eve.dev/docs/extensions) that gives an agent
4
+ Browserbase Search and Fetch plus a persistent Browserbase browser powered by
5
+ [Stagehand](https://docs.stagehand.dev/). It exposes Stagehand's focused
6
+ primitives (`act`, `observe`, and `extract`) plus navigation and autonomous agent
7
+ tools.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @browserbasehq/eve
13
+ ```
14
+
15
+ Add your credentials to the consuming Eve app:
16
+
17
+ ```bash
18
+ BROWSERBASE_API_KEY=bb_live_...
19
+ ```
20
+
21
+ ## Mount the extension
22
+
23
+ The filename under `agent/extensions/` becomes the tool namespace. Mounting the
24
+ extension as `browserbase.ts` creates tools such as `browserbase__search`,
25
+ `browserbase__fetch`, `browserbase__create_session`, and
26
+ `browserbase__navigate`.
27
+
28
+ ```ts
29
+ // agent/extensions/browserbase.ts
30
+ import browserbase from '@browserbasehq/eve';
31
+
32
+ export default browserbase({
33
+ apiKey: process.env.BROWSERBASE_API_KEY!,
34
+ model: 'openai/gpt-5.4-mini',
35
+ });
36
+ ```
37
+
38
+ Start Eve and give the agent a browser task:
39
+
40
+ ```bash
41
+ pnpm exec eve dev
42
+ ```
43
+
44
+ ```text
45
+ Open https://news.ycombinator.com and extract the titles and URLs of the first
46
+ five stories.
47
+ ```
48
+
49
+ The extension keeps the Browserbase session ID in Eve's durable per-session
50
+ state. Each browser tool reconnects to that browser, performs one operation,
51
+ and disconnects without terminating it. `browserbase__create_session` creates
52
+ or reconnects the browser explicitly, and `browserbase__stop_session`
53
+ terminates it.
54
+
55
+ ## Tools
56
+
57
+ | Tool | Purpose |
58
+ | ---------------- | ------------------------------------------------------- |
59
+ | `search` | Find relevant public web pages with Browserbase Search. |
60
+ | `fetch` | Retrieve raw, markdown, or structured page content. |
61
+ | `create_session` | Create or reconnect the Browserbase session. |
62
+ | `stop_session` | Stop the Browserbase session and release resources. |
63
+ | `navigate` | Open a URL in the current browser. |
64
+ | `observe` | Find relevant elements and candidate actions. |
65
+ | `act` | Perform one natural-language page interaction. |
66
+ | `extract` | Return data validated against a supplied JSON Schema. |
67
+ | `agent` | Run a multi-step autonomous Stagehand task. |
68
+
69
+ Use Search → Fetch → browser as an escalation path: discover sources cheaply,
70
+ retrieve straightforward content without a session, and create a browser only
71
+ when the page requires JavaScript or interaction. Fetch supports Browserbase's
72
+ `raw`, `markdown`, and schema-driven `json` formats.
73
+
74
+ For predictable and efficient runs, use `create_session` → `navigate` →
75
+ `observe` → `act` or `extract`, then `stop_session`. Reserve `agent` for
76
+ workflows that need Stagehand to plan several steps on its own.
77
+
78
+ ## Configuration
79
+
80
+ | Option | Default | Description |
81
+ | ----------------------- | --------------------- | --------------------------------------------------- |
82
+ | `apiKey` | required | Browserbase API key for browsers and Model Gateway. |
83
+ | `model` | `openai/gpt-5.4-mini` | Stagehand Model Gateway model identifier. |
84
+ | `sessionTimeoutSeconds` | `900` | Session timeout, from 60 to 21,600 seconds. |
85
+ | `proxies` | `false` | Enable Browserbase proxies for new sessions. |
86
+
87
+ Stagehand runs through Browserbase Model Gateway, so consumers do not need an
88
+ OpenAI or other model-provider API key. The Browserbase API key covers both the
89
+ browser session and Stagehand inference.
90
+
91
+ The extension uses Browserbase `keepAlive` sessions so it can reconnect across
92
+ Eve workflow steps and Vercel function invocations. Parallel browser calls made
93
+ inside one Eve workflow step are queued in that step's managed runtime; durable
94
+ state reconnects later steps and invocations. Close the session after the task
95
+ to avoid leaving billable browser time running. Keep-alive availability depends
96
+ on your Browserbase plan.
97
+
98
+ ## Build
99
+
100
+ ```bash
101
+ nvm use
102
+ pnpm install
103
+ pnpm check
104
+ ```
105
+
106
+ Eve requires Node.js 24 or newer. This directory's `.nvmrc` pins Node 24.16.0;
107
+ run `nvm install 24.16.0` first if needed.
108
+
109
+ `eve extension build` writes the publishable extension and type declarations to
110
+ `dist/`.
111
+
112
+ ## Example agent
113
+
114
+ The [`eve-example`](../../examples/integrations/vercel/eve-example/) directory
115
+ contains a runnable Eve agent that mounts this package locally. It includes the
116
+ agent instructions, environment template, and a Browserbase research prompt for
117
+ a complete smoke test.
@@ -0,0 +1,13 @@
1
+ {
2
+ "kind": "eve-extension",
3
+ "formatVersion": 1,
4
+ "builtWithEve": "0.25.3",
5
+ "requires": {
6
+ "extension": 1,
7
+ "tool": 1,
8
+ "dynamicTool": 1,
9
+ "instructions": 1,
10
+ "config": 1,
11
+ "state": 1
12
+ }
13
+ }
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ declare const _default: import("eve/extension").ExtensionHandle<z.ZodObject<{
3
+ apiKey: z.ZodString;
4
+ model: z.ZodDefault<z.ZodString>;
5
+ sessionTimeoutSeconds: z.ZodDefault<z.ZodNumber>;
6
+ proxies: z.ZodDefault<z.ZodBoolean>;
7
+ }, z.core.$strip>>;
8
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { defineExtension } from "eve/extension";
7
+ import { z } from "zod";
8
+ var extension_default = defineExtension({ config: z.object({
9
+ apiKey: z.string().min(1),
10
+ model: z.string().min(1).default("openai/gpt-5.4-mini"),
11
+ sessionTimeoutSeconds: z.number().int().min(60).max(21600).default(900),
12
+ proxies: z.boolean().default(false)
13
+ }) });
14
+ export { extension_default as default };
@@ -0,0 +1,9 @@
1
+ Use Browserbase Search to discover relevant URLs and Browserbase Fetch for quick
2
+ retrievals that do not need JavaScript or interaction. Escalate to a browser
3
+ session when a page needs rendering or interaction: create a session, navigate,
4
+ then use observe to plan, act for one interaction, and extract for structured
5
+ results. Use agent only for genuinely multi-step work. Browser tools share one
6
+ Browserbase session per Eve session. Parallel browser calls from the same Eve
7
+ workflow step are queued within that step; later steps reconnect through durable
8
+ state. Prefer calling tools sequentially, and stop the browser session when the
9
+ task is complete.
@@ -0,0 +1,2 @@
1
+ import Browserbase from '@browserbasehq/sdk';
2
+ export declare function createBrowserbaseClient(): Browserbase;
@@ -0,0 +1,11 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import extension_default from "../extension.mjs";
7
+ import Browserbase from "@browserbasehq/sdk";
8
+ function createBrowserbaseClient() {
9
+ return new Browserbase({ apiKey: extension_default.config.apiKey });
10
+ }
11
+ export { createBrowserbaseClient };
@@ -0,0 +1,5 @@
1
+ interface ClosableStagehand {
2
+ close(): Promise<void>;
3
+ }
4
+ export declare function disconnectStagehand(stagehand: ClosableStagehand): Promise<void>;
5
+ export {};
@@ -0,0 +1,9 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ async function disconnectStagehand(stagehand) {
7
+ await stagehand.close().catch(() => {});
8
+ }
9
+ export { disconnectStagehand };
@@ -0,0 +1,14 @@
1
+ type BrowserbaseSessionStatus = 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
2
+ interface BrowserbaseSessionRecord {
3
+ status: BrowserbaseSessionStatus;
4
+ }
5
+ interface BrowserbaseSessionApi {
6
+ retrieve(sessionId: string): Promise<BrowserbaseSessionRecord>;
7
+ update(sessionId: string, body: {
8
+ status: 'REQUEST_RELEASE';
9
+ }): Promise<BrowserbaseSessionRecord>;
10
+ }
11
+ export type RemoteSessionState = 'active' | 'terminal' | 'unknown';
12
+ export declare function getRemoteSessionState(sessions: Pick<BrowserbaseSessionApi, 'retrieve'>, sessionId: string): Promise<RemoteSessionState>;
13
+ export declare function releaseBrowserbaseSession(sessions: BrowserbaseSessionApi, sessionId: string, wait?: (milliseconds: number) => Promise<void>): Promise<void>;
14
+ export {};
@@ -0,0 +1,38 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { APIError } from "@browserbasehq/sdk";
7
+ const RELEASE_POLL_DELAYS_MS = [
8
+ 100,
9
+ 250,
10
+ 500,
11
+ 1e3
12
+ ];
13
+ function isActiveSessionStatus(status) {
14
+ return status === "PENDING" || status === "RUNNING";
15
+ }
16
+ async function getRemoteSessionState(sessions, sessionId) {
17
+ try {
18
+ return isActiveSessionStatus((await sessions.retrieve(sessionId)).status) ? "active" : "terminal";
19
+ } catch (error) {
20
+ return error instanceof APIError && error.status === 404 ? "terminal" : "unknown";
21
+ }
22
+ }
23
+ async function releaseBrowserbaseSession(sessions, sessionId, wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))) {
24
+ try {
25
+ if (!isActiveSessionStatus((await sessions.update(sessionId, { status: "REQUEST_RELEASE" })).status)) return;
26
+ } catch (error) {
27
+ if (error instanceof APIError && error.status === 404) return;
28
+ throw error;
29
+ }
30
+ for (const delay of RELEASE_POLL_DELAYS_MS) {
31
+ await wait(delay);
32
+ const state = await getRemoteSessionState(sessions, sessionId);
33
+ if (state === "terminal") return;
34
+ if (state === "unknown") throw new Error(`Unable to confirm Browserbase session ${sessionId} was released.`);
35
+ }
36
+ throw new Error(`Browserbase session ${sessionId} is still active after requesting release.`);
37
+ }
38
+ export { getRemoteSessionState, releaseBrowserbaseSession };
@@ -0,0 +1 @@
1
+ export declare function withSessionLock<T>(sessionId: string, operation: () => Promise<T>): Promise<T>;
@@ -0,0 +1,22 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ const sessionTails = new Map();
7
+ async function withSessionLock(sessionId, operation) {
8
+ const previous = sessionTails.get(sessionId) ?? Promise.resolve();
9
+ let release = () => {};
10
+ const current = new Promise((resolve) => {
11
+ release = resolve;
12
+ });
13
+ sessionTails.set(sessionId, current);
14
+ await previous;
15
+ try {
16
+ return await operation();
17
+ } finally {
18
+ release();
19
+ if (sessionTails.get(sessionId) === current) sessionTails.delete(sessionId);
20
+ }
21
+ }
22
+ export { withSessionLock };
@@ -0,0 +1,5 @@
1
+ export interface BrowserSessionState {
2
+ id: string | null;
3
+ url: string | null;
4
+ }
5
+ export declare const browserSession: import("eve/context").StateHandle<BrowserSessionState>;
@@ -0,0 +1,11 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { defineState } from "eve/context";
7
+ const browserSession = defineState("browser-session", () => ({
8
+ id: null,
9
+ url: null
10
+ }));
11
+ export { browserSession };
@@ -0,0 +1,10 @@
1
+ import { Stagehand } from '@browserbasehq/stagehand';
2
+ import { type BrowserSessionState } from './session-state';
3
+ type StagehandOperation<T> = (stagehand: Stagehand) => Promise<T>;
4
+ export interface BrowserSessionResult extends BrowserSessionState {
5
+ created: boolean;
6
+ }
7
+ export declare function withStagehand<T>(sessionId: string, operation: StagehandOperation<T>): Promise<T>;
8
+ export declare function createBrowserSession(sessionId: string): Promise<BrowserSessionResult>;
9
+ export declare function closeBrowserSession(sessionId: string): Promise<BrowserSessionState>;
10
+ export {};
@@ -0,0 +1,118 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import extension_default from "../extension.mjs";
7
+ import { createBrowserbaseClient } from "./browserbase.mjs";
8
+ import { disconnectStagehand } from "./disconnect.mjs";
9
+ import { getRemoteSessionState, releaseBrowserbaseSession } from "./release-session.mjs";
10
+ import { withSessionLock } from "./session-lock.mjs";
11
+ import { browserSession } from "./session-state.mjs";
12
+ import { Stagehand } from "@browserbasehq/stagehand";
13
+ function createStagehand() {
14
+ const { apiKey, model, proxies, sessionTimeoutSeconds } = extension_default.config;
15
+ return new Stagehand({
16
+ env: "BROWSERBASE",
17
+ apiKey,
18
+ model,
19
+ disablePino: true,
20
+ keepAlive: true,
21
+ browserbaseSessionCreateParams: {
22
+ keepAlive: true,
23
+ timeout: sessionTimeoutSeconds,
24
+ proxies
25
+ }
26
+ });
27
+ }
28
+ function resumeStagehand(sessionId) {
29
+ const { apiKey, model } = extension_default.config;
30
+ return new Stagehand({
31
+ env: "BROWSERBASE",
32
+ apiKey,
33
+ model,
34
+ disablePino: true,
35
+ keepAlive: true,
36
+ browserbaseSessionID: sessionId
37
+ });
38
+ }
39
+ async function connect() {
40
+ const current = browserSession.get();
41
+ if (current.id) {
42
+ const resumed = resumeStagehand(current.id);
43
+ try {
44
+ await resumed.init();
45
+ return {
46
+ stagehand: resumed,
47
+ created: false
48
+ };
49
+ } catch (error) {
50
+ await disconnectStagehand(resumed);
51
+ const sessions = createBrowserbaseClient().sessions;
52
+ if (await getRemoteSessionState(sessions, current.id) !== "terminal") throw error;
53
+ browserSession.update(() => ({
54
+ id: null,
55
+ url: null
56
+ }));
57
+ }
58
+ }
59
+ const created = createStagehand();
60
+ try {
61
+ await created.init();
62
+ } catch (error) {
63
+ await disconnectStagehand(created);
64
+ throw error;
65
+ }
66
+ browserSession.update(() => ({
67
+ id: created.browserbaseSessionID ?? null,
68
+ url: created.browserbaseSessionURL ?? null
69
+ }));
70
+ return {
71
+ stagehand: created,
72
+ created: true
73
+ };
74
+ }
75
+ async function withStagehand(sessionId, operation) {
76
+ return withSessionLock(sessionId, async () => {
77
+ const { stagehand } = await connect();
78
+ try {
79
+ return await operation(stagehand);
80
+ } finally {
81
+ await disconnectStagehand(stagehand);
82
+ }
83
+ });
84
+ }
85
+ async function createBrowserSession(sessionId) {
86
+ return withSessionLock(sessionId, async () => {
87
+ const { stagehand, created } = await connect();
88
+ try {
89
+ return {
90
+ ...browserSession.get(),
91
+ created
92
+ };
93
+ } finally {
94
+ await disconnectStagehand(stagehand);
95
+ }
96
+ });
97
+ }
98
+ async function closeBrowserSession(sessionId) {
99
+ return withSessionLock(sessionId, async () => {
100
+ const current = browserSession.get();
101
+ if (!current.id) return current;
102
+ const sessions = createBrowserbaseClient().sessions;
103
+ if (await getRemoteSessionState(sessions, current.id) === "terminal") {
104
+ browserSession.update(() => ({
105
+ id: null,
106
+ url: null
107
+ }));
108
+ return current;
109
+ }
110
+ await releaseBrowserbaseSession(sessions, current.id);
111
+ browserSession.update(() => ({
112
+ id: null,
113
+ url: null
114
+ }));
115
+ return current;
116
+ });
117
+ }
118
+ export { closeBrowserSession, createBrowserSession, withStagehand };
@@ -0,0 +1,4 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ instruction: string;
3
+ }, import("@browserbasehq/stagehand").ActResult>;
4
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { withStagehand } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ var act_default = defineTool({
10
+ description: "Perform one natural-language action in the current Browserbase page with Stagehand.",
11
+ inputSchema: z.object({ instruction: z.string().min(1).describe("A precise single action, such as clicking or filling a field.") }),
12
+ async execute({ instruction }, ctx) {
13
+ return withStagehand(ctx.session.id, (stagehand) => stagehand.act(instruction));
14
+ }
15
+ });
16
+ export { act_default as default };
@@ -0,0 +1,5 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ instruction: string;
3
+ maxSteps: number;
4
+ }, import("@browserbasehq/stagehand").AgentResult>;
5
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { withStagehand } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ var agent_default = defineTool({
10
+ description: "Run an autonomous multi-step Stagehand browser task in the current Browserbase session. Prefer act, observe, and extract for targeted work.",
11
+ inputSchema: z.object({
12
+ instruction: z.string().min(1).describe("The browser task to complete."),
13
+ maxSteps: z.number().int().min(1).max(50).default(20)
14
+ }),
15
+ async execute({ instruction, maxSteps }, ctx) {
16
+ return withStagehand(ctx.session.id, async (stagehand) => {
17
+ return stagehand.agent().execute({
18
+ instruction,
19
+ maxSteps
20
+ });
21
+ });
22
+ }
23
+ });
24
+ export { agent_default as default };
@@ -0,0 +1,5 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ instruction: string;
3
+ schema: Record<string, unknown>;
4
+ }, unknown>;
5
+ export default _default;
@@ -0,0 +1,21 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { withStagehand } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ const jsonSchema = z.record(z.string(), z.unknown()).describe("A JSON Schema describing the exact data shape to return.");
10
+ var extract_default = defineTool({
11
+ description: "Extract structured data from the current Browserbase page with Stagehand and validate it against a JSON Schema.",
12
+ inputSchema: z.object({
13
+ instruction: z.string().min(1).describe("What data to extract."),
14
+ schema: jsonSchema
15
+ }),
16
+ async execute({ instruction, schema }, ctx) {
17
+ const outputSchema = z.fromJSONSchema(schema);
18
+ return withStagehand(ctx.session.id, (stagehand) => stagehand.extract(instruction, outputSchema));
19
+ }
20
+ });
21
+ export { extract_default as default };
@@ -0,0 +1,9 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ url: string;
3
+ format: "json" | "markdown" | "raw";
4
+ schema?: Record<string, unknown> | undefined;
5
+ allowRedirects: boolean;
6
+ allowInsecureSsl: boolean;
7
+ proxies: boolean;
8
+ }, import("@browserbasehq/sdk/resources/fetch-api.mjs").FetchAPICreateResponse>;
9
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { createBrowserbaseClient } from "../lib/browserbase.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ const jsonSchema = z.record(z.string(), z.unknown()).optional().describe("A JSON Schema for structured extraction. Required with the json format and invalid with other formats.");
10
+ var fetch_default = defineTool({
11
+ description: "Retrieve a URL through Browserbase Fetch without starting a browser session. Use raw for the original response, markdown for agent-friendly content, or json with a schema for structured extraction.",
12
+ inputSchema: z.object({
13
+ url: z.url().describe("The absolute URL to retrieve."),
14
+ format: z.enum([
15
+ "raw",
16
+ "markdown",
17
+ "json"
18
+ ]).default("raw").describe("The response content format."),
19
+ schema: jsonSchema,
20
+ allowRedirects: z.boolean().default(false).describe("Whether to follow HTTP redirects."),
21
+ allowInsecureSsl: z.boolean().default(false).describe("Whether to bypass TLS certificate verification."),
22
+ proxies: z.boolean().default(false).describe("Whether to route the request through Browserbase proxies.")
23
+ }),
24
+ async execute({ url, format, schema, allowRedirects, allowInsecureSsl, proxies }) {
25
+ if (format === "json" && !schema) throw new Error("schema is required when format is json.");
26
+ if (format !== "json" && schema) throw new Error("schema can only be used when format is json.");
27
+ return createBrowserbaseClient().fetchAPI.create({
28
+ url,
29
+ format,
30
+ schema,
31
+ allowRedirects,
32
+ allowInsecureSsl,
33
+ proxies
34
+ });
35
+ }
36
+ });
37
+ export { fetch_default as default };
@@ -0,0 +1,7 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ url: string;
3
+ }, {
4
+ url: string;
5
+ title: string;
6
+ }>;
7
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { withStagehand } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ var navigate_default = defineTool({
10
+ description: "Navigate the Browserbase browser to a URL. Call this before using other browser tools on a new site.",
11
+ inputSchema: z.object({ url: z.url().describe("The absolute URL to open.") }),
12
+ async execute({ url }, ctx) {
13
+ return withStagehand(ctx.session.id, async (stagehand) => {
14
+ const page = await stagehand.context.awaitActivePage();
15
+ await page.goto(url);
16
+ return {
17
+ url: page.url(),
18
+ title: await page.title()
19
+ };
20
+ });
21
+ }
22
+ });
23
+ export { navigate_default as default };
@@ -0,0 +1,4 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ instruction: string;
3
+ }, import("@browserbasehq/stagehand").Action[]>;
4
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { withStagehand } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ var observe_default = defineTool({
10
+ description: "Inspect the current Browserbase page with Stagehand and return candidate elements and actions.",
11
+ inputSchema: z.object({ instruction: z.string().min(1).describe("The elements or actions to find on the page.") }),
12
+ async execute({ instruction }, ctx) {
13
+ return withStagehand(ctx.session.id, (stagehand) => stagehand.observe(instruction));
14
+ }
15
+ });
16
+ export { observe_default as default };
@@ -0,0 +1,5 @@
1
+ declare const _default: import("eve/tools").ToolDefinition<{
2
+ query: string;
3
+ numResults: number;
4
+ }, import("@browserbasehq/sdk/resources/search.mjs").SearchWebResponse>;
5
+ export default _default;
@@ -0,0 +1,22 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { createBrowserbaseClient } from "../lib/browserbase.mjs";
7
+ import { z } from "zod";
8
+ import { defineTool } from "eve/tools";
9
+ var search_default = defineTool({
10
+ description: "Search the public web with Browserbase Search. Use this to discover relevant URLs before fetching content or opening a browser session.",
11
+ inputSchema: z.object({
12
+ query: z.string().min(1).max(200).describe("The web search query, from 1 to 200 characters."),
13
+ numResults: z.number().int().min(1).max(25).default(10).describe("The number of results to return, from 1 to 25.")
14
+ }),
15
+ async execute({ query, numResults }) {
16
+ return createBrowserbaseClient().search.web({
17
+ query,
18
+ numResults
19
+ });
20
+ }
21
+ });
22
+ export { search_default as default };
@@ -0,0 +1,9 @@
1
+ declare const _default: import("eve/tools").DynamicSentinel<{
2
+ create_session: import("eve/tools").ToolDefinition<Record<string, never>, import("../lib/stagehand").BrowserSessionResult>;
3
+ stop_session: import("eve/tools").ToolDefinition<Record<string, never>, {
4
+ id: string | null;
5
+ url: string | null;
6
+ stopped: boolean;
7
+ }>;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,29 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import { closeBrowserSession, createBrowserSession } from "../lib/stagehand.mjs";
7
+ import { z } from "zod";
8
+ import { defineDynamic, defineTool } from "eve/tools";
9
+ var session_default = defineDynamic({ events: { "session.started": () => ({
10
+ create_session: defineTool({
11
+ description: "Create a Browserbase browser session for this Eve conversation, or reconnect if one already exists.",
12
+ inputSchema: z.object({}),
13
+ async execute(_input, ctx) {
14
+ return createBrowserSession(ctx.session.id);
15
+ }
16
+ }),
17
+ stop_session: defineTool({
18
+ description: "Stop the Browserbase browser session for this Eve conversation and release its resources.",
19
+ inputSchema: z.object({}),
20
+ async execute(_input, ctx) {
21
+ const session = await closeBrowserSession(ctx.session.id);
22
+ return {
23
+ ...session,
24
+ stopped: Boolean(session.id)
25
+ };
26
+ }
27
+ })
28
+ }) } });
29
+ export { session_default as default };
@@ -0,0 +1,4 @@
1
+ // Generated by eve. Do not edit by hand.
2
+
3
+ export { default } from "./extension/extension.js";
4
+ export { default as eve } from "./extension/extension.js";
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import extension_default from "./extension/extension.mjs";
7
+ export { extension_default as default, extension_default as eve };
@@ -0,0 +1,10 @@
1
+ // Generated by eve. Do not edit by hand.
2
+
3
+ export { default as act } from "../extension/tools/act.js";
4
+ export { default as agent } from "../extension/tools/agent.js";
5
+ export { default as extract } from "../extension/tools/extract.js";
6
+ export { default as fetch } from "../extension/tools/fetch.js";
7
+ export { default as navigate } from "../extension/tools/navigate.js";
8
+ export { default as observe } from "../extension/tools/observe.js";
9
+ export { default as search } from "../extension/tools/search.js";
10
+ export { default as session } from "../extension/tools/session.js";
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath as __eveFileURLToPath } from "node:url";
2
+ import { dirname as __eveDirname } from "node:path";
3
+ import { createRequire as __eveCreateRequire } from "node:module";
4
+ __eveDirname(__eveFileURLToPath(import.meta.url));
5
+ __eveCreateRequire(import.meta.url);
6
+ import act_default from "../extension/tools/act.mjs";
7
+ import agent_default from "../extension/tools/agent.mjs";
8
+ import extract_default from "../extension/tools/extract.mjs";
9
+ import fetch_default from "../extension/tools/fetch.mjs";
10
+ import navigate_default from "../extension/tools/navigate.mjs";
11
+ import observe_default from "../extension/tools/observe.mjs";
12
+ import search_default from "../extension/tools/search.mjs";
13
+ import session_default from "../extension/tools/session.mjs";
14
+ export { act_default as act, agent_default as agent, extract_default as extract, fetch_default as fetch, navigate_default as navigate, observe_default as observe, search_default as search, session_default as session };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@browserbasehq/eve",
3
+ "version": "0.1.0",
4
+ "description": "An Eve extension that gives agents Browserbase-powered Stagehand browser tools.",
5
+ "type": "module",
6
+ "packageManager": "pnpm@10.9.0",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "browserbase",
10
+ "eve",
11
+ "stagehand",
12
+ "browser-automation",
13
+ "ai-agents"
14
+ ],
15
+ "eve": {
16
+ "extension": {
17
+ "source": "./extension",
18
+ "dist": "./dist/extension"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.mjs"
29
+ },
30
+ "./tools": {
31
+ "types": "./dist/tools/index.d.ts",
32
+ "default": "./dist/tools/index.mjs"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "build": "eve extension build",
37
+ "prepare": "eve extension build",
38
+ "typecheck": "tsc",
39
+ "check": "pnpm typecheck && pnpm build"
40
+ },
41
+ "dependencies": {
42
+ "@browserbasehq/sdk": "2.16.0",
43
+ "@browserbasehq/stagehand": "3.7.0",
44
+ "zod": "4.4.3"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "24.10.1",
48
+ "eve": "0.25.3",
49
+ "typescript": "7.0.2"
50
+ },
51
+ "peerDependencies": {
52
+ "eve": "*"
53
+ },
54
+ "engines": {
55
+ "node": ">=24"
56
+ }
57
+ }