diff --git a/.github/workflows/codemode-framework-examples.yml b/.github/workflows/codemode-framework-examples.yml index c6ca1a1eb4..a8114c9840 100644 --- a/.github/workflows/codemode-framework-examples.yml +++ b/.github/workflows/codemode-framework-examples.yml @@ -256,3 +256,47 @@ jobs: VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + + eve: + name: Eve + if: >- + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository || + contains(github.event.pull_request.labels.*.name, 'safe-to-test') + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + + - uses: ./.github/actions/setup-node-pnpm + with: + use-prebuilt-artifacts: "false" + + - run: pnpm --filter @browserbasehq/stagehand-integrations-example-eve typecheck + - run: pnpm --filter @browserbasehq/stagehand-integrations-example-eve contract + - run: pnpm --filter @browserbasehq/stagehand-integrations-example-vercel-sandbox pack:artifacts + - name: Detect Eve live test credentials + id: eve-live-credentials + uses: ./.github/actions/detect-codemode-live-credentials + with: + require-openai: "true" + env: + BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }} + BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }} + VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }} + VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + - name: Run Eve live sandbox proof + if: steps.eve-live-credentials.outputs.available == 'true' + run: pnpm --filter @browserbasehq/stagehand-integrations-example-eve e2e + env: + STAGEHAND_SANDBOX_ARTIFACTS: ${{ github.workspace }}/packages/integrations/examples/vercel-sandbox/.artifacts + BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }} + BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }} + VERCEL_OIDC_TOKEN: ${{ secrets.VERCEL_OIDC_TOKEN }} + VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/packages/integrations/README.md b/packages/integrations/README.md index 3507efce28..70a03583ba 100644 --- a/packages/integrations/README.md +++ b/packages/integrations/README.md @@ -85,3 +85,5 @@ implementation modules and an in-process arbitrary-code executor are not public one crew execution. - [LangChain Deep Agents](./examples/langchain) uses one explicit MCP session so every tool call reaches the same browser. +- [Eve](./examples/eve) discovers the same authenticated remote MCP connection and exposes only + `code_execute` to the agent. diff --git a/packages/integrations/examples/eve/.gitignore b/packages/integrations/examples/eve/.gitignore new file mode 100644 index 0000000000..0ee2c24870 --- /dev/null +++ b/packages/integrations/examples/eve/.gitignore @@ -0,0 +1,2 @@ +.eve/ +.output/ diff --git a/packages/integrations/examples/eve/README.md b/packages/integrations/examples/eve/README.md new file mode 100644 index 0000000000..3ddb74f9b1 --- /dev/null +++ b/packages/integrations/examples/eve/README.md @@ -0,0 +1,68 @@ +# Eve with Stagehand code mode + +This example gives an Eve agent one browser tool, `stagehand__code_execute`, without running +generated JavaScript in the Eve process. Eve stays on the host while the package-installed +[Vercel Sandbox example](../vercel-sandbox) owns Stagehand, the browser session, and generated code: + +```text +Eve connection_search -> authenticated Streamable HTTP -> Vercel Sandbox -> Stagehand MCP + `-> generated JavaScript +``` + +[`agent/connections/stagehand.ts`](./agent/connections/stagehand.ts) is the complete framework +adapter. It receives the shared sandbox `{ url, token }`, uses Eve's native +`defineMcpClientConnection`, and allows only `code_execute`. Eve qualifies the discovered tool as +`stagehand__code_execute`; the canonical Stagehand guidance stays in the MCP tool description. + +## Dependency version + +The example pins Eve 0.29.4, the newest release old enough for this repository's dependency-age +policy when the integration was validated. Eve is pre-1.0 and its public API is still evolving, so +upgrade this pin only with the contract and live proofs below. + +## Secret-free framework contract + +From the repository root: + +```bash +pnpm install +pnpm --filter @browserbasehq/stagehand-integrations-example-eve typecheck +pnpm --filter @browserbasehq/stagehand-integrations-example-eve contract +``` + +The contract starts an authenticated MCP server built on the official MCP SDK, deliberately returns +405 for the optional GET stream, and runs the real Eve build, server, and eval runtime with a +deterministic model. The eval must: + +1. use `connection_search` exactly once; +2. discover only `stagehand__code_execute`; +3. call it exactly twice through one connection; +4. receive persistent-page-shaped results and the exact expected markers; and +5. reject an unauthenticated request while tolerating authenticated GET 405. + +This proves Eve's connection behavior without claiming browser or sandbox isolation. + +## Live package-backed proof + +Build and pack the exact Stagehand packages under review, then run the live composition: + +```bash +pnpm --filter @browserbasehq/stagehand-integrations-example-vercel-sandbox pack:artifacts + +STAGEHAND_SANDBOX_ARTIFACTS="$PWD/packages/integrations/examples/vercel-sandbox/.artifacts" \ +BROWSERBASE_API_KEY= \ +BROWSERBASE_PROJECT_ID= \ +VERCEL_OIDC_TOKEN= \ +OPENAI_API_KEY= \ +pnpm --filter @browserbasehq/stagehand-integrations-example-eve e2e +``` + +For external CI, replace `VERCEL_OIDC_TOKEN` with `VERCEL_TEAM_ID`, `VERCEL_PROJECT_ID`, and +`VERCEL_TOKEN`. `EVE_STAGEHAND_MODEL` selects the direct OpenAI model and defaults to +`gpt-5-mini`. + +The live proof checks unauthenticated 401 and authenticated optional-GET 405, then runs both the +deterministic and real-model Eve evals against one package-installed Vercel Sandbox connection. +Each eval must discover the connection, make two `code_execute` calls, retain a DOM marker between +calls, and observe neither `OPENAI_API_KEY` nor a host-only marker inside generated code. `PASS` is +emitted only after the Eve runtimes stop and the sandbox is destroyed. diff --git a/packages/integrations/examples/eve/agent/agent.ts b/packages/integrations/examples/eve/agent/agent.ts new file mode 100644 index 0000000000..1b74f21817 --- /dev/null +++ b/packages/integrations/examples/eve/agent/agent.ts @@ -0,0 +1,92 @@ +import { openai } from "@ai-sdk/openai"; +import { defineAgent } from "eve"; +import { mockModel } from "eve/evals"; + +const stagehandTool = "stagehand__code_execute"; + +export default defineAgent({ + modelContextWindowTokens: 131_072, + model: + process.env.EVE_STAGEHAND_DETERMINISTIC === "1" + ? mockModel(({ toolResults, tools }) => { + const stagehandResults = toolResults.filter(({ name }) => name === stagehandTool); + + if (!tools.some(({ name }) => name === stagehandTool)) { + return { + toolCalls: [ + { + name: "connection_search", + input: { connection: "stagehand", keywords: "execute browser JavaScript" }, + }, + ], + }; + } + + if (stagehandResults.length === 0) { + return { + toolCalls: [ + { + name: stagehandTool, + input: { + code: ` + await page.goto("https://example.com", { + waitUntil: "domcontentloaded", + }); + await page.evaluate(() => { + document.documentElement.dataset.eveStagehandDirectMarker = + "eve-direct-persistent"; + }); + return { + pageId: page.pageId, + title: await page.title(), + directMarker: await page.evaluate( + () => document.documentElement.dataset.eveStagehandDirectMarker, + ), + modelKeyVisible: process.env.OPENAI_API_KEY ?? null, + hostMarkerVisible: process.env.EVE_HOST_ONLY_MARKER ?? null, + }; + `, + }, + }, + ], + }; + } + + if (stagehandResults.length === 1) { + return { + toolCalls: [ + { + name: stagehandTool, + input: { + code: ` + const directMarker = await page.evaluate( + () => document.documentElement.dataset.eveStagehandDirectMarker, + ); + if (directMarker !== "eve-direct-persistent") { + throw new Error("Eve lost Stagehand browser state between tool calls"); + } + await page.evaluate(() => { + document.documentElement.dataset.eveStagehandModelMarker = + "eve-model-persistent"; + }); + return { + pageId: page.pageId, + title: await page.title(), + directMarker, + modelMarker: await page.evaluate( + () => document.documentElement.dataset.eveStagehandModelMarker, + ), + modelKeyVisible: process.env.OPENAI_API_KEY ?? null, + hostMarkerVisible: process.env.EVE_HOST_ONLY_MARKER ?? null, + }; + `, + }, + }, + ], + }; + } + + return `STAGEHAND_RESULTS ${JSON.stringify(stagehandResults.map(({ output }) => output))}`; + }) + : openai(process.env.EVE_STAGEHAND_MODEL ?? "gpt-5-mini"), +}); diff --git a/packages/integrations/examples/eve/agent/connections/stagehand.ts b/packages/integrations/examples/eve/agent/connections/stagehand.ts new file mode 100644 index 0000000000..9dfc24d404 --- /dev/null +++ b/packages/integrations/examples/eve/agent/connections/stagehand.ts @@ -0,0 +1,17 @@ +import { defineMcpClientConnection } from "eve/connections"; + +const endpoint = process.env.STAGEHAND_MCP_URL ?? "http://127.0.0.1:3000/mcp"; + +export default defineMcpClientConnection({ + url: endpoint, + description: + "Stagehand browser automation isolated behind an authenticated code-mode MCP gateway.", + auth: { + getToken: async () => { + const token = process.env.STAGEHAND_MCP_TOKEN; + if (!token) throw new Error("STAGEHAND_MCP_TOKEN is required"); + return { token }; + }, + }, + tools: { allow: ["code_execute"] }, +}); diff --git a/packages/integrations/examples/eve/agent/instructions.md b/packages/integrations/examples/eve/agent/instructions.md new file mode 100644 index 0000000000..f25cb06bcb --- /dev/null +++ b/packages/integrations/examples/eve/agent/instructions.md @@ -0,0 +1,3 @@ +You are a browser agent. Use `connection_search` to discover the Stagehand connection, then use its +`code_execute` tool for browser work. Follow the requested number and order of calls exactly. Keep +browser state in the existing tool session and return only the evidence the user requested. diff --git a/packages/integrations/examples/eve/evals/evals.config.ts b/packages/integrations/examples/eve/evals/evals.config.ts new file mode 100644 index 0000000000..3a1a10d6fd --- /dev/null +++ b/packages/integrations/examples/eve/evals/evals.config.ts @@ -0,0 +1,3 @@ +import { defineEvalConfig } from "eve/evals"; + +export default defineEvalConfig({}); diff --git a/packages/integrations/examples/eve/evals/stagehand.eval.ts b/packages/integrations/examples/eve/evals/stagehand.eval.ts new file mode 100644 index 0000000000..38c9cff98c --- /dev/null +++ b/packages/integrations/examples/eve/evals/stagehand.eval.ts @@ -0,0 +1,41 @@ +import { defineEval } from "eve/evals"; +import { includes } from "eve/evals/expect"; + +export default defineEval({ + description: "Eve discovers and calls Stagehand through an authenticated remote MCP gateway.", + async test(t) { + await t.send( + [ + "Use connection_search to discover the Stagehand connection.", + "Then call stagehand__code_execute exactly twice, in order.", + 'First open https://example.com and store "eve-direct-persistent" in', + "document.documentElement.dataset.eveStagehandDirectMarker.", + "Return the pageId, title, marker, process.env.OPENAI_API_KEY, and", + "process.env.EVE_HOST_ONLY_MARKER from that first call.", + "In the second call, read and verify that same direct marker without navigating.", + 'Then store "eve-model-persistent" in', + "document.documentElement.dataset.eveStagehandModelMarker and return both markers,", + "the current pageId and title, and the same two environment lookups.", + "Report the title and both exact markers after the two calls.", + ].join(" "), + ); + + t.succeeded(); + t.calledTool("connection_search", { count: 1 }); + t.calledTool("stagehand__code_execute", { + count: 2, + output: (value) => { + const output = JSON.stringify(value); + return ( + output.includes("Example Domain") && + output.includes("eve-direct-persistent") && + output.includes('"modelKeyVisible":null') && + output.includes('"hostMarkerVisible":null') + ); + }, + }); + t.check(t.reply, includes("Example Domain")); + t.check(t.reply, includes("eve-direct-persistent")); + t.check(t.reply, includes("eve-model-persistent")); + }, +}); diff --git a/packages/integrations/examples/eve/package.json b/packages/integrations/examples/eve/package.json new file mode 100644 index 0000000000..845a0ed60a --- /dev/null +++ b/packages/integrations/examples/eve/package.json @@ -0,0 +1,28 @@ +{ + "name": "@browserbasehq/stagehand-integrations-example-eve", + "version": "4.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "eve build", + "contract": "tsx src/contract.ts", + "dev": "eve dev", + "e2e": "tsx src/e2e.ts", + "typecheck": "eve build && tsc --noEmit" + }, + "dependencies": { + "@ai-sdk/openai": "catalog:", + "@browserbasehq/stagehand-integrations-example-vercel-sandbox": "workspace:*", + "@modelcontextprotocol/sdk": "catalog:", + "eve": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:" + }, + "engines": { + "node": ">=24" + } +} diff --git a/packages/integrations/examples/eve/src/contract-server.ts b/packages/integrations/examples/eve/src/contract-server.ts new file mode 100644 index 0000000000..e4bb6240a0 --- /dev/null +++ b/packages/integrations/examples/eve/src/contract-server.ts @@ -0,0 +1,107 @@ +import assert from "node:assert/strict"; +import { randomBytes, timingSafeEqual } from "node:crypto"; +import { once } from "node:events"; +import type { Server } from "node:http"; + +import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { z } from "zod"; + +export type EveContractServer = { + url: string; + token: string; + codeExecuteCalls: () => number; + close: () => Promise; +}; + +export async function startEveContractServer(): Promise { + const token = randomBytes(32).toString("hex"); + const expectedAuthorization = Buffer.from(`Bearer ${token}`); + let codeExecuteCalls = 0; + const app = createMcpExpressApp(); + + app.use("/mcp", (request, response, next) => { + const authorization = request.headers.authorization; + const provided = authorization === undefined ? undefined : Buffer.from(authorization); + if ( + provided === undefined || + provided.length !== expectedAuthorization.length || + !timingSafeEqual(provided, expectedAuthorization) + ) { + response.status(401).type("text/plain").send("Unauthorized\n"); + return; + } + next(); + }); + + app.post("/mcp", async (request, response) => { + const server = new McpServer({ name: "eve-stagehand-contract", version: "1.0.0" }); + server.registerTool( + "code_execute", + { + description: + "# Stagehand V4 code-mode syntax\nContract-only Stagehand browser execution tool.", + inputSchema: { code: z.string() }, + }, + async () => { + codeExecuteCalls += 1; + const value = { + pageId: "contract-page", + title: "Example Domain", + directMarker: "eve-direct-persistent", + ...(codeExecuteCalls > 1 ? { modelMarker: "eve-model-persistent" } : {}), + modelKeyVisible: null, + hostMarkerVisible: null, + }; + const result = { ok: true, value }; + return { + content: [{ type: "text" as const, text: JSON.stringify(result) }], + structuredContent: result, + }; + }, + ); + + const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); + try { + await server.connect(transport); + await transport.handleRequest(request, response, request.body); + response.on("close", () => { + void transport.close(); + void server.close(); + }); + } catch { + if (!response.headersSent) { + response.status(500).json({ + jsonrpc: "2.0", + error: { code: -32603, message: "Contract MCP request failed" }, + id: null, + }); + } + } + }); + app.get("/mcp", (_request, response) => { + response.status(405).set("Allow", "POST").send("Method Not Allowed"); + }); + app.delete("/mcp", (_request, response) => { + response.status(405).set("Allow", "POST").send("Method Not Allowed"); + }); + + const httpServer = app.listen(0, "127.0.0.1"); + await once(httpServer, "listening"); + const address = httpServer.address(); + assert.ok(address && typeof address === "object"); + + return { + url: `http://127.0.0.1:${address.port}/mcp`, + token, + codeExecuteCalls: () => codeExecuteCalls, + close: () => closeServer(httpServer), + }; +} + +function closeServer(server: Server): Promise { + return new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); +} diff --git a/packages/integrations/examples/eve/src/contract.ts b/packages/integrations/examples/eve/src/contract.ts new file mode 100644 index 0000000000..38bf8d5409 --- /dev/null +++ b/packages/integrations/examples/eve/src/contract.ts @@ -0,0 +1,37 @@ +import assert from "node:assert/strict"; + +import { startEveContractServer } from "./contract-server.js"; +import { assertEveEndpointContract, runEveStagehandEval } from "./run-eval.js"; + +const server = await startEveContractServer(); +let primaryError: unknown; +try { + await assertEveEndpointContract(server); + await runEveStagehandEval(server, true); + assert.equal(server.codeExecuteCalls(), 2, "Eve must call code_execute twice in one session"); +} catch (error) { + primaryError = error; +} + +let cleanupError: unknown; +await server.close().catch((error: unknown) => { + cleanupError = error; +}); + +if (primaryError !== undefined && cleanupError !== undefined) { + throw new AggregateError([primaryError, cleanupError], "Eve contract and cleanup both failed"); +} +if (primaryError !== undefined) throw primaryError; +if (cleanupError !== undefined) throw cleanupError; + +process.stdout.write( + `${JSON.stringify({ + status: "PASS", + framework: "eve", + proof: "deterministic authenticated Streamable HTTP contract", + connectionSearchCalls: 1, + codeExecuteCalls: 2, + unauthorizedStatus: 401, + optionalGetStatus: 405, + })}\n`, +); diff --git a/packages/integrations/examples/eve/src/e2e.ts b/packages/integrations/examples/eve/src/e2e.ts new file mode 100644 index 0000000000..212179dd5c --- /dev/null +++ b/packages/integrations/examples/eve/src/e2e.ts @@ -0,0 +1,71 @@ +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; + +import { createStagehandSandbox } from "@browserbasehq/stagehand-integrations-example-vercel-sandbox"; + +import { assertEveEndpointContract, runEveStagehandEval } from "./run-eval.js"; + +const NO_ERROR = Symbol("no error"); +process.env.EVE_HOST_ONLY_MARKER = `host-${randomUUID()}`; + +const connection = await createStagehandSandbox({ + packageArtifactsPath: requiredEnvironment("STAGEHAND_SANDBOX_ARTIFACTS"), + browserbaseApiKey: requiredEnvironment("BROWSERBASE_API_KEY"), + browserbaseProjectId: requiredEnvironment("BROWSERBASE_PROJECT_ID"), + vercelCredentials: vercelCredentialsFromEnvironment(), +}); + +let primaryError: unknown = NO_ERROR; +try { + await assertEveEndpointContract(connection); + await runEveStagehandEval(connection, true); + await runEveStagehandEval(connection, false); +} catch (error) { + primaryError = error; +} + +let cleanupError: unknown; +await connection.close().catch((error: unknown) => { + cleanupError = error; +}); + +if (primaryError !== NO_ERROR && cleanupError !== undefined) { + throw new AggregateError([primaryError, cleanupError], "Eve E2E and sandbox cleanup both failed"); +} +if (primaryError !== NO_ERROR) throw primaryError; +if (cleanupError !== undefined) throw cleanupError; + +process.stdout.write( + `${JSON.stringify({ + status: "PASS", + framework: "eve", + eveVersion: "0.29.4", + deterministicEval: true, + realModelEval: true, + connectionSearchCallsPerEval: 1, + codeExecuteCallsPerEval: 2, + sessionPersisted: true, + modelCredentialIsolated: true, + unauthorizedStatus: 401, + optionalGetStatus: 405, + cleanup: ["eve-runtime", "vercel-sandbox"], + })}\n`, +); + +function requiredEnvironment(name: string): string { + const value = process.env[name]; + assert.ok(value, `Missing ${name}`); + return value; +} + +function vercelCredentialsFromEnvironment(): + | { teamId: string; projectId: string; token: string } + | undefined { + const token = process.env.VERCEL_TOKEN; + if (!token) return undefined; + return { + teamId: requiredEnvironment("VERCEL_TEAM_ID"), + projectId: requiredEnvironment("VERCEL_PROJECT_ID"), + token, + }; +} diff --git a/packages/integrations/examples/eve/src/run-eval.ts b/packages/integrations/examples/eve/src/run-eval.ts new file mode 100644 index 0000000000..bf331f2aad --- /dev/null +++ b/packages/integrations/examples/eve/src/run-eval.ts @@ -0,0 +1,122 @@ +import assert from "node:assert/strict"; +import { spawn, type ChildProcess } from "node:child_process"; +import { once } from "node:events"; +import { mkdtemp, rm } from "node:fs/promises"; +import http from "node:http"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const EVE_BINARY = fileURLToPath(new URL("../node_modules/.bin/eve", import.meta.url)); +const EXAMPLE_ROOT = fileURLToPath(new URL("../", import.meta.url)); +const BUILT_SERVER = fileURLToPath(new URL("../.output/server/index.mjs", import.meta.url)); + +export type EveStagehandConnection = { + url: URL | string; + token: string; +}; + +export async function assertEveEndpointContract(connection: EveStagehandConnection): Promise { + const unauthorized = await fetch(connection.url); + assert.equal(unauthorized.status, 401, "the Stagehand endpoint must require its bearer token"); + + const optionalGet = await fetch(connection.url, { + headers: { authorization: `Bearer ${connection.token}` }, + }); + assert.equal(optionalGet.status, 405, "Eve must tolerate the endpoint's optional GET rejection"); +} + +export async function runEveStagehandEval( + connection: EveStagehandConnection, + deterministic: boolean, +): Promise { + const environment = eveEnvironment(connection, deterministic); + await runChild(EVE_BINARY, ["build"], environment); + + const port = await reservePort(); + const serverRoot = await mkdtemp(join(tmpdir(), "eve-stagehand-")); + const server = spawn(process.execPath, [BUILT_SERVER], { + cwd: serverRoot, + env: { ...environment, HOST: "127.0.0.1", PORT: String(port) }, + stdio: "inherit", + }); + try { + await waitForAgent(port, server); + await runChild( + EVE_BINARY, + ["eval", "stagehand", "--skip-report", "--url", `http://127.0.0.1:${port}`], + environment, + ); + } finally { + await stopChild(server); + await rm(serverRoot, { recursive: true, force: true }); + } +} + +function eveEnvironment( + connection: EveStagehandConnection, + deterministic: boolean, +): NodeJS.ProcessEnv { + const environment = { ...process.env }; + if (deterministic) environment.EVE_STAGEHAND_DETERMINISTIC = "1"; + else delete environment.EVE_STAGEHAND_DETERMINISTIC; + environment.STAGEHAND_MCP_URL = connection.url.toString(); + environment.STAGEHAND_MCP_TOKEN = connection.token; + return environment; +} + +async function runChild( + command: string, + args: string[], + environment: NodeJS.ProcessEnv, +): Promise { + const child = spawn(command, args, { + cwd: EXAMPLE_ROOT, + env: environment, + stdio: "inherit", + }); + const [exitCode, signal] = (await once(child, "exit")) as [number | null, NodeJS.Signals | null]; + if (exitCode !== 0) { + throw new Error( + `${command} exited with ${signal === null ? `code ${String(exitCode)}` : signal}`, + ); + } +} + +async function reservePort(): Promise { + const server = http.createServer(); + server.listen(0, "127.0.0.1"); + await once(server, "listening"); + const address = server.address(); + assert.ok(address && typeof address === "object"); + await new Promise((resolve, reject) => + server.close((error) => (error ? reject(error) : resolve())), + ); + return address.port; +} + +async function waitForAgent(port: number, child: ChildProcess): Promise { + for (let attempt = 0; attempt < 200; attempt += 1) { + if (child.exitCode !== null || child.signalCode !== null) { + throw new Error("the built Eve server exited before startup"); + } + const response = await fetch(`http://127.0.0.1:${port}/eve/v1/health`).catch(() => undefined); + if (response?.ok) return; + await new Promise((resolve) => setTimeout(resolve, 50)); + } + throw new Error("the built Eve server did not become healthy"); +} + +async function stopChild(child: ChildProcess): Promise { + if (child.exitCode !== null || child.signalCode !== null) return; + const exited = once(child, "exit").then(() => true); + child.kill("SIGTERM"); + const stopped = await Promise.race([ + exited, + new Promise((resolve) => setTimeout(() => resolve(false), 3_000)), + ]); + if (!stopped) { + child.kill("SIGKILL"); + await exited; + } +} diff --git a/packages/integrations/examples/eve/tsconfig.json b/packages/integrations/examples/eve/tsconfig.json new file mode 100644 index 0000000000..3512e1ce7e --- /dev/null +++ b/packages/integrations/examples/eve/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2022", + "types": ["node"], + "rootDir": ".", + "noEmit": true, + "skipLibCheck": true + }, + "include": ["agent/**/*.ts", "evals/**/*.ts", "src/**/*.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23b7e42489..39af0cbd21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -298,6 +298,9 @@ catalogs: esbuild: specifier: 0.28.1 version: 0.28.1 + eve: + specifier: 0.29.4 + version: 0.29.4 fflate: specifier: ^0.8.3 version: 0.8.3 @@ -604,6 +607,34 @@ importers: specifier: 'catalog:' version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@24.13.2)(vite@8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0)) + packages/integrations/examples/eve: + dependencies: + '@ai-sdk/openai': + specifier: 'catalog:' + version: 4.0.8(zod@4.4.3) + '@browserbasehq/stagehand-integrations-example-vercel-sandbox': + specifier: workspace:* + version: link:../vercel-sandbox + '@modelcontextprotocol/sdk': + specifier: 'catalog:' + version: 1.29.0(zod@4.4.3) + eve: + specifier: 'catalog:' + version: 0.29.4(@opentelemetry/api@1.9.1)(ai@7.0.16(zod@4.4.3))(aws4fetch@1.0.20)(dotenv@17.4.2)(jiti@1.21.7)(lru-cache@11.5.2)(vite@8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0))(xml2js@0.6.2) + zod: + specifier: 'catalog:' + version: 4.4.3 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 24.13.2 + tsx: + specifier: 'catalog:' + version: 4.23.1 + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages/integrations/examples/mastra: dependencies: '@browserbasehq/stagehand-integrations-example-vercel-sandbox': @@ -3873,6 +3904,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -3940,6 +3975,14 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.4.10: + resolution: {integrity: sha512-pz3oubH/dt12KjqsUB0IuXW4nwRDQ583iDsP4555Cpdqx0NoU7pGlWBcayyFI8f/l/idRpgjMEfwuOxSWJYlIA==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true + css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -3984,6 +4027,29 @@ packages: dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -4217,6 +4283,24 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-runner@0.1.16: + resolution: {integrity: sha512-2LRJM4P2KLX6J83QZZrMqvgCDt/D5ea7wPcI3yYiy5cG/9rX5QwdwZFx0D7ktWnjdRyZxYjttGGorb5nFqb1CA==} + hasBin: true + peerDependencies: + '@netlify/runtime': ^4.1.23 + '@vercel/queue': '>=0.2.0' + miniflare: ^4.20260515.0 + wrangler: ^4.0.0 + peerDependenciesMeta: + '@netlify/runtime': + optional: true + '@vercel/queue': + optional: true + miniflare: + optional: true + wrangler: + optional: true + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -4340,6 +4424,26 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + eve@0.29.4: + resolution: {integrity: sha512-EwOmL37l+Iuu7Umno7at3flFpMs4AtT9cg1J4dt7EZ8ZdxaCvGXwvKGwiulMkG8oXkMQ5CNhxMi2ruHJwrtpwQ==} + engines: {node: '>=24'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + ai: ^7.0.38 + braintrust: ^3.0.0 + just-bash: ^3.0.0 + microsandbox: ^0.5.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + braintrust: + optional: true + just-bash: + optional: true + microsandbox: + optional: true + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4397,6 +4501,9 @@ packages: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -4727,6 +4834,16 @@ packages: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} + h3@2.0.1-rc.22: + resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -4861,6 +4978,9 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + httpxy@0.5.5: + resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==} + human-id@4.2.0: resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} hasBin: true @@ -5769,10 +5889,44 @@ packages: react: '>= 18.3.0 < 19.0.0' react-dom: '>= 18.3.0 < 19.0.0' + nf3@0.3.23: + resolution: {integrity: sha512-RWVLAWozmVD3AaDmaU3qMGB3v+yNlH5d9qqStI4e/WLlNQVnJ4YErGDbYCIrGFyrHdbF6I6Baf0Ae6c7tFYmSg==} + nimma@0.2.3: resolution: {integrity: sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==} engines: {node: ^12.20 || >=14.13} + nitro@3.0.260610-beta: + resolution: {integrity: sha512-KPb4L5yaF/Rx/xoGMpgHRJvZhbhGiqbRKOwwPLCH9jKTKTsEUHLjnJas85AeCzaswqa8Wi52eQBtRsODC4PS0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@vercel/queue': ^0.3.0 + dotenv: '*' + giget: '*' + jiti: ^2.7.0 + rollup: ^4.61.1 + vite: 8.1.3 + xml2js: ^0.6.2 + zephyr-agent: ^0.2.0 + peerDependenciesMeta: + '@vercel/queue': + optional: true + dotenv: + optional: true + giget: + optional: true + jiti: + optional: true + rollup: + optional: true + vite: + optional: true + xml2js: + optional: true + zephyr-agent: + optional: true + nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} @@ -5988,6 +6142,15 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} + ocache@0.1.5: + resolution: {integrity: sha512-kNNnkkVQup/QDvmTz8Q84wc2ntiyoVHDxa6eHWKt5qdGAmFRBIxy83rxgCYEjW0x06UJ9E3P6VgM2yY4rOBH4w==} + + ofetch@2.0.0-alpha.3: + resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + ollama-ai-provider-v2@1.5.5: resolution: {integrity: sha512-1YwTFdPjhPNHny/DrOHO+s8oVGGIE5Jib61/KnnjPRNWQhVVimrJJdaAX3e6nNRRDXrY5zbb9cfm2+yVvgsrqw==} engines: {node: '>=18'} @@ -6729,6 +6892,9 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -6975,6 +7141,11 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + srvx@0.11.22: + resolution: {integrity: sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==} + engines: {node: '>=20.16.0'} + hasBin: true + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -7331,14 +7502,21 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - undici@7.29.0: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} + undici@8.9.0: + resolution: {integrity: sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==} + engines: {node: '>=22.19.0'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -7405,6 +7583,80 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unstorage@2.0.0-alpha.7: + resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} + peerDependencies: + '@azure/app-configuration': ^1.11.0 + '@azure/cosmos': ^4.9.1 + '@azure/data-tables': ^13.3.2 + '@azure/identity': ^4.13.0 + '@azure/keyvault-secrets': ^4.10.0 + '@azure/storage-blob': ^12.31.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.13.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.36.2 + '@vercel/blob': '>=0.27.3' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + chokidar: ^4 || ^5 + db0: '>=0.3.4' + idb-keyval: ^6.2.2 + ioredis: ^5.9.3 + lru-cache: ^11.2.6 + mongodb: ^6 || ^7 + ofetch: '*' + uploadthing: ^7.7.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + chokidar: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + lru-cache: + optional: true + mongodb: + optional: true + ofetch: + optional: true + uploadthing: + optional: true + urijs@1.19.11: resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} @@ -11161,6 +11413,8 @@ snapshots: concat-map@0.0.1: {} + consola@3.4.2: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -11212,6 +11466,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.4.10(srvx@0.11.22): + optionalDependencies: + srvx: 0.11.22 + css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -11254,6 +11512,8 @@ snapshots: dateformat@4.6.3: {} + db0@0.3.4: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -11453,6 +11713,13 @@ snapshots: env-paths@2.2.1: {} + env-runner@0.1.16: + dependencies: + crossws: 0.4.10(srvx@0.11.22) + exsolve: 1.1.1 + httpxy: 0.5.5 + srvx: 0.11.22 + environment@1.1.0: {} error-ex@1.3.4: @@ -11693,6 +11960,53 @@ snapshots: etag@1.8.1: {} + eve@0.29.4(@opentelemetry/api@1.9.1)(ai@7.0.16(zod@4.4.3))(aws4fetch@1.0.20)(dotenv@17.4.2)(jiti@1.21.7)(lru-cache@11.5.2)(vite@8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0))(xml2js@0.6.2): + dependencies: + ai: 7.0.16(zod@4.4.3) + nitro: 3.0.260610-beta(aws4fetch@1.0.20)(dotenv@17.4.2)(jiti@1.21.7)(lru-cache@11.5.2)(vite@8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0))(xml2js@0.6.2) + undici: 8.9.0 + optionalDependencies: + '@opentelemetry/api': 1.9.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@netlify/runtime' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vercel/queue' + - aws4fetch + - better-sqlite3 + - chokidar + - dotenv + - drizzle-orm + - giget + - idb-keyval + - ioredis + - jiti + - lru-cache + - miniflare + - mongodb + - mysql2 + - rollup + - sqlite3 + - uploadthing + - vite + - wrangler + - xml2js + - zephyr-agent + event-target-shim@5.0.1: {} events-universal@1.0.1: @@ -11812,6 +12126,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.1.1: {} + extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 @@ -12213,6 +12529,13 @@ snapshots: section-matter: 1.0.0 strip-bom-string: 1.0.0 + h3@2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22)): + dependencies: + rou3: 0.8.1 + srvx: 0.11.22 + optionalDependencies: + crossws: 0.4.10(srvx@0.11.22) + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -12464,6 +12787,8 @@ snapshots: transitivePeerDependencies: - supports-color + httpxy@0.5.5: {} + human-id@4.2.0: {} human-signals@8.0.1: {} @@ -13625,6 +13950,8 @@ snapshots: - supports-color - unified + nf3@0.3.23: {} + nimma@0.2.3: dependencies: '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) @@ -13635,6 +13962,59 @@ snapshots: jsonpath-plus: 10.4.0 lodash.topath: 4.5.2 + nitro@3.0.260610-beta(aws4fetch@1.0.20)(dotenv@17.4.2)(jiti@1.21.7)(lru-cache@11.5.2)(vite@8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0))(xml2js@0.6.2): + dependencies: + consola: 3.4.2 + crossws: 0.4.10(srvx@0.11.22) + db0: 0.3.4 + env-runner: 0.1.16 + h3: 2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22)) + hookable: 6.1.1 + nf3: 0.3.23 + ocache: 0.1.5 + ofetch: 2.0.0-alpha.3 + ohash: 2.0.11 + rolldown: 1.1.5 + srvx: 0.11.22 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.7(aws4fetch@1.0.20)(db0@0.3.4)(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3) + optionalDependencies: + dotenv: 17.4.2 + jiti: 1.21.7 + vite: 8.1.3(@types/node@24.13.2)(esbuild@0.28.1)(jiti@1.21.7)(tsx@4.23.1)(yaml@2.9.0) + xml2js: 0.6.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@netlify/runtime' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - better-sqlite3 + - chokidar + - drizzle-orm + - idb-keyval + - ioredis + - lru-cache + - miniflare + - mongodb + - mysql2 + - sqlite3 + - uploadthing + - wrangler + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -13713,6 +14093,14 @@ snapshots: obug@2.1.3: {} + ocache@0.1.5: + dependencies: + ohash: 2.0.11 + + ofetch@2.0.0-alpha.3: {} + + ohash@2.0.11: {} + ollama-ai-provider-v2@1.5.5(zod@4.4.3): dependencies: '@ai-sdk/provider': 2.0.3 @@ -14651,6 +15039,8 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.1.5 + rou3@0.8.1: {} + router@2.2.0: dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -15033,6 +15423,8 @@ snapshots: sprintf-js@1.0.3: {} + srvx@0.11.22: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -15486,10 +15878,16 @@ snapshots: undici-types@7.24.6: {} - unicorn-magic@0.3.0: {} - undici@7.29.0: {} + undici@8.9.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unicorn-magic@0.3.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -15586,6 +15984,13 @@ snapshots: unpipe@1.0.0: {} + unstorage@2.0.0-alpha.7(aws4fetch@1.0.20)(db0@0.3.4)(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3): + optionalDependencies: + aws4fetch: 1.0.20 + db0: 0.3.4 + lru-cache: 11.5.2 + ofetch: 2.0.0-alpha.3 + urijs@1.19.11: {} use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.3): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a1c3071966..13612cb592 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,6 +8,7 @@ catalog: "@mastra/core": ^1.55.0 "@mastra/mcp": ^1.15.0 "@modelcontextprotocol/sdk": 1.29.0 + eve: 0.29.4 "@vercel/sandbox": 2.9.2 "@ast-grep/lang-go": 0.0.6 "@ast-grep/lang-python": 0.0.6