← VercelCONTENT HISTORY

Update to Vercel

Snapshot Sep 30, 2026 · 23:18 UTC · version 0.21.4

Collection source: not recorded for this historical snapshot. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.

WHAT CHANGED · RULE-BASED ANALYSIS

Supporting file metadata differs

Newly listed paths: agents/openai.yaml. This compares saved file lists, not package contents; a different collection source can change the list.

Observed in package metadata. These changes alone do not establish a new customer-facing feature.

Supporting files

Before

[]

After

[{"relative_path":"agents/openai.yaml","size_in_bytes":160}]

Compare saved observations

Download comparison JSON
Full technical diff · 1 changed fields

changed /included_files

BEFORE
[]
AFTER
[
  {
    "relative_path": "agents/openai.yaml",
    "size_in_bytes": 160
  }
]
Full snapshot data
{
  "name": "vercel-sandbox",
  "description": "Vercel Sandbox guidance — ephemeral Firecracker microVMs for running untrusted code safely. Supports AI agents, code generation, and experimentation. Use when executing user-generated or AI-generated code in isolation.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 160
    }
  ],
  "skill_md_contents": "---\nname: vercel-sandbox\ndescription: Vercel Sandbox guidance — ephemeral Firecracker microVMs for running untrusted code safely. Supports AI agents, code generation, and experimentation. Use when executing user-generated or AI-generated code in isolation.\nmetadata:\n  priority: 4\n  docs:\n    - \"https://vercel.com/docs/sandbox\"\n  sitemap: \"https://vercel.com/sitemap/docs.xml\"\n  pathPatterns: []\n  importPatterns:\n    - '@vercel/sandbox'\n  bashPatterns:\n    - '\\bnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/sandbox\\b'\n    - '\\bpnpm\\s+(install|i|add)\\s+[^\\n]*@vercel/sandbox\\b'\n    - '\\bbun\\s+(install|i|add)\\s+[^\\n]*@vercel/sandbox\\b'\n    - '\\byarn\\s+add\\s+[^\\n]*@vercel/sandbox\\b'\n  promptSignals:\n    phrases:\n      - \"@vercel/sandbox\"\n      - \"sandbox\"\n      - \"code sandbox\"\n      - \"vercel sandbox\"\n      - \"isolated environment\"\n      - \"sandboxed execution\"\n    allOf:\n      - [sandbox, code]\n      - [sandbox, execute]\n      - [sandbox, run]\n      - [sandbox, isolated]\n      - [sandbox, safe]\n      - [sandbox, environment]\n      - [isolated, execute]\n      - [isolated, code]\n      - [isolated, environment]\n      - [isolated, run]\n      - [safe, execute]\n      - [safe, code]\n      - [untrusted, code]\n      - [untrusted, execute]\n      - [code, runner]\n      - [code, playground]\n      - [execute, safely]\n      - [run, safely]\n      - [run, isolation]\n      - [execute, isolation]\n      - [ffmpeg, process]\n      - [ffmpeg, convert]\n      - [ffmpeg, compress]\n      - [student, code]\n      - [student, execute]\n      - [student, run]\n    anyOf:\n      - \"sandbox\"\n      - \"isolated\"\n      - \"isolation\"\n      - \"untrusted\"\n      - \"safely\"\n      - \"microvm\"\n      - \"ffmpeg\"\n      - \"playground\"\n    noneOf:\n      - \"iframe sandbox\"\n      - \"sandbox attribute\"\n      - \"codesandbox.io\"\n      - \"stackblitz\"\n    minScore: 4\n---\n\n# Browser Automation with Vercel Sandbox\n\nRun agent-browser + headless Chrome inside ephemeral Vercel Sandbox microVMs. A Linux VM spins up on demand, executes browser commands, and shuts down. Works with any Vercel-deployed framework (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.).\n\n## Dependencies\n\n```bash\npnpm add @vercel/sandbox\n```\n\nThe sandbox VM needs system dependencies for Chromium plus agent-browser itself. Use sandbox snapshots (below) to pre-install everything for sub-second startup.\n\n## Core Pattern\n\n```ts\nimport { Sandbox } from \"@vercel/sandbox\";\n\n// System libraries required by Chromium on the sandbox VM (Amazon Linux / dnf)\nconst CHROMIUM_SYSTEM_DEPS = [\n  \"nss\", \"nspr\", \"libxkbcommon\", \"atk\", \"at-spi2-atk\", \"at-spi2-core\",\n  \"libXcomposite\", \"libXdamage\", \"libXrandr\", \"libXfixes\", \"libXcursor\",\n  \"libXi\", \"libXtst\", \"libXScrnSaver\", \"libXext\", \"mesa-libgbm\", \"libdrm\",\n  \"mesa-libGL\", \"mesa-libEGL\", \"cups-libs\", \"alsa-lib\", \"pango\", \"cairo\",\n  \"gtk3\", \"dbus-libs\",\n];\n\nfunction getSandboxCredentials() {\n  if (\n    process.env.VERCEL_TOKEN &&\n    process.env.VERCEL_TEAM_ID &&\n    process.env.VERCEL_PROJECT_ID\n  ) {\n    return {\n      token: process.env.VERCEL_TOKEN,\n      teamId: process.env.VERCEL_TEAM_ID,\n      projectId: process.env.VERCEL_PROJECT_ID,\n    };\n  }\n  return {};\n}\n\nasync function withBrowser<T>(\n  fn: (sandbox: InstanceType<typeof Sandbox>) => Promise<T>,\n): Promise<T> {\n  const snapshotId = process.env.AGENT_BROWSER_SNAPSHOT_ID;\n  const credentials = getSandboxCredentials();\n\n  const sandbox = snapshotId\n    ? await Sandbox.create({\n        ...credentials,\n        source: { type: \"snapshot\", snapshotId },\n        timeout: 120_000,\n      })\n    : await Sandbox.create({ ...credentials, runtime: \"node24\", timeout: 120_000 });\n\n  if (!snapshotId) {\n    await sandbox.runCommand(\"sh\", [\n      \"-c\",\n      `sudo dnf clean all 2>&1 && sudo dnf install -y --skip-broken ${CHROMIUM_SYSTEM_DEPS.join(\" \")} 2>&1 && sudo ldconfig 2>&1`,\n    ]);\n    await sandbox.runCommand(\"npm\", [\"install\", \"-g\", \"agent-browser\"]);\n    await sandbox.runCommand(\"npx\", [\"agent-browser\", \"install\"]);\n  }\n\n  try {\n    return await fn(sandbox);\n  } finally {\n    await sandbox.stop();\n  }\n}\n```\n\n## Screenshot\n\nThe `screenshot --json` command saves to a file and returns the path. Read the file back as base64:\n\n```ts\nexport async function screenshotUrl(url: string) {\n  return withBrowser(async (sandbox) => {\n    await sandbox.runCommand(\"agent-browser\", [\"open\", url]);\n\n    const titleResult = await sandbox.runCommand(\"agent-browser\", [\n      \"get\", \"title\", \"--json\",\n    ]);\n    const title = JSON.parse(await titleResult.stdout())?.data?.title || url;\n\n    const ssResult = await sandbox.runCommand(\"agent-browser\", [\n      \"screenshot\", \"--json\",\n    ]);\n    const ssPath = JSON.parse(await ssResult.stdout())?.data?.path;\n    const b64Result = await sandbox.runCommand(\"base64\", [\"-w\", \"0\", ssPath]);\n    const screenshot = (await b64Result.stdout()).trim();\n\n    await sandbox.runCommand(\"agent-browser\", [\"close\"]);\n\n    return { title, screenshot };\n  });\n}\n```\n\n## Accessibility Snapshot\n\n```ts\nexport async function snapshotUrl(url: string) {\n  return withBrowser(async (sandbox) => {\n    await sandbox.runCommand(\"agent-browser\", [\"open\", url]);\n\n    const titleResult = await sandbox.runCommand(\"agent-browser\", [\n      \"get\", \"title\", \"--json\",\n    ]);\n    const title = JSON.parse(await titleResult.stdout())?.data?.title || url;\n\n    const snapResult = await sandbox.runCommand(\"agent-browser\", [\n      \"snapshot\", \"-i\", \"-c\",\n    ]);\n    const snapshot = await snapResult.stdout();\n\n    await sandbox.runCommand(\"agent-browser\", [\"close\"]);\n\n    return { title, snapshot };\n  });\n}\n```\n\n## Multi-Step Workflows\n\nThe sandbox persists between commands, so you can run full automation sequences:\n\n```ts\nexport async function fillAndSubmitForm(url: string, data: Record<string, string>) {\n  return withBrowser(async (sandbox) => {\n    await sandbox.runCommand(\"agent-browser\", [\"open\", url]);\n\n    const snapResult = await sandbox.runCommand(\"agent-browser\", [\n      \"snapshot\", \"-i\",\n    ]);\n    const snapshot = await snapResult.stdout();\n    // Parse snapshot to find element refs...\n\n    for (const [ref, value] of Object.entries(data)) {\n      await sandbox.runCommand(\"agent-browser\", [\"fill\", ref, value]);\n    }\n\n    await sandbox.runCommand(\"agent-browser\", [\"click\", \"@e5\"]);\n    await sandbox.runCommand(\"agent-browser\", [\"wait\", \"--load\", \"networkidle\"]);\n\n    const ssResult = await sandbox.runCommand(\"agent-browser\", [\n      \"screenshot\", \"--json\",\n    ]);\n    const ssPath = JSON.parse(await ssResult.stdout())?.data?.path;\n    const b64Result = await sandbox.runCommand(\"base64\", [\"-w\", \"0\", ssPath]);\n    const screenshot = (await b64Result.stdout()).trim();\n\n    await sandbox.runCommand(\"agent-browser\", [\"close\"]);\n\n    return { screenshot };\n  });\n}\n```\n\n## Sandbox Snapshots (Fast Startup)\n\nA **sandbox snapshot** is a saved VM image of a Vercel Sandbox with system dependencies + agent-browser + Chromium already installed. Think of it like a Docker image -- instead of installing dependencies from scratch every time, the sandbox boots from the pre-built image.\n\nThis is unrelated to agent-browser's *accessibility snapshot* feature (`agent-browser snapshot`), which dumps a page's accessibility tree. A sandbox snapshot is a Vercel infrastructure concept for fast VM startup.\n\nWithout a sandbox snapshot, each run installs system deps + agent-browser + Chromium (~30s). With one, startup is sub-second.\n\n### Creating a sandbox snapshot\n\nThe snapshot must include system dependencies (via `dnf`), agent-browser, and Chromium:\n\n```ts\nimport { Sandbox } from \"@vercel/sandbox\";\n\nconst CHROMIUM_SYSTEM_DEPS = [\n  \"nss\", \"nspr\", \"libxkbcommon\", \"atk\", \"at-spi2-atk\", \"at-spi2-core\",\n  \"libXcomposite\", \"libXdamage\", \"libXrandr\", \"libXfixes\", \"libXcursor\",\n  \"libXi\", \"libXtst\", \"libXScrnSaver\", \"libXext\", \"mesa-libgbm\", \"libdrm\",\n  \"mesa-libGL\", \"mesa-libEGL\", \"cups-libs\", \"alsa-lib\", \"pango\", \"cairo\",\n  \"gtk3\", \"dbus-libs\",\n];\n\nasync function createSnapshot(): Promise<string> {\n  const sandbox = await Sandbox.create({\n    runtime: \"node24\",\n    timeout: 300_000,\n  });\n\n  await sandbox.runCommand(\"sh\", [\n    \"-c\",\n    `sudo dnf clean all 2>&1 && sudo dnf install -y --skip-broken ${CHROMIUM_SYSTEM_DEPS.join(\" \")} 2>&1 && sudo ldconfig 2>&1`,\n  ]);\n  await sandbox.runCommand(\"npm\", [\"install\", \"-g\", \"agent-browser\"]);\n  await sandbox.runCommand(\"npx\", [\"agent-browser\", \"install\"]);\n\n  const snapshot = await sandbox.snapshot();\n  return snapshot.snapshotId;\n}\n```\n\nRun this once, then set the environment variable:\n\n```bash\nAGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx\n```\n\nA helper script is available in the demo app:\n\n```bash\nnpx tsx examples/environments/scripts/create-snapshot.ts\n```\n\nRecommended for any production deployment using the Sandbox pattern.\n\n## Authentication\n\nOn Vercel deployments, the Sandbox SDK authenticates automatically via OIDC. For local development or explicit control, set:\n\n```bash\nVERCEL_TOKEN=<personal-access-token>\nVERCEL_TEAM_ID=<team-id>\nVERCEL_PROJECT_ID=<project-id>\n```\n\nThese are spread into `Sandbox.create()` calls. When absent, the SDK falls back to `VERCEL_OIDC_TOKEN` (automatic on Vercel).\n\n## Scheduled Workflows (Cron)\n\nCombine with Vercel Cron Jobs for recurring browser tasks:\n\n```ts\n// app/api/cron/route.ts  (or equivalent in your framework)\nexport async function GET() {\n  const result = await withBrowser(async (sandbox) => {\n    await sandbox.runCommand(\"agent-browser\", [\"open\", \"https://example.com/pricing\"]);\n    const snap = await sandbox.runCommand(\"agent-browser\", [\"snapshot\", \"-i\", \"-c\"]);\n    await sandbox.runCommand(\"agent-browser\", [\"close\"]);\n    return await snap.stdout();\n  });\n\n  // Process results, send alerts, store data...\n  return Response.json({ ok: true, snapshot: result });\n}\n```\n\n```json\n// vercel.json\n{ \"crons\": [{ \"path\": \"/api/cron\", \"schedule\": \"0 9 * * *\" }] }\n```\n\n## Environment Variables\n\n| Variable | Required | Description |\n|---|---|---|\n| `AGENT_BROWSER_SNAPSHOT_ID` | No (but recommended) | Pre-built sandbox snapshot ID for sub-second startup (see above) |\n| `VERCEL_TOKEN` | No | Vercel personal access token (for local dev; OIDC is automatic on Vercel) |\n| `VERCEL_TEAM_ID` | No | Vercel team ID (for local dev) |\n| `VERCEL_PROJECT_ID` | No | Vercel project ID (for local dev) |\n\n## Framework Examples\n\nThe pattern works identically across frameworks. The only difference is where you put the server-side code:\n\n| Framework | Server code location |\n|---|---|\n| Next.js | Server actions, API routes, route handlers |\n| SvelteKit | `+page.server.ts`, `+server.ts` |\n| Nuxt | `server/api/`, `server/routes/` |\n| Remix | `loader`, `action` functions |\n| Astro | `.astro` frontmatter, API routes |\n\n## Example\n\nSee `examples/environments/` in the agent-browser repo for a working app with the Vercel Sandbox pattern, including a sandbox snapshot creation script, streaming progress UI, and rate limiting.\n"
}

SHA-256: b9f67eca4a3c76121b74b52e0426579884d93e5e30e1b2e5575d5d934199ed73