@biroai/adapter-daytona-sandbox
v2026.513.0
Published
Provider-lifecycle adapter for [Daytona](https://daytona.io) sandboxes. Defined in `ROADMAP-NEXT.md` P2.1; narrowed to provider-lifecycle only in P2.2a.
Readme
@biroai/adapter-daytona-sandbox
Provider-lifecycle adapter for Daytona sandboxes. Defined in ROADMAP-NEXT.md P2.1; narrowed to provider-lifecycle only in P2.2a.
This package speaks the Daytona REST API to provision, fetch, list, and tear down Daytona sandbox VMs. It does not drive what runs inside the sandbox — that belongs to @biroai/agent-runtime.
Why Daytona
- Open-source and MIT-compatible with Biro's licensing stance
- ~90ms cold start (vs. seconds for most VM-based sandboxes)
- First-class workspace and snapshot support aligns with Biro's agent execution model
- Self-hostable: companies can point
apiUrlat their own Daytona cluster
What is in this package
| Path | Contents |
|---|---|
| src/shared/types.ts | SandboxHandle, SandboxStatus, SandboxConfig, CreateSandboxOptions, SandboxConfigSchema, mapDaytonaStateToStatus |
| src/shared/errors.ts | SandboxConfigError, DaytonaApiError, (legacy) NotImplementedError |
| src/server/client.ts | DaytonaSandboxClient — createHandle / getHandle / listHandles / stopHandle against POST / GET / DELETE /sandbox |
| src/server/index.ts | Re-exports for server consumers |
| src/cli/index.ts | Stub CLI descriptor |
| src/ui/index.ts | Stub UI version constant |
What this package is NOT
This package is provider lifecycle only. It provisions and tears down Daytona sandbox VMs. It does not (and will not) expose:
- Command execution — use
@biroai/agent-runtime(behindAgentRuntimeClient) - File operations (upload, download, directory ops) — use
@biroai/agent-runtime - Agent session control (postMessage, streamEvents, permission lifecycle) — use
@biroai/agent-runtime - Any "inside the sandbox" operation — those belong to the runtime layer, which is shaped after the Rivet Sandbox Agent SDK and works across Daytona, E2B, Vercel sandbox, and others
State mapping
Daytona's lifecycle has 16 states (creating, starting, started, stopping, stopped, archiving, archived, deleting, deleted, resizing, restoring, pulling_snapshot, building_snapshot, build_pending, error, build_failed, unknown). They are reduced to four abstract SandboxStatus values:
| SandboxStatus | Daytona states |
|---|---|
| starting | creating, starting, restoring, pulling_snapshot, building_snapshot, build_pending |
| ready | started, running, resizing |
| stopped | stopping, stopped, archiving, archived, deleting, deleted |
| error | error, build_failed, unknown, anything else |
Unknown / future states map to error deliberately — silent coercion to ready would mask real failures.
Usage
import { DaytonaSandboxClient } from "@biroai/adapter-daytona-sandbox/server";
const client = new DaytonaSandboxClient({
config: {
apiKey: process.env.DAYTONA_API_KEY!,
apiUrl: "https://app.daytona.io/api",
defaultImage: "biro-default-image:1",
idleTimeoutSeconds: 900, // 15 minutes
},
// fetchImpl is optional; defaults to globalThis.fetch
});
const handle = await client.createHandle({
companyId: "acme",
agentId: "agent-42",
issueId: "issue-7",
envVars: { LANG: "en_US.UTF-8" },
});
// Hand `handle` to @biroai/agent-runtime for in-sandbox ops.
await client.stopHandle(handle.id);Identifying biro-managed sandboxes
createHandle tags every sandbox with these labels:
| Label | Source |
|---|---|
| biro.companyId | CreateSandboxOptions.companyId |
| biro.agentId | CreateSandboxOptions.agentId |
| biro.issueId | CreateSandboxOptions.issueId (omitted when null) |
listHandles({ companyId, agentId }) filters by these labels via Daytona's labels= query param. The status filter is applied client-side because Daytona's filter language has no state predicate.
