Run Vercel AI SDK agents in a Promigence sandbox
Pass a Promigence sandbox to the Vercel AI SDK as experimental_sandbox, or to bash-tool, so an agent's commands and files live there.
Also as markdown: /docs/guides/vercel-ai-sdk.md
The Vercel AI SDK lets a tool reach a sandbox through experimental_sandbox, and bash-tool takes a sandbox of its own. The promigence-ai-sdk-sandbox package wraps a Promigence sandbox in one object that both accept. Your tools run commands, read files and write files in the sandbox, and nothing the model asks for touches your server.
What you need#
- A Promigence API key in
PROMIGENCE_API_KEY. - Node.js 20 or newer.
ai7.0.106 and, if you use it,bash-tool1.3.19. Those are the versions the package was tested against, on TypeScript 5.9.
Install#
npm install promigence-ai-sdk-sandbox @promigence/sdk ai zod bash-tool
export PROMIGENCE_API_KEY=prom_api_...
Language: bashA minimal example#
Create the sandbox with @promigence/sdk, then wrap it:
import { generateText, tool } from "ai";
import { Sandbox } from "@promigence/sdk";
import { promigenceSandbox } from "promigence-ai-sdk-sandbox";
import { z } from "zod";
const sandbox = await Sandbox.create({ timeoutMs: 600_000, spendCap: 1 });
const session = promigenceSandbox(sandbox, { workingDirectory: "/workspace" });
try {
await generateText({
model: "anthropic/claude-sonnet-4-5",
prompt: "run the tests and fix what fails",
experimental_sandbox: session,
tools: {
shell: tool({
description: "run a shell command",
inputSchema: z.object({ command: z.string() }),
execute: async ({ command }, { experimental_sandbox }) => {
const { exitCode, stdout, stderr } = await experimental_sandbox!.run({ command });
return `${exitCode}\n${stdout}${stderr}`;
},
}),
},
});
} finally {
await session.stop();
}
Language: tsWith no snapshot named, Sandbox.create starts from the built-in base. To start from your own environment, pass a snapshot alias (Sandbox.create("my-env", { ... })) or an image digest with { image: "registry/name@sha256:...", allowBuild: true }. A snapshot is a saved, verified environment, and each sandbox starts as a fresh copy of it. The first use of a new image builds that snapshot, and allowBuild: true is how you agree to that build. Alternatively, build it once ahead of time with promigence snapshot create --image <ref> --alias my-env. Either way, the build is billed per second while it runs, whether it verifies or not, up to $0.17 at the default small size (quickstart), and sandboxes started from the snapshot pay no further build charge.
The same session works as bash-tool's sandbox:
import { createBashTool } from "bash-tool";
const { tools } = await createBashTool({ sandbox: session, destination: "/workspace" });
Language: tspromigenceSandbox accepts description (what the agent is told about its environment), workingDirectory (default /workspace, created before the first command that uses it), env, timeoutMs, maxOutputBytes and user. Beyond run, the session has spawn for a background command with streamed stdout and stderr, and read and write methods for text and binary files. Relative paths resolve under workingDirectory.
One sandbox per conversation#
The package does not create or end sandboxes beyond stop(). It wraps the handle you give it. A sensible default is one sandbox per agent run or per user conversation, created at the start and stopped at the end, so no two runs share files.
Setting a spend cap#
Pass spendCap (US dollars) to Sandbox.create, or set PROMIGENCE_SPEND_CAP. Without either, your organization's default applies. It is the most the sandbox may spend. timeoutMs on Sandbox.create bounds how long the sandbox lives.
Cleaning up#
await session.stop() ends the sandbox. Put it in finally, as above. promigence sandbox list shows anything left running.
A command that exits non-zero is the agent's result and comes back in exitCode. A failure of the sandbox itself is thrown as PromigenceEnvironmentError with a failureClass, because an exit code of 1 would wrongly say the agent's command failed. failureClassOf(error) classifies any error the Promigence SDK throws.
What is not supported yet#
- Sandbox lifecycle beyond
stop(). Create the sandbox with@promigence/sdk; copies and parking stay there too. SandboxProcess.pid. The command id is onprocess.execIdinstead.- Byte-by-byte output.
spawn's streams deliver chunks as they are read. - Per-file errors from
writeFiles. It stops at the first failure. - Directory upload, port previews and interactive terminals through this wrapper. Use the
@promigence/sdkhandle for those.
Next#
Read the agent briefing for what a sandbox gives an agent, or start with the quickstart.