diff --git a/docs/deploy/cloudflare.mdx b/docs/deploy/cloudflare.mdx index 5328653..46b0890 100644 --- a/docs/deploy/cloudflare.mdx +++ b/docs/deploy/cloudflare.mdx @@ -31,18 +31,23 @@ RUN sandbox-agent install-agent claude && sandbox-agent install-agent codex EXPOSE 8000 ``` -## TypeScript proxy example +## TypeScript example ```typescript import { getSandbox, type Sandbox } from "@cloudflare/sandbox"; +import { Hono } from "hono"; +import { SandboxAgent } from "sandbox-agent"; + export { Sandbox } from "@cloudflare/sandbox"; -type Env = { +type Bindings = { Sandbox: DurableObjectNamespace; + ASSETS: Fetcher; ANTHROPIC_API_KEY?: string; OPENAI_API_KEY?: string; }; +const app = new Hono<{ Bindings: Bindings }>(); const PORT = 8000; async function isServerRunning(sandbox: Sandbox): Promise { @@ -54,64 +59,50 @@ async function isServerRunning(sandbox: Sandbox): Promise { } } -async function ensureRunning(sandbox: Sandbox, env: Env): Promise { - if (await isServerRunning(sandbox)) return; - - const envVars: Record = {}; - if (env.ANTHROPIC_API_KEY) envVars.ANTHROPIC_API_KEY = env.ANTHROPIC_API_KEY; - if (env.OPENAI_API_KEY) envVars.OPENAI_API_KEY = env.OPENAI_API_KEY; - await sandbox.setEnvVars(envVars); - - await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`); - - for (let i = 0; i < 30; i++) { - if (await isServerRunning(sandbox)) return; - await new Promise((r) => setTimeout(r, 200)); +async function getReadySandbox(name: string, env: Bindings): Promise { + const sandbox = getSandbox(env.Sandbox, name); + if (!(await isServerRunning(sandbox))) { + const envVars: Record = {}; + if (env.ANTHROPIC_API_KEY) envVars.ANTHROPIC_API_KEY = env.ANTHROPIC_API_KEY; + if (env.OPENAI_API_KEY) envVars.OPENAI_API_KEY = env.OPENAI_API_KEY; + await sandbox.setEnvVars(envVars); + await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`); } - - throw new Error("sandbox-agent failed to start"); + return sandbox; } -export default { - async fetch(request: Request, env: Env): Promise { - const url = new URL(request.url); - const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/); +app.post("/sandbox/:name/prompt", async (c) => { + const sandbox = await getReadySandbox(c.req.param("name"), c.env); - if (!match) { - return new Response("Not found", { status: 404 }); - } + const sdk = await SandboxAgent.connect({ + fetch: (input, init) => sandbox.containerFetch(input as Request | string | URL, init, PORT), + }); - const [, name, path = "/"] = match; - const sandbox = getSandbox(env.Sandbox, name); - await ensureRunning(sandbox, env); + const session = await sdk.createSession({ agent: "codex" }); + const response = await session.prompt([{ type: "text", text: "Summarize this repository" }]); + await sdk.destroySession(session.id); + await sdk.dispose(); - return sandbox.containerFetch( - new Request(`http://localhost${path}${url.search}`, request), - PORT, - ); - }, -}; -``` - -## Connect from a client - -```typescript -import { SandboxAgent } from "sandbox-agent"; - -const sdk = await SandboxAgent.connect({ - baseUrl: "http://localhost:8787/sandbox/my-sandbox", + return c.json(response); }); -const session = await sdk.createSession({ agent: "claude" }); +app.all("/sandbox/:name/proxy/*", async (c) => { + const sandbox = await getReadySandbox(c.req.param("name"), c.env); + const wildcard = c.req.param("*"); + const path = wildcard ? `/${wildcard}` : "/"; + const query = new URL(c.req.raw.url).search; -const off = session.onEvent((event) => { - console.log(event.sender, event.payload); + return sandbox.containerFetch(new Request(`http://localhost${path}${query}`, c.req.raw), PORT); }); -await session.prompt([{ type: "text", text: "Summarize this repository" }]); -off(); +app.all("*", (c) => c.env.ASSETS.fetch(c.req.raw)); + +export default app; ``` +Create the SDK client inside the Worker using custom `fetch` backed by `sandbox.containerFetch(...)`. +This keeps all Sandbox Agent calls inside the Cloudflare sandbox routing path and does not require a `baseUrl`. + ## Local development ```bash @@ -121,7 +112,7 @@ npm run dev Test health: ```bash -curl http://localhost:8787/sandbox/demo/v1/health +curl http://localhost:8787/sandbox/demo/proxy/v1/health ``` ## Production deployment diff --git a/docs/sdk-overview.mdx b/docs/sdk-overview.mdx index 215ab82..7974c65 100644 --- a/docs/sdk-overview.mdx +++ b/docs/sdk-overview.mdx @@ -39,6 +39,14 @@ const sdk = await SandboxAgent.connect({ }); ``` +With a custom fetch handler (for example, proxying requests inside Workers): + +```ts +const sdk = await SandboxAgent.connect({ + fetch: (input, init) => customFetch(input, init), +}); +``` + With persistence: ```ts @@ -158,9 +166,10 @@ console.log(url); Parameters: -- `baseUrl` (required): Sandbox Agent server URL +- `baseUrl` (required unless `fetch` is provided): Sandbox Agent server URL - `token` (optional): Bearer token for authenticated servers - `headers` (optional): Additional request headers +- `fetch` (optional): Custom fetch implementation used by SDK HTTP and ACP calls ## Types diff --git a/examples/cloudflare/README.md b/examples/cloudflare/README.md index 783d34e..415bd39 100644 --- a/examples/cloudflare/README.md +++ b/examples/cloudflare/README.md @@ -36,6 +36,16 @@ Test the endpoint: curl http://localhost:8787 ``` +Test prompt routing through the SDK with a custom sandbox fetch handler: + +```bash +curl -X POST "http://localhost:8787/sandbox/demo/prompt" \ + -H "Content-Type: application/json" \ + -d '{"agent":"codex","prompt":"Reply with one short sentence."}' +``` + +The response includes `events`, an array of all recorded session events for that prompt. + ## Deploy ```bash diff --git a/examples/cloudflare/frontend/App.tsx b/examples/cloudflare/frontend/App.tsx index 2f7f0c5..812f83d 100644 --- a/examples/cloudflare/frontend/App.tsx +++ b/examples/cloudflare/frontend/App.tsx @@ -25,7 +25,7 @@ export function App() { try { // Connect via proxy endpoint (need full URL for SDK) - const baseUrl = `${window.location.origin}/sandbox/${encodeURIComponent(sandboxName)}`; + const baseUrl = `${window.location.origin}/sandbox/${encodeURIComponent(sandboxName)}/proxy`; log(`Connecting to sandbox: ${sandboxName}`); const client = await SandboxAgent.connect({ baseUrl }); diff --git a/examples/cloudflare/package.json b/examples/cloudflare/package.json index e052605..0385703 100644 --- a/examples/cloudflare/package.json +++ b/examples/cloudflare/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@cloudflare/sandbox": "latest", + "hono": "^4.12.2", "react": "^19.1.0", "react-dom": "^19.1.0", "sandbox-agent": "workspace:*" diff --git a/examples/cloudflare/src/index.ts b/examples/cloudflare/src/index.ts index 3866727..4d8ad10 100644 --- a/examples/cloudflare/src/index.ts +++ b/examples/cloudflare/src/index.ts @@ -1,16 +1,20 @@ import { getSandbox, type Sandbox } from "@cloudflare/sandbox"; +import { Hono } from "hono"; +import { HTTPException } from "hono/http-exception"; +import { runPromptTest, type PromptTestRequest } from "./prompt-test"; export { Sandbox } from "@cloudflare/sandbox"; -type Env = { - Bindings: { - Sandbox: DurableObjectNamespace; - ASSETS: Fetcher; - ANTHROPIC_API_KEY?: string; - OPENAI_API_KEY?: string; - }; +type Bindings = { + Sandbox: DurableObjectNamespace; + ASSETS: Fetcher; + ANTHROPIC_API_KEY?: string; + OPENAI_API_KEY?: string; + CODEX_API_KEY?: string; }; +type AppEnv = { Bindings: Bindings }; + const PORT = 8000; /** Check if sandbox-agent is already running by probing its health endpoint */ @@ -23,54 +27,60 @@ async function isServerRunning(sandbox: Sandbox): Promise { } } -/** Ensure sandbox-agent is running in the container */ -async function ensureRunning(sandbox: Sandbox, env: Env["Bindings"]): Promise { - if (await isServerRunning(sandbox)) return; - - // Set environment variables for agents +async function getReadySandbox(name: string, env: Bindings): Promise { + const sandbox = getSandbox(env.Sandbox, name); const envVars: Record = {}; if (env.ANTHROPIC_API_KEY) envVars.ANTHROPIC_API_KEY = env.ANTHROPIC_API_KEY; if (env.OPENAI_API_KEY) envVars.OPENAI_API_KEY = env.OPENAI_API_KEY; + if (env.CODEX_API_KEY) envVars.CODEX_API_KEY = env.CODEX_API_KEY; + if (!envVars.CODEX_API_KEY && envVars.OPENAI_API_KEY) envVars.CODEX_API_KEY = envVars.OPENAI_API_KEY; await sandbox.setEnvVars(envVars); - // Start sandbox-agent server as background process - await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`); + if (!(await isServerRunning(sandbox))) { + await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`); - // Poll health endpoint until server is ready (max ~6 seconds) - for (let i = 0; i < 30; i++) { - if (await isServerRunning(sandbox)) return; - await new Promise((r) => setTimeout(r, 200)); + for (let i = 0; i < 30; i++) { + if (await isServerRunning(sandbox)) break; + await new Promise((r) => setTimeout(r, 200)); + } } + return sandbox; } -export default { - async fetch(request: Request, env: Env["Bindings"]): Promise { - const url = new URL(request.url); +async function proxyToSandbox(sandbox: Sandbox, request: Request, path: string): Promise { + const query = new URL(request.url).search; + return sandbox.containerFetch(new Request(`http://localhost${path}${query}`, request), PORT); +} - // Proxy requests to sandbox-agent: /sandbox/:name/v1/... - const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/); - if (match) { - if (!env.ANTHROPIC_API_KEY && !env.OPENAI_API_KEY) { - return Response.json( - { error: "ANTHROPIC_API_KEY or OPENAI_API_KEY must be set" }, - { status: 500 } - ); - } +const app = new Hono(); - const name = match[1]; - const path = match[2] || "/"; - const sandbox = getSandbox(env.Sandbox, name); +app.onError((error) => { + return new Response(String(error), { status: 500 }); +}); - await ensureRunning(sandbox, env); +app.post("/sandbox/:name/prompt", async (c) => { + if (!(c.req.header("content-type") ?? "").includes("application/json")) { + throw new HTTPException(400, { message: "Content-Type must be application/json" }); + } - // Proxy request to container - return sandbox.containerFetch( - new Request(`http://localhost${path}${url.search}`, request), - PORT - ); - } + let payload: PromptTestRequest; + try { + payload = await c.req.json(); + } catch { + throw new HTTPException(400, { message: "Invalid JSON body" }); + } - // Serve frontend assets - return env.ASSETS.fetch(request); - }, -} satisfies ExportedHandler; + const sandbox = await getReadySandbox(c.req.param("name"), c.env); + return c.json(await runPromptTest(sandbox, payload, PORT)); +}); + +app.all("/sandbox/:name/proxy/*", async (c) => { + const sandbox = await getReadySandbox(c.req.param("name"), c.env); + const wildcard = c.req.param("*"); + const path = wildcard ? `/${wildcard}` : "/"; + return proxyToSandbox(sandbox, c.req.raw, path); +}); + +app.all("*", (c) => c.env.ASSETS.fetch(c.req.raw)); + +export default app; diff --git a/examples/cloudflare/src/prompt-test.ts b/examples/cloudflare/src/prompt-test.ts new file mode 100644 index 0000000..f9bf50c --- /dev/null +++ b/examples/cloudflare/src/prompt-test.ts @@ -0,0 +1,66 @@ +import type { Sandbox } from "@cloudflare/sandbox"; +import { SandboxAgent } from "sandbox-agent"; + +export type PromptTestRequest = { + agent?: string; + prompt?: string; +}; + +export type PromptTestResponse = { + sessionId: string; + agent: string; + prompt: string; + events: unknown[]; +}; + +export async function runPromptTest( + sandbox: Sandbox, + request: PromptTestRequest, + port: number, +): Promise { + const client = await SandboxAgent.connect({ + fetch: (req, init) => + sandbox.containerFetch(req, init, port), + }); + + let sessionId: string | null = null; + try { + const session = await client.createSession({ + agent: request.agent ?? "codex", + }); + sessionId = session.id; + + const promptText = + request.prompt?.trim() || "Reply with a short confirmation."; + await session.prompt([{ type: "text", text: promptText }]); + + const events: unknown[] = []; + let cursor: string | undefined; + while (true) { + const page = await client.getEvents({ + sessionId: session.id, + cursor, + limit: 200, + }); + events.push(...page.items); + if (!page.nextCursor) break; + cursor = page.nextCursor; + } + + return { + sessionId: session.id, + agent: session.agent, + prompt: promptText, + events, + }; + } finally { + if (sessionId) { + try { + await client.destroySession(sessionId); + } catch { + // Ignore cleanup failures; session teardown is best-effort. + } + } + await client.dispose(); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45e190a..ec77fb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,13 +17,16 @@ importers: version: 2.7.6 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) examples/cloudflare: dependencies: '@cloudflare/sandbox': specifier: latest - version: 0.7.2 + version: 0.7.5 + hono: + specifier: ^4.12.2 + version: 4.12.2 react: specifier: ^19.1.0 version: 19.2.4 @@ -36,10 +39,10 @@ importers: devDependencies: '@cloudflare/workers-types': specifier: latest - version: 4.20260210.0 + version: 4.20260302.0 '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 '@types/react': specifier: ^18.3.3 version: 18.3.27 @@ -48,19 +51,19 @@ importers: version: 18.3.7(@types/react@18.3.27) '@vitejs/plugin-react': specifier: ^4.5.0 - version: 4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) typescript: specifier: latest version: 5.9.3 vite: specifier: ^6.2.0 - version: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) wrangler: specifier: latest - version: 4.64.0(@cloudflare/workers-types@4.20260210.0) + version: 4.67.1(@cloudflare/workers-types@4.20260302.0) examples/computesdk: dependencies: @@ -69,14 +72,14 @@ importers: version: link:../shared computesdk: specifier: latest - version: 2.2.0 + version: 2.2.1 sandbox-agent: specifier: workspace:* version: link:../../sdks/typescript devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -85,13 +88,13 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) examples/daytona: dependencies: '@daytonaio/sdk': specifier: latest - version: 0.141.0(ws@8.19.0) + version: 0.144.0(ws@8.19.0) '@sandbox-agent/example-shared': specifier: workspace:* version: link:../shared @@ -101,7 +104,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -126,7 +129,7 @@ importers: version: 4.0.1 '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -135,7 +138,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) examples/e2b: dependencies: @@ -151,7 +154,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -160,7 +163,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) examples/file-system: dependencies: @@ -176,7 +179,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -195,7 +198,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -220,7 +223,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 esbuild: specifier: latest version: 0.27.3 @@ -235,7 +238,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 typescript: specifier: latest version: 5.9.3 @@ -251,7 +254,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -276,7 +279,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 '@types/pg': specifier: latest version: 8.16.0 @@ -301,7 +304,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -323,7 +326,7 @@ importers: version: 4.0.1 '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 typescript: specifier: latest version: 5.9.3 @@ -339,7 +342,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -358,7 +361,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 esbuild: specifier: latest version: 0.27.3 @@ -376,14 +379,14 @@ importers: version: link:../shared '@vercel/sandbox': specifier: latest - version: 1.5.0 + version: 1.6.0 sandbox-agent: specifier: workspace:* version: link:../../sdks/typescript devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -392,7 +395,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) frontend/packages/inspector: dependencies: @@ -417,7 +420,7 @@ importers: version: 18.3.7(@types/react@18.3.27) '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.7.0(vite@5.4.21(@types/node@25.2.3)) + version: 4.7.0(vite@5.4.21(@types/node@25.3.0)) fake-indexeddb: specifier: ^6.2.4 version: 6.2.5 @@ -429,25 +432,25 @@ importers: version: 5.9.3 vite: specifier: ^5.4.7 - version: 5.4.21(@types/node@25.2.3) + version: 5.4.21(@types/node@25.3.0) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) frontend/packages/website: dependencies: '@astrojs/react': specifier: ^4.2.0 - version: 4.4.2(@types/node@25.2.3)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2) + version: 4.4.2(@types/node@25.3.0)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2) '@astrojs/sitemap': specifier: ^3.2.0 version: 3.7.0 '@astrojs/tailwind': specifier: ^6.0.0 - version: 6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)) + version: 6.0.2(astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)) astro: specifier: ^5.1.0 - version: 5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + version: 5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) framer-motion: specifier: ^12.0.0 version: 12.29.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -531,14 +534,14 @@ importers: dependencies: '@daytonaio/sdk': specifier: latest - version: 0.141.0(ws@8.19.0) + version: 0.144.0(ws@8.19.0) '@e2b/code-interpreter': specifier: latest version: 2.3.3 devDependencies: '@types/node': specifier: latest - version: 25.2.3 + version: 25.3.0 tsx: specifier: latest version: 4.21.0 @@ -589,7 +592,7 @@ importers: devDependencies: vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) sdks/cli-shared: devDependencies: @@ -637,7 +640,7 @@ importers: devDependencies: vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) sdks/gigacode/platforms/darwin-arm64: {} @@ -1142,11 +1145,11 @@ packages: resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} - '@cloudflare/sandbox@0.7.2': - resolution: {integrity: sha512-q0dn6RUBLZ8CD4g99sVJbxoQK/BjJ3luw0T03mvTrUbRicjyzdFWP7dlEYzhvqpyd6TWpTsYr+TSHf/95bVuWw==} + '@cloudflare/sandbox@0.7.5': + resolution: {integrity: sha512-lOegEUL6eDsHrsxEMxqRcftsp46hn+ilQryCLuSDghHvnCdDAenzyN/E3nVjQdYAZnoh5xsLzis/G295LcZr1w==} peerDependencies: '@openai/agents': ^0.3.3 - '@opencode-ai/sdk': ^1.0.137 + '@opencode-ai/sdk': ^1.1.40 '@xterm/xterm': '>=5.0.0' peerDependenciesMeta: '@openai/agents': @@ -1156,47 +1159,47 @@ packages: '@xterm/xterm': optional: true - '@cloudflare/unenv-preset@2.12.1': - resolution: {integrity: sha512-tP/Wi+40aBJovonSNJSsS7aFJY0xjuckKplmzDs2Xat06BJ68B6iG7YDUWXJL8gNn0gqW7YC5WhlYhO3QbugQA==} + '@cloudflare/unenv-preset@2.14.0': + resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==} peerDependencies: unenv: 2.0.0-rc.24 - workerd: ^1.20260115.0 + workerd: ^1.20260218.0 peerDependenciesMeta: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260210.0': - resolution: {integrity: sha512-e3vMgzr8ZM6VjpJVFrnMBhjvFhlMIkhT+BLpBk3pKaWsrXao+azDlmzzxB3Zf4CZ8LmCEtaP7n5d2mNGL6Dqww==} + '@cloudflare/workerd-darwin-64@1.20260302.0': + resolution: {integrity: sha512-cGtxPByeVrgoqxbmd8qs631wuGwf8yTm/FY44dEW4HdoXrb5jhlE4oWYHFafedkQCvGjY1Vbs3puAiKnuMxTXQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260210.0': - resolution: {integrity: sha512-ng2uLJVMrI5VrcAS26gDGM+qxCuWD4ZA8VR4i88RdyM8TLn+AqPFisrvn7AMA+QSv0+ck+ZdFtXek7qNp2gNuA==} + '@cloudflare/workerd-darwin-arm64@1.20260302.0': + resolution: {integrity: sha512-WRGqV6RNXM3xoQblJJw1EHKwx9exyhB18cdnToSCUFPObFhk3fzMLoQh7S+nUHUpto6aUrXPVj6R/4G3UPjCxw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260210.0': - resolution: {integrity: sha512-frn2/+6DV59h13JbGSk9ATvJw3uORWssFIKZ/G/to+WRrIDQgCpSrjLtGbFSSn5eBEhYOvwxPKc7IrppkmIj/w==} + '@cloudflare/workerd-linux-64@1.20260302.0': + resolution: {integrity: sha512-gG423mtUjrmlQT+W2+KisLc6qcGcBLR+QcK5x1gje3bu/dF3oNiYuqY7o58A+sQk6IB849UC4UyNclo1RhP2xw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260210.0': - resolution: {integrity: sha512-0fmxEHaDcAF+7gcqnBcQdBCOzNvGz3mTMwqxEYJc5xZgFwQf65/dYK5fnV8z56GVNqu88NEnLMG3DD2G7Ey1vw==} + '@cloudflare/workerd-linux-arm64@1.20260302.0': + resolution: {integrity: sha512-7M25noGI4WlSBOhrIaY8xZrnn87OQKtJg9YWAO2EFqGjF1Su5QXGaLlQVF4fAKbqTywbHnI8BAuIsIlUSNkhCg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260210.0': - resolution: {integrity: sha512-G/Apjk/QLNnwbu8B0JO9FuAJKHNr+gl8X3G/7qaUrpwIkPx5JFQElVE6LKk4teSrycvAy5AzLFAL0lOB1xsUIQ==} + '@cloudflare/workerd-windows-64@1.20260302.0': + resolution: {integrity: sha512-jK1L3ADkiWxFzlqZTq2iHW1Bd2Nzu1fmMWCGZw4sMZ2W1B2WCm2wHwO2SX/py4BgylyEN3wuF+5zagbkNKht9A==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260210.0': - resolution: {integrity: sha512-zHaF0RZVYUQwNCJCECnNAJdMur72Lk3FMiD6wU78Dx3Bv7DQRcuXNmPNuJmsGnosVZCcWintHlPTQ/4BEiDG5w==} + '@cloudflare/workers-types@4.20260302.0': + resolution: {integrity: sha512-mbFRnlu1lNCScMpXZk/X/uBPufYx5OSbq+euGonGRcY+DgOwm2kczGdK401rUh52NB0fFMEcOy/zqwxv7CdDNA==} '@computesdk/cmd@0.4.1': resolution: {integrity: sha512-hhcYrwMnOpRSwWma3gkUeAVsDFG56nURwSaQx8vCepv0IuUv39bK4mMkgszolnUQrVjBDdW7b3lV+l5B2S8fRA==} @@ -1216,14 +1219,14 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@daytonaio/api-client@0.141.0': - resolution: {integrity: sha512-DSPCurIEjfFyXCd07jkDgfsoFppVhTLyIJdvfb0LgG1EgV75BPqqzk2WM4ragBFJUuK2URF5CK7qkaHW0AXKMA==} + '@daytonaio/api-client@0.144.0': + resolution: {integrity: sha512-qr9c6YLntqmxbGAwYNIvHqZxA5zWl0nGY5aH3oK++1x03uDc5YnCwfcX7M7WCemt2iBUtLjQshdRKwFv0sDYsA==} - '@daytonaio/sdk@0.141.0': - resolution: {integrity: sha512-JUopkS9SkO7h4WN8CjparOrP9k954euOF5KG//PeCEFOxUWTPFOME70GrmHXQKa1qkdZiF/4tz9jtZ744B1I2w==} + '@daytonaio/sdk@0.144.0': + resolution: {integrity: sha512-Le8auU/h4xOlcTBQZWEx8jJwDEHeyKT/7oTUzkvONCp50agcuKjd5CDf79wsR9IqMuoCtlYcvhWXCNsdEuWkYg==} - '@daytonaio/toolbox-api-client@0.141.0': - resolution: {integrity: sha512-KGkCLDLAltd9FCic3PhSJGrTp3RwGsUwWEGp5vyWZFQGWpJV8CVp08CH5SBdo4YhuqFUVlyQcwha1HpzpVH++A==} + '@daytonaio/toolbox-api-client@0.144.0': + resolution: {integrity: sha512-ll5IXlH3nzhFtN+sW5fbAvT0mculP/GZCKnhHge/UlyG0cXKvlb3aoG2xWE1qpl5xWv2aQJbVdGhyuiGG/kPsQ==} '@e2b/code-interpreter@2.3.3': resolution: {integrity: sha512-WOpSwc1WpvxyOijf6WMbR76BUuvd2O9ddXgCHHi65lkuy6YgQGq7oyd8PNsT331O9Tqbccjy6uF4xanSdLX1UA==} @@ -2780,8 +2783,8 @@ packages: '@types/node@24.10.9': resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} - '@types/node@25.2.3': - resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==} + '@types/node@25.3.0': + resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} '@types/pg@8.16.0': resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} @@ -2819,8 +2822,8 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} - '@vercel/sandbox@1.5.0': - resolution: {integrity: sha512-+czegl5FtCCNV0EOkJFTf3XX5Pfds/QyysA1HfYMbnePYiypZJW9rWYGDEfYTLC4oyNsp/9d3hmmV8FvYMhLwA==} + '@vercel/sandbox@1.6.0': + resolution: {integrity: sha512-T204S3RZIdo+BCR5gpErTQBYz+ruXjTMRiZyf+HV/UQeKjPKAVKWu27T65kahDvVvJrbadHhxR2Cds28rXXF/Q==} '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} @@ -2954,6 +2957,9 @@ packages: peerDependencies: postcss: ^8.1.0 + aws4fetch@1.0.20: + resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} + axios@1.13.5: resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} @@ -3190,8 +3196,8 @@ packages: compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - computesdk@2.2.0: - resolution: {integrity: sha512-gAAL8vMLkYUFH138OwbebTG9AYMh4RudhRvYboJvRdc9NQAafVHfvZtPwg4YVKPB3VpsfK5m9pkgv60Xr2cE1g==} + computesdk@2.2.1: + resolution: {integrity: sha512-u4xAtvuTNTquaN57hJq030aeGJtmxQ6Pzd/8kXB7uaCgwyyePK4W0hPDeDOFzyNd9QgoDDUD9r1j1sdO+UqVXg==} confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -3744,6 +3750,10 @@ packages: resolution: {integrity: sha512-eVkB/CYCCei7K2WElZW9yYQFWssG0DhaDhVvr7wy5jJ22K+ck8fWW0EsLpB0sITUTvPnc97+rrbQqIr5iqiy9Q==} engines: {node: '>=16.9.0'} + hono@4.12.2: + resolution: {integrity: sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==} + engines: {node: '>=16.9.0'} + html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} @@ -4137,8 +4147,8 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - miniflare@4.20260210.0: - resolution: {integrity: sha512-HXR6m53IOqEzq52DuGF1x7I1K6lSIqzhbCbQXv/cTmPnPJmNkr7EBtLDm4nfSkOvlDtnwDCLUjWII5fyGJI5Tw==} + miniflare@4.20260302.0: + resolution: {integrity: sha512-joGFywlo7HdfHXXGOkc6tDCVkwjEncM0mwEsMOLWcl+vDVJPj9HRV7JtEa0+lCpNOLdYw7mZNHYe12xz9KtJOw==} engines: {node: '>=18.0.0'} hasBin: true @@ -4518,6 +4528,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prismjs@1.30.0: @@ -4962,6 +4973,7 @@ packages: tar@7.5.7: resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} engines: {node: '>=18'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} @@ -5139,6 +5151,9 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@5.29.0: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} @@ -5426,17 +5441,17 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} - workerd@1.20260210.0: - resolution: {integrity: sha512-Sb0WXhrvf+XHQigP2trAxQnXo7wxZFC4PWnn6I7LhFxiTvzxvOAqMEiLkIz58wggRCb54T/KAA8hdjkTniR5FA==} + workerd@1.20260302.0: + resolution: {integrity: sha512-FhNdC8cenMDllI6bTktFgxP5Bn5ZEnGtofgKipY6pW9jtq708D1DeGI6vGad78KQLBGaDwFy1eThjCoLYgFfog==} engines: {node: '>=16'} hasBin: true - wrangler@4.64.0: - resolution: {integrity: sha512-0PBiVEbshQT4Av/KLHbOAks4ioIKp/eAO7Xr2BgAX5v7cFYYgeOvudBrbtZa/hDDIA6858QuJnTQ8mI+cm8Vqw==} + wrangler@4.67.1: + resolution: {integrity: sha512-5GXz8cQNN/2KCMQ/22oe9Vf9xwVNillOowhKq6W2+ZuveU4/IrTm61K4iIUOsBYmrhYGopSfk0GmyCTuNh2rsA==} engines: {node: '>=20.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260210.0 + '@cloudflare/workers-types': ^4.20260302.0 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -5607,15 +5622,15 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/react@4.4.2(@types/node@25.2.3)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)': + '@astrojs/react@4.4.2(@types/node@25.3.0)(@types/react-dom@18.3.7(@types/react@18.3.27))(@types/react@18.3.27)(jiti@1.21.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.8.2)': dependencies: '@types/react': 18.3.27 '@types/react-dom': 18.3.7(@types/react@18.3.27) - '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) + '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) ultrahtml: 1.6.0 - vite: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -5636,9 +5651,9 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.25.76 - '@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))': + '@astrojs/tailwind@6.0.2(astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))': dependencies: - astro: 5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + astro: 5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) autoprefixer: 10.4.23(postcss@8.5.6) postcss: 8.5.6 postcss-load-config: 4.0.2(postcss@8.5.6) @@ -6344,32 +6359,33 @@ snapshots: '@cloudflare/kv-asset-handler@0.4.2': {} - '@cloudflare/sandbox@0.7.2': + '@cloudflare/sandbox@0.7.5': dependencies: '@cloudflare/containers': 0.0.30 + aws4fetch: 1.0.20 - '@cloudflare/unenv-preset@2.12.1(unenv@2.0.0-rc.24)(workerd@1.20260210.0)': + '@cloudflare/unenv-preset@2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260302.0)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260210.0 + workerd: 1.20260302.0 - '@cloudflare/workerd-darwin-64@1.20260210.0': + '@cloudflare/workerd-darwin-64@1.20260302.0': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260210.0': + '@cloudflare/workerd-darwin-arm64@1.20260302.0': optional: true - '@cloudflare/workerd-linux-64@1.20260210.0': + '@cloudflare/workerd-linux-64@1.20260302.0': optional: true - '@cloudflare/workerd-linux-arm64@1.20260210.0': + '@cloudflare/workerd-linux-arm64@1.20260302.0': optional: true - '@cloudflare/workerd-windows-64@1.20260210.0': + '@cloudflare/workerd-windows-64@1.20260302.0': optional: true - '@cloudflare/workers-types@4.20260210.0': {} + '@cloudflare/workers-types@4.20260302.0': {} '@computesdk/cmd@0.4.1': {} @@ -6386,18 +6402,18 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@daytonaio/api-client@0.141.0': + '@daytonaio/api-client@0.144.0': dependencies: axios: 1.13.5 transitivePeerDependencies: - debug - '@daytonaio/sdk@0.141.0(ws@8.19.0)': + '@daytonaio/sdk@0.144.0(ws@8.19.0)': dependencies: '@aws-sdk/client-s3': 3.975.0 '@aws-sdk/lib-storage': 3.975.0(@aws-sdk/client-s3@3.975.0) - '@daytonaio/api-client': 0.141.0 - '@daytonaio/toolbox-api-client': 0.141.0 + '@daytonaio/api-client': 0.144.0 + '@daytonaio/toolbox-api-client': 0.144.0 '@iarna/toml': 2.2.5 '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-http': 0.207.0(@opentelemetry/api@1.9.0) @@ -6423,7 +6439,7 @@ snapshots: - supports-color - ws - '@daytonaio/toolbox-api-client@0.141.0': + '@daytonaio/toolbox-api-client@0.144.0': dependencies: axios: 1.13.5 transitivePeerDependencies: @@ -7766,7 +7782,7 @@ snapshots: '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 25.2.3 + '@types/node': 24.10.9 '@types/chai@5.2.3': dependencies: @@ -7781,13 +7797,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 25.2.3 + '@types/node': 25.3.0 '@types/ssh2': 1.15.5 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 25.2.3 + '@types/node': 25.3.0 '@types/ssh2': 1.15.5 '@types/estree@1.0.8': {} @@ -7822,13 +7838,13 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/node@25.2.3': + '@types/node@25.3.0': dependencies: - undici-types: 7.16.0 + undici-types: 7.18.2 '@types/pg@8.16.0': dependencies: - '@types/node': 25.2.3 + '@types/node': 24.10.9 pg-protocol: 1.11.0 pg-types: 2.2.0 @@ -7847,7 +7863,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 25.2.3 + '@types/node': 24.10.9 '@types/semver@7.7.1': {} @@ -7861,7 +7877,7 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vercel/sandbox@1.5.0': + '@vercel/sandbox@1.6.0': dependencies: '@vercel/oidc': 3.1.0 async-retry: 1.3.3 @@ -7876,7 +7892,7 @@ snapshots: - bare-abort-controller - react-native-b4a - '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.2.3))': + '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@25.3.0))': dependencies: '@babel/core': 7.28.6 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) @@ -7884,11 +7900,11 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.21(@types/node@25.2.3) + vite: 5.4.21(@types/node@25.3.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.6 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) @@ -7896,7 +7912,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -7916,13 +7932,13 @@ snapshots: optionalDependencies: vite: 5.4.21(@types/node@22.19.7) - '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.2.3))': + '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.3.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 5.4.21(@types/node@25.2.3) + vite: 5.4.21(@types/node@25.3.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -8009,7 +8025,7 @@ snapshots: assertion-error@2.0.1: {} - astro@5.16.15(@types/node@25.2.3)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): + astro@5.16.15(@types/node@25.3.0)(aws4fetch@1.0.20)(jiti@1.21.7)(rollup@4.56.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: '@astrojs/compiler': 2.13.0 '@astrojs/internal-helpers': 0.7.5 @@ -8064,10 +8080,10 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.3 unist-util-visit: 5.1.0 - unstorage: 1.17.4 + unstorage: 1.17.4(aws4fetch@1.0.20) vfile: 6.0.3 - vite: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.1(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) + vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vitefu: 1.1.1(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -8128,6 +8144,8 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 + aws4fetch@1.0.20: {} + axios@1.13.5: dependencies: follow-redirects: 1.15.11 @@ -8360,7 +8378,7 @@ snapshots: compare-versions@6.1.1: {} - computesdk@2.2.0: + computesdk@2.2.1: dependencies: '@computesdk/cmd': 0.4.1 @@ -9065,6 +9083,8 @@ snapshots: hono@4.11.8: {} + hono@4.12.2: {} + html-escaper@3.0.3: {} html-void-elements@3.0.0: {} @@ -9582,12 +9602,12 @@ snapshots: mimic-response@3.1.0: {} - miniflare@4.20260210.0: + miniflare@4.20260302.0: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.18.2 - workerd: 1.20260210.0 + workerd: 1.20260302.0 ws: 8.18.0 youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -9969,7 +9989,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 25.2.3 + '@types/node': 25.3.0 long: 5.3.2 proxy-addr@2.0.7: @@ -10735,6 +10755,8 @@ snapshots: undici-types@7.16.0: {} + undici-types@7.18.2: {} + undici@5.29.0: dependencies: '@fastify/busboy': 2.1.1 @@ -10807,7 +10829,7 @@ snapshots: unpipe@1.0.0: {} - unstorage@1.17.4: + unstorage@1.17.4(aws4fetch@1.0.20): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -10817,6 +10839,8 @@ snapshots: node-fetch-native: 1.6.7 ofetch: 1.5.1 ufo: 1.6.3 + optionalDependencies: + aws4fetch: 1.0.20 update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: @@ -10870,13 +10894,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -10900,13 +10924,13 @@ snapshots: '@types/node': 22.19.7 fsevents: 2.3.3 - vite@5.4.21(@types/node@25.2.3): + vite@5.4.21(@types/node@25.3.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 rollup: 4.56.0 optionalDependencies: - '@types/node': 25.2.3 + '@types/node': 25.3.0 fsevents: 2.3.3 vite@6.4.1(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): @@ -10924,7 +10948,7 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): + vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -10933,15 +10957,15 @@ snapshots: rollup: 4.56.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.2.3 + '@types/node': 25.3.0 fsevents: 2.3.3 jiti: 1.21.7 tsx: 4.21.0 yaml: 2.8.2 - vitefu@1.1.1(vite@6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)): + vitefu@1.1.1(vite@6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)): optionalDependencies: - vite: 6.4.1(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): dependencies: @@ -10985,11 +11009,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.2.3)) + '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.3.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11007,12 +11031,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 5.4.21(@types/node@25.2.3) - vite-node: 3.2.4(@types/node@25.2.3)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) + vite: 5.4.21(@types/node@25.3.0) + vite-node: 3.2.4(@types/node@25.3.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 25.2.3 + '@types/node': 25.3.0 transitivePeerDependencies: - jiti - less @@ -11048,26 +11072,26 @@ snapshots: dependencies: string-width: 7.2.0 - workerd@1.20260210.0: + workerd@1.20260302.0: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260210.0 - '@cloudflare/workerd-darwin-arm64': 1.20260210.0 - '@cloudflare/workerd-linux-64': 1.20260210.0 - '@cloudflare/workerd-linux-arm64': 1.20260210.0 - '@cloudflare/workerd-windows-64': 1.20260210.0 + '@cloudflare/workerd-darwin-64': 1.20260302.0 + '@cloudflare/workerd-darwin-arm64': 1.20260302.0 + '@cloudflare/workerd-linux-64': 1.20260302.0 + '@cloudflare/workerd-linux-arm64': 1.20260302.0 + '@cloudflare/workerd-windows-64': 1.20260302.0 - wrangler@4.64.0(@cloudflare/workers-types@4.20260210.0): + wrangler@4.67.1(@cloudflare/workers-types@4.20260302.0): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.12.1(unenv@2.0.0-rc.24)(workerd@1.20260210.0) + '@cloudflare/unenv-preset': 2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260302.0) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260210.0 + miniflare: 4.20260302.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260210.0 + workerd: 1.20260302.0 optionalDependencies: - '@cloudflare/workers-types': 4.20260210.0 + '@cloudflare/workers-types': 4.20260302.0 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil diff --git a/sdks/typescript/src/client.ts b/sdks/typescript/src/client.ts index 4e81909..77d3622 100644 --- a/sdks/typescript/src/client.ts +++ b/sdks/typescript/src/client.ts @@ -48,22 +48,36 @@ import { const API_PREFIX = "/v1"; const FS_PATH = `${API_PREFIX}/fs`; +const DEFAULT_BASE_URL = "http://sandbox-agent"; const DEFAULT_REPLAY_MAX_EVENTS = 50; const DEFAULT_REPLAY_MAX_CHARS = 12_000; const EVENT_INDEX_SCAN_EVENTS_LIMIT = 500; -export interface SandboxAgentConnectOptions { - baseUrl: string; +interface SandboxAgentConnectCommonOptions { + headers?: HeadersInit; + persist?: SessionPersistDriver; + replayMaxEvents?: number; + replayMaxChars?: number; token?: string; +} + +export type SandboxAgentConnectOptions = + | (SandboxAgentConnectCommonOptions & { + baseUrl: string; + fetch?: typeof fetch; + }) + | (SandboxAgentConnectCommonOptions & { + fetch: typeof fetch; + baseUrl?: string; + }); + +export interface SandboxAgentStartOptions { fetch?: typeof fetch; headers?: HeadersInit; persist?: SessionPersistDriver; replayMaxEvents?: number; replayMaxChars?: number; -} - -export interface SandboxAgentStartOptions extends Omit { spawn?: SandboxAgentSpawnOptions | boolean; } @@ -443,18 +457,22 @@ export class SandboxAgent { private readonly seedSessionEventIndexBySession = new Map>(); constructor(options: SandboxAgentConnectOptions) { - this.baseUrl = options.baseUrl.replace(/\/$/, ""); + const baseUrl = options.baseUrl?.trim(); + if (!baseUrl && !options.fetch) { + throw new Error("baseUrl is required unless fetch is provided."); + } + this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, ""); this.token = options.token; - this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis); + const resolvedFetch = options.fetch ?? globalThis.fetch?.bind(globalThis); + if (!resolvedFetch) { + throw new Error("Fetch API is not available; provide a fetch implementation."); + } + this.fetcher = resolvedFetch; this.defaultHeaders = options.headers; this.persist = options.persist ?? new InMemorySessionPersistDriver(); this.replayMaxEvents = normalizePositiveInt(options.replayMaxEvents, DEFAULT_REPLAY_MAX_EVENTS); this.replayMaxChars = normalizePositiveInt(options.replayMaxChars, DEFAULT_REPLAY_MAX_CHARS); - - if (!this.fetcher) { - throw new Error("Fetch API is not available; provide a fetch implementation."); - } } static async connect(options: SandboxAgentConnectOptions): Promise { @@ -468,7 +486,8 @@ export class SandboxAgent { } const { spawnSandboxAgent } = await import("./spawn.js"); - const handle = await spawnSandboxAgent(spawnOptions, options.fetch ?? globalThis.fetch); + const resolvedFetch = options.fetch ?? globalThis.fetch?.bind(globalThis); + const handle = await spawnSandboxAgent(spawnOptions, resolvedFetch); const client = new SandboxAgent({ baseUrl: handle.baseUrl, diff --git a/sdks/typescript/tests/integration.test.ts b/sdks/typescript/tests/integration.test.ts index 71f9189..84b0d1a 100644 --- a/sdks/typescript/tests/integration.test.ts +++ b/sdks/typescript/tests/integration.test.ts @@ -137,6 +137,45 @@ describe("Integration: TypeScript SDK flat session API", () => { await sdk.dispose(); }); + it("uses custom fetch for both HTTP helpers and ACP session traffic", async () => { + const defaultFetch = globalThis.fetch; + if (!defaultFetch) { + throw new Error("Global fetch is not available in this runtime."); + } + + const seenPaths: string[] = []; + const customFetch: typeof fetch = async (input, init) => { + const outgoing = new Request(input, init); + const parsed = new URL(outgoing.url); + seenPaths.push(parsed.pathname); + + const forwardedUrl = new URL(`${parsed.pathname}${parsed.search}`, baseUrl); + const forwarded = new Request(forwardedUrl.toString(), outgoing); + return defaultFetch(forwarded); + }; + + const sdk = await SandboxAgent.connect({ + token, + fetch: customFetch, + }); + + await sdk.getHealth(); + const session = await sdk.createSession({ agent: "mock" }); + const prompt = await session.prompt([{ type: "text", text: "custom fetch integration test" }]); + expect(prompt.stopReason).toBe("end_turn"); + + expect(seenPaths).toContain("/v1/health"); + expect(seenPaths.some((path) => path.startsWith("/v1/acp/"))).toBe(true); + + await sdk.dispose(); + }); + + it("requires baseUrl when fetch is not provided", async () => { + await expect(SandboxAgent.connect({ token } as any)).rejects.toThrow( + "baseUrl is required unless fetch is provided.", + ); + }); + it("restores a session on stale connection by recreating and replaying history on first prompt", async () => { const persist = new InMemorySessionPersistDriver({ maxEventsPerSession: 200,