Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Sandbox Agent solves three problems:

- **Universal Agent API**: Single interface to control Claude Code, Codex, OpenCode, Cursor, Amp, and Pi with full feature coverage
- **Universal Session Schema**: Standardized schema that normalizes all agent event formats for storage and replay
- **Runs Inside Any Sandbox**: Lightweight static Rust binary. One curl command to install inside E2B, Daytona, Vercel Sandboxes, or Docker
- **Runs Inside Any Sandbox**: Lightweight static Rust binary. One curl command to install inside Agent Computer, E2B, Daytona, Vercel Sandboxes, or Docker
- **Server or SDK Mode**: Run as an HTTP server or embed with the TypeScript SDK
- **OpenAPI Spec**: [Well documented](https://sandboxagent.dev/docs/api-reference) and easy to integrate from any language
- **OpenCode SDK & UI Support** *(Experimental)*: [Connect OpenCode CLI, SDK, or web UI](https://sandboxagent.dev/docs/opencode-compatibility) to control agents through familiar OpenCode tooling
Expand Down
60 changes: 60 additions & 0 deletions docs/deploy/agentcomputer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: "Agent Computer"
description: "Run Sandbox Agent on Agent Computer managed workers."
---

## Prerequisites

- `COMPUTER_API_KEY` or `AGENTCOMPUTER_API_KEY`
- An Agent Computer managed-worker image, or default machine source, that already has your coding agent authenticated

The platform image already includes Sandbox Agent, Claude Code, and Codex. Agent credentials still live inside the machine, so make sure your image or workspace bootstrap has already handled agent login.

## TypeScript example

```bash
npm install sandbox-agent@0.4.x
```

```typescript
import { SandboxAgent } from "sandbox-agent";
import { agentcomputer } from "sandbox-agent/agentcomputer";

const sdk = await SandboxAgent.start({
sandbox: agentcomputer(),
});

try {
const health = await sdk.getHealth();
console.log(health.status);
console.log(sdk.inspectorUrl);

const agents = await sdk.listAgents();
console.log(agents.agents.map((agent) => agent.id));
} finally {
await sdk.destroySandbox();
}
```

The `agentcomputer` provider creates a managed-worker computer, waits until browser access is ready, and routes SDK requests through the machine's authenticated web host. `sdk.inspectorUrl` is already a browser-ready URL, so it opens the built-in Inspector directly.

By default, the provider creates:

- `runtimeFamily: "managed-worker"`
- `usePlatformDefault: true`

Pass `create` when you want to override the handle, workspace name, or other machine-create settings:

```typescript
const sdk = await SandboxAgent.start({
sandbox: agentcomputer({
create: {
handle: "sandbox-agent-demo",
workspaceName: "sandbox-agent-demo",
usePlatformDefault: false,
},
}),
});
```

Set `apiUrl` when you are targeting a self-hosted or local Agent Computer API instead of `https://api.computer.agentcomputer.ai`.
9 changes: 5 additions & 4 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"deploy/local",
"deploy/e2b",
"deploy/daytona",
"deploy/agentcomputer",
"deploy/vercel",
"deploy/cloudflare",
"deploy/docker",
Expand Down Expand Up @@ -122,9 +123,9 @@
]
},
"__removed": [
{
"group": "Orchestration",
"pages": ["orchestration-architecture", "session-persistence", "observability", "multiplayer", "security"]
}
{
"group": "Orchestration",
"pages": ["orchestration-architecture", "session-persistence", "observability", "multiplayer", "security"]
}
]
}
3 changes: 3 additions & 0 deletions docs/sdk-overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ await sdk.destroySandbox(); // provider-defined cleanup + disposes client
| `sandbox-agent/docker` | Docker container |
| `sandbox-agent/e2b` | E2B sandbox |
| `sandbox-agent/daytona` | Daytona workspace |
| `sandbox-agent/agentcomputer` | Agent Computer managed worker |
| `sandbox-agent/vercel` | Vercel Sandbox |
| `sandbox-agent/cloudflare` | Cloudflare Sandbox |

