|
22 | 22 | | --- | --- | --- | --- | |
23 | 23 | | Local process | `@tanstack/ai-sandbox-local-process` | none (host) | The fast, no-Docker dev loop. Trusted/dev use only. | |
24 | 24 | | Docker | `@tanstack/ai-sandbox-docker` | container | Real isolation; commit-based snapshots, fork, resume-by-id. | |
25 | | -| Daytona | `@tanstack/ai-sandbox-daytona` | cloud sandbox | Managed [Daytona](https://www.daytona.io/) sandboxes; port preview links, resume-by-id. Needs `DAYTONA_API_KEY`. | |
| 25 | +| Daytona | `@tanstack/ai-sandbox-daytona` | cloud sandbox | Managed [Daytona](https://www.daytona.io/) sandboxes; snapshots after setup, port preview links, resume-by-id. Needs `DAYTONA_API_KEY`. | |
26 | 26 | | Vercel | `@tanstack/ai-sandbox-vercel` | microVM | Managed [Vercel Sandbox](https://vercel.com/docs/sandbox) microVMs; exposed-port domains, resume-by-id (persistent). Needs `VERCEL_TOKEN` + team/project. | |
27 | 27 | | Sprites | `@tanstack/ai-sandbox-sprites` | stateful sandbox | Managed [Sprites](https://sprites.dev) (Fly.io) sandboxes; durable filesystem, in-place checkpoints, single proxied public-URL port, resume-by-id. Needs `SPRITES_API_KEY`. | |
28 | 28 |
|
@@ -139,18 +139,78 @@ const isolated = dockerSandbox({ image: 'node:22' }) |
139 | 139 | ```ts |
140 | 140 | import { daytonaSandbox } from '@tanstack/ai-sandbox-daytona' |
141 | 141 |
|
142 | | -const daytona = daytonaSandbox({ apiKey: process.env.DAYTONA_API_KEY }) |
| 142 | +const daytona = daytonaSandbox({ |
| 143 | + apiKey: process.env.DAYTONA_API_KEY, |
| 144 | + snapshot: 'daytona-medium', |
| 145 | + autoStopInterval: 0, |
| 146 | +}) |
143 | 147 | ``` |
144 | 148 |
|
145 | 149 | - **Isolation:** a managed cloud sandbox on a remote VM you do not run yourself. |
146 | | -- **Auth / env:** needs `DAYTONA_API_KEY`. Harness credentials are injected as |
147 | | - workspace secrets; there is no host login to fall back on. |
148 | | -- **Snapshot / resume:** no snapshots; resume-by-id reconnects to a still-running |
149 | | - sandbox (not a restored point-in-time snapshot), plus port preview links for |
150 | | - live previews. |
151 | | -- **Bridge:** the sandbox is remote, so a [bridged tool](./tools) call can't reach |
152 | | - your laptop's `localhost`. In local dev, tunnel the bridge (see [tools](./tools)); |
153 | | - a deployed orchestrator is reachable out of the box. |
| 150 | +- **Auth / env:** needs `DAYTONA_API_KEY`. Put harness credentials in |
| 151 | + [workspace secrets](./provisioning). They are applied to the live sandbox |
| 152 | + at create, resume, and restore. They are not stored on the Daytona create |
| 153 | + record, and they are not written into command history. |
| 154 | +- **Snapshot / resume:** point-in-time snapshots after setup (default when |
| 155 | + `lifecycle.snapshot` is `'after-setup'`). Pass `snapshot` on |
| 156 | + `daytonaSandbox()` to pick the Daytona image (for example |
| 157 | + `'daytona-medium'`). Resume starts a `stopped` or `archived` sandbox, then |
| 158 | + returns the handle. |
| 159 | +- **Idle stop:** Daytona stops an idle sandbox after 15 minutes by default. |
| 160 | + Set `autoStopInterval` in minutes to change that. Pass `0` to turn auto-stop |
| 161 | + off. Set `ephemeral: true` to delete the sandbox when it stops. |
| 162 | +- **Network:** `policy.capabilities.network: 'deny'` blocks all outbound |
| 163 | + network on create. |
| 164 | +- **Working directory:** the portable root `/workspace` maps to |
| 165 | + `/home/daytona/workspace` by default. Override with `workdir` on |
| 166 | + `daytonaSandbox()` if you need another path. |
| 167 | +- **Stdin:** spawned processes accept host stdin (`writableStdin: true`). |
| 168 | +- **Privileges:** the Daytona user is not root. Package installs in `setup` |
| 169 | + must use `sudo -n` (for example `sudo -n apt-get install …`). Do not put |
| 170 | + `sudo *` in a [policy](./policy) deny list for this provider. |
| 171 | +- **Bridge:** the sandbox is remote, so a [bridged tool](./tools) call cannot |
| 172 | + reach your laptop's `localhost`. In local dev, tunnel the bridge (see |
| 173 | + [tools](./tools)). A deployed orchestrator is reachable without a tunnel. |
| 174 | + |
| 175 | +Default headless path on Daytona: |
| 176 | + |
| 177 | +```ts |
| 178 | +import { chat } from '@tanstack/ai' |
| 179 | +import { grokBuildText } from '@tanstack/ai-grok-build' |
| 180 | +import { |
| 181 | + defineSandbox, |
| 182 | + defineSandboxPolicy, |
| 183 | + defineWorkspace, |
| 184 | + gitSkill, |
| 185 | + githubRepo, |
| 186 | + withSandbox, |
| 187 | +} from '@tanstack/ai-sandbox' |
| 188 | +import { daytonaSandbox } from '@tanstack/ai-sandbox-daytona' |
| 189 | + |
| 190 | +const sandbox = defineSandbox({ |
| 191 | + id: 'daytona-agent', |
| 192 | + provider: daytonaSandbox({ |
| 193 | + apiKey: process.env.DAYTONA_API_KEY, |
| 194 | + snapshot: 'daytona-medium', |
| 195 | + }), |
| 196 | + workspace: defineWorkspace({ |
| 197 | + source: githubRepo({ repo: 'owner/app' }), |
| 198 | + skills: [gitSkill({ repo: 'owner/skills-pack' })], |
| 199 | + }), |
| 200 | + policy: defineSandboxPolicy({ |
| 201 | + default: 'allow', |
| 202 | + }), |
| 203 | +}) |
| 204 | + |
| 205 | +const stream = chat({ |
| 206 | + adapter: grokBuildText('grok-build'), |
| 207 | + messages: [{ role: 'user', content: 'List the project files.' }], |
| 208 | + middleware: [withSandbox(sandbox)], |
| 209 | +}) |
| 210 | +``` |
| 211 | + |
| 212 | +Headless Grok Build and Codex stay on auto-approve with `default: 'allow'`. |
| 213 | +Isolation is the Daytona VM. Use Claude Code when you need command-level deny. |
154 | 214 |
|
155 | 215 | ## Vercel |
156 | 216 |
|
@@ -211,7 +271,7 @@ Providers declare what they support via `capabilities()`. The flags are: |
211 | 271 | | `env` | Inject environment variables. | |
212 | 272 | | `ports` | Expose/forward ports (preview URLs). | |
213 | 273 | | `backgroundProcesses` | Keep long-running processes alive between calls. | |
214 | | -| `writableStdin` | A spawned process exposes a writable host→process stdin. `true` for local-process and Docker; `false` on remote/edge providers (Daytona, Vercel, Cloudflare), where stdin-fed harnesses deliver the prompt via a file + shell redirection instead. | |
| 274 | +| `writableStdin` | A spawned process exposes a writable host→process stdin. `true` for local-process, Docker, and Daytona. `false` on Vercel and Cloudflare, where stdin-fed harnesses deliver the prompt via a file + shell redirection. | |
215 | 275 | | `killableProcesses` | A spawned process can be forcibly stopped via `SpawnHandle.kill()` **and** aborted mid-flight via the `signal` passed to `spawn`. | |
216 | 276 | | `snapshots` | Capture and restore point-in-time snapshots. | |
217 | 277 | | `networkPolicy` | Enforce network allow/deny rules. | |
|
0 commit comments