chore: simplify cloudflare compatibility (#191)

This commit is contained in:
NathanFlurry 2026-02-23 19:31:53 +00:00
parent 03e06e956d
commit 4201bd204b
No known key found for this signature in database
GPG key ID: 6A5F43A4F3241BCA
10 changed files with 418 additions and 249 deletions

View file

@ -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<Sandbox>;
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<boolean> {
@ -54,64 +59,50 @@ async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
}
}
async function ensureRunning(sandbox: Sandbox, env: Env): Promise<void> {
if (await isServerRunning(sandbox)) return;
async function getReadySandbox(name: string, env: Bindings): Promise<Sandbox> {
const sandbox = getSandbox(env.Sandbox, name);
if (!(await isServerRunning(sandbox))) {
const envVars: Record<string, string> = {};
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));
}
return sandbox;
}
throw new Error("sandbox-agent failed to start");
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/);
if (!match) {
return new Response("Not found", { status: 404 });
}
const [, name, path = "/"] = match;
const sandbox = getSandbox(env.Sandbox, name);
await ensureRunning(sandbox, env);
return sandbox.containerFetch(
new Request(`http://localhost${path}${url.search}`, request),
PORT,
);
},
};
```
## Connect from a client
```typescript
import { SandboxAgent } from "sandbox-agent";
app.post("/sandbox/:name/prompt", async (c) => {
const sandbox = await getReadySandbox(c.req.param("name"), c.env);
const sdk = await SandboxAgent.connect({
baseUrl: "http://localhost:8787/sandbox/my-sandbox",
fetch: (input, init) => sandbox.containerFetch(input as Request | string | URL, init, PORT),
});
const session = await sdk.createSession({ agent: "claude" });
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();
const off = session.onEvent((event) => {
console.log(event.sender, event.payload);
return c.json(response);
});
await session.prompt([{ type: "text", text: "Summarize this repository" }]);
off();
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;
return sandbox.containerFetch(new Request(`http://localhost${path}${query}`, c.req.raw), PORT);
});
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

View file

@ -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

View file

@ -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

View file

@ -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 });

View file

@ -10,6 +10,7 @@
},
"dependencies": {
"@cloudflare/sandbox": "latest",
"hono": "^4.12.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"sandbox-agent": "workspace:*"

View file

@ -1,15 +1,19 @@
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: {
type Bindings = {
Sandbox: DurableObjectNamespace<Sandbox>;
ASSETS: Fetcher;
ANTHROPIC_API_KEY?: string;
OPENAI_API_KEY?: string;
CODEX_API_KEY?: string;
};
};
type AppEnv = { Bindings: Bindings };
const PORT = 8000;
@ -23,54 +27,60 @@ async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
}
}
/** Ensure sandbox-agent is running in the container */
async function ensureRunning(sandbox: Sandbox, env: Env["Bindings"]): Promise<void> {
if (await isServerRunning(sandbox)) return;
// Set environment variables for agents
async function getReadySandbox(name: string, env: Bindings): Promise<Sandbox> {
const sandbox = getSandbox(env.Sandbox, name);
const envVars: Record<string, string> = {};
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
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;
if (await isServerRunning(sandbox)) break;
await new Promise((r) => setTimeout(r, 200));
}
}
export default {
async fetch(request: Request, env: Env["Bindings"]): Promise<Response> {
const url = new URL(request.url);
// 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 }
);
return sandbox;
}
const name = match[1];
const path = match[2] || "/";
const sandbox = getSandbox(env.Sandbox, name);
await ensureRunning(sandbox, env);
// Proxy request to container
return sandbox.containerFetch(
new Request(`http://localhost${path}${url.search}`, request),
PORT
);
async function proxyToSandbox(sandbox: Sandbox, request: Request, path: string): Promise<Response> {
const query = new URL(request.url).search;
return sandbox.containerFetch(new Request(`http://localhost${path}${query}`, request), PORT);
}
// Serve frontend assets
return env.ASSETS.fetch(request);
},
} satisfies ExportedHandler<Env["Bindings"]>;
const app = new Hono<AppEnv>();
app.onError((error) => {
return new Response(String(error), { status: 500 });
});
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" });
}
let payload: PromptTestRequest;
try {
payload = await c.req.json<PromptTestRequest>();
} catch {
throw new HTTPException(400, { message: "Invalid JSON body" });
}
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;

View file

@ -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<PromptTestResponse> {
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();
}
}

310
pnpm-lock.yaml generated
View file

@ -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

View file

@ -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<SandboxAgentConnectOptions, "baseUrl" | "token"> {
spawn?: SandboxAgentSpawnOptions | boolean;
}
@ -443,18 +457,22 @@ export class SandboxAgent {
private readonly seedSessionEventIndexBySession = new Map<string, Promise<void>>();
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<SandboxAgent> {
@ -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,

View file

@ -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,