Expand Down Expand Up @@ -250,6 +251,8 @@ try {

## Inspector URL

When you call `SandboxAgent.start(...)`, prefer `sdk.inspectorUrl`. Some hosted providers return a browser-ready Inspector URL there.

```ts
import { buildInspectorUrl } from "sandbox-agent";

Expand Down
19 changes: 19 additions & 0 deletions examples/agentcomputer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@sandbox-agent/example-agentcomputer",
"private": true,
"type": "module",
"scripts": {
"start": "tsx src/index.ts",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@sandbox-agent/example-shared": "workspace:*",
"sandbox-agent": "workspace:*"
},
"devDependencies": {
"@types/node": "latest",
"tsx": "latest",
"typescript": "latest",
"vitest": "^3.0.0"
}
}
19 changes: 19 additions & 0 deletions examples/agentcomputer/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SandboxAgent } from "sandbox-agent";
import { agentcomputer } from "sandbox-agent/agentcomputer";

const client = await SandboxAgent.start({
sandbox: agentcomputer(),
});

console.log(`UI: ${client.inspectorUrl}`);

const health = await client.getHealth();
console.log(`Health: ${health.status}`);

const agents = await client.listAgents();
console.log("Agents:", agents.agents.map((agent) => agent.id).join(", "));

process.once("SIGINT", async () => {
await client.destroySandbox();
process.exit(0);
});
27 changes: 27 additions & 0 deletions examples/agentcomputer/tests/agentcomputer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, it, expect } from "vitest";
import { SandboxAgent } from "sandbox-agent";
import { agentcomputer } from "sandbox-agent/agentcomputer";

const shouldRun = Boolean(process.env.COMPUTER_API_KEY || process.env.AGENTCOMPUTER_API_KEY);
const timeoutMs = Number.parseInt(process.env.SANDBOX_TEST_TIMEOUT_MS || "", 10) || 300_000;

const testFn = shouldRun ? it : it.skip;

describe("agentcomputer provider", () => {
testFn(
"starts sandbox-agent and responds to /v1/health",
async () => {
const sdk = await SandboxAgent.start({
sandbox: agentcomputer(),
});

try {
const health = await sdk.getHealth();
expect(health.status).toBe("ok");
} finally {
await sdk.destroySandbox();
}
},
timeoutMs,
);
});
17 changes: 17 additions & 0 deletions examples/agentcomputer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM"],
"module": "ESNext",
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/release/update_version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const VERSION_REFERENCE_FILES = [
"docs/deploy/cloudflare.mdx",
"docs/deploy/vercel.mdx",
"docs/deploy/daytona.mdx",
"docs/deploy/agentcomputer.mdx",
"docs/deploy/e2b.mdx",
"docs/deploy/docker.mdx",
"docs/deploy/boxlite.mdx",
Expand Down
4 changes: 4 additions & 0 deletions sdks/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"types": "./dist/providers/daytona.d.ts",
"import": "./dist/providers/daytona.js"
},
"./agentcomputer": {
"types": "./dist/providers/agentcomputer.d.ts",
"import": "./dist/providers/agentcomputer.js"
},
"./docker": {
"types": "./dist/providers/docker.d.ts",
"import": "./dist/providers/docker.js"
Expand Down
5 changes: 4 additions & 1 deletion sdks/typescript/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ export class SandboxAgent {
private sandboxProvider?: SandboxProvider;
private sandboxProviderId?: string;
private sandboxProviderRawId?: string;
private sandboxInspectorUrl?: string;

private readonly persist: SessionPersistDriver;
private readonly replayMaxEvents: number;
Expand Down Expand Up @@ -959,6 +960,7 @@ export class SandboxAgent {
try {
const fetcher = await resolveProviderFetch(provider, rawSandboxId);
const baseUrl = provider.getUrl ? await provider.getUrl(rawSandboxId) : undefined;
const inspectorUrl = provider.getInspectorUrl ? await provider.getInspectorUrl(rawSandboxId, baseUrl) : undefined;
const providerFetch = options.fetch ?? fetcher;
const commonConnectOptions = {
headers: options.headers,
Expand All @@ -984,6 +986,7 @@ export class SandboxAgent {
client.sandboxProvider = provider;
client.sandboxProviderId = prefixedSandboxId;
client.sandboxProviderRawId = rawSandboxId;
client.sandboxInspectorUrl = inspectorUrl;
return client;
} catch (error) {
if (createdSandbox) {
Expand All @@ -1006,7 +1009,7 @@ export class SandboxAgent {
}

get inspectorUrl(): string {
return `${this.baseUrl.replace(/\/+$/, "")}/ui/`;
return this.sandboxInspectorUrl ?? `${this.baseUrl.replace(/\/+$/, "")}/ui/`;
}

async dispose(): Promise<void> {
Expand Down
Loading