Loading...
Loading...
Run vercel-plugin eval scenarios in Vercel Sandboxes instead of local WezTerm panels. Provisions ephemeral microVMs with Claude Code + plugin pre-installed, runs benchmark prompts, extracts hook artifacts, and produces coverage reports.
npx skill4agent add vercel-labs/vercel-plugin benchmark-sandbox--dangerously-skip-permissions --debugagent-browservercel deployclaude -p --json-schema --model haikurun-eval.ts# Run default scenarios with full 3-phase pipeline
bun run .claude/skills/benchmark-sandbox/run-eval.ts
# With dynamic scenarios from a JSON file (recommended — see "Dynamic Scenarios" below)
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios-file /tmp/my-scenarios.json
# Keep sandboxes alive overnight with public URLs
bun run .claude/skills/benchmark-sandbox/run-eval.ts --keep-alive --keep-hours 8
# Build-only (skip verification and deploy)
bun run .claude/skills/benchmark-sandbox/run-eval.ts --skip-verify --skip-deploy
# Run specific scenarios by slug
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios splitwise-clone,calendly-clone| Flag | Default | Description |
|---|---|---|
| 5 | Max parallel sandboxes (max 10) |
| 1800000 (30 min) | Per-phase timeout in ms |
| off | Keep sandboxes running after eval |
| 8 | Hours to keep alive (with |
| off | Skip the agent-browser verification phase |
| off | Skip the Vercel deploy phase |
| all | Only run specific scenarios by slug |
| — | Load scenarios from a JSON file instead of built-in defaults |
[
{
"slug": "pet-adoption-board",
"prompt": "Build me a pet adoption listing board where shelters can post animals...",
"expectedSkills": ["ai-sdk", "nextjs", "shadcn", "vercel-functions"],
"userStories": [
"As a visitor, I can see a grid of pet listings with photos and names",
"As a visitor, I can click a pet card to see a detail page",
"As a visitor, I can filter pets by type"
]
}
]slugpromptexpectedSkillsuserStories"Link the project to my vercel-labs team. After building all files, start the dev server on port 3000 with \claude -p --json-schema --model haiku --setting-sources ""{
"completeness": "complete|partial|minimal|empty",
"hasApiRoutes": true,
"hasUIComponents": true,
"hasAIFeature": true,
"devServerRunning": true,
"missingFeatures": ["feature1"],
"summary": "Brief assessment"
}{
"stories": [
{ "index": 1, "status": "pass|fail", "reason": "Evidence from output" }
]
}{
"deployed": true,
"url": "https://xxx.vercel.app",
"buildSucceeded": true,
"errors": [],
"summary": "Brief assessment"
}claude -p --output-format jsonparsed.structured_output| Property | Value |
|---|---|
| Home directory | |
| User | |
| Claude binary | |
| PATH (via sh -c) | Includes |
| Port exposure | |
| Snapshot persistence | Files AND npm globals survive snapshot restore — use |
| SDK version | |
| Team tier | Enterprise (vercel-labs) — no known sandbox time cap |
sandbox.snapshot()npx add-plugin <path> -s project -y --target claude-codenpm install -g--target claude-code~/.claude/sandbox.writeFiles([{ path, content: Buffer }])--dangerously-skip-permissions --debug--debug~/.claude/debug/ANTHROPIC_AUTH_TOKENvck_*~/.local/share/com.vercel.cli/auth.jsonvca_*npx vercel link --scope vercel-labs -ynpx vercel env pullports: [3000]Sandbox.create()sandbox.domain(3000)sandbox.extendTimeout(ms)runCommand&nohupsession-end-cleanup.mjs/tmp/vercel-plugin-*-seen-skills.d/npm install -g agent-browserclaude -p --json-schema --output-format json --model haikupet-adoption-board-202603101853<slug>-<YYYYMMDDHHMM>| benchmark-agents (WezTerm) | benchmark-sandbox | |
|---|---|---|
| Environment | Local macOS terminal panes | Remote Vercel Sandboxes (Amazon Linux) |
| Parallelism | Limited by local resources | Up to 10 (Hobby) or 2,000 (Pro) concurrent |
| Session type | Interactive TTY via | Direct |
| Artifact access | Direct filesystem ( | |
| Port exposure | | Public |
| Verification | Manual browser check | Automated agent-browser in Phase 2 |
| Deploy | Manual | Automated Phase 3 → permanent |
| Scoring | Manual review | Haiku structured JSON scoring per phase |
| Best for | Manual eval + iteration loop | Automated parallel coverage + verification + deploy runs |
Sandbox.create({ runtime: "node24", ports: [3000], env: { ANTHROPIC_API_KEY, ... } })npm install -g @anthropic-ai/claude-code vercel agent-browser~/.local/share/com.vercel.cli/auth.jsonsandbox.writeFiles()npx add-pluginnpx next dev --port 3000sandbox.extendTimeout()agent-browservercel linkvercel deployresult.jsonsource.tar.gzSandbox.create({ runtime: "node24", ports: [3000], env: { ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, VERCEL_PLUGIN_LOG_LEVEL: "trace" } })
│
├─ npm install -g @anthropic-ai/claude-code vercel agent-browser (~20s)
├─ Write Vercel CLI auth token to ~/.local/share/com.vercel.cli/auth.json
├─ mkdir -p /home/vercel-sandbox/<slug> && npm init -y
├─ sandbox.writeFiles() → /home/vercel-sandbox/vercel-plugin/ (80 files, ~945KB)
├─ npx add-plugin /home/vercel-sandbox/vercel-plugin -s project -y --target claude-code
│
├─ Phase 1: BUILD
│ ├─ sandbox.writeFiles() → /tmp/prompt.txt
│ ├─ claude --dangerously-skip-permissions --debug --settings <path> "$(cat /tmp/prompt.txt)"
│ │ (with AbortSignal.timeout(TIMEOUT_MS))
│ ├─ Poll every 20s:
│ │ ├─ ls /tmp/vercel-plugin-*-seen-skills.d/ (claimed skills)
│ │ ├─ cat /tmp/vercel-plugin-*-seen-skills.txt (seen skills snapshot)
│ │ ├─ find ~/.claude/debug -type f (debug log count)
│ │ ├─ find <project> -newer /tmp/prompt.txt (new project files)
│ │ └─ curl localhost:3000 (port status)
│ ├─ Extract build artifacts
│ └─ Haiku build score (structured JSON)
│
├─ Start dev server (if not already running)
├─ sandbox.extendTimeout(...)
│
├─ Phase 2: VERIFY (if >1 project file exists)
│ ├─ sandbox.writeFiles() → /tmp/verify.txt (agent-browser verification prompt)
│ ├─ claude --dangerously-skip-permissions --debug "$(cat /tmp/verify.txt)"
│ │ (with AbortSignal.timeout(1_200_000) — 20 min)
│ ├─ Re-extract skills (verify phase triggers more)
│ └─ Haiku verify score (per-story pass/fail JSON)
│
├─ Phase 3: DEPLOY (if >3 project files)
│ ├─ sandbox.writeFiles() → /tmp/deploy.txt
│ ├─ claude --dangerously-skip-permissions --debug "$(cat /tmp/deploy.txt)"
│ │ (links to vercel-labs, deploys, fixes build errors up to 3x)
│ ├─ Extract deploy URL from output (*.vercel.app)
│ ├─ Re-extract skills (deploy phase triggers more)
│ └─ Haiku deploy score (structured JSON)
│
├─ Write <slug>/result.json immediately (crash-safe)
├─ Update aggregate results.json (complete: false until all done)
├─ Extract source.tar.gz
└─ sandbox.stop() (skipped if --keep-alive)localhost:3000npx next dev --port 3000openwait --load networkidlescreenshot --annotatesnapshot -iSTORY_1: PASSvercel link --yes --scope vercel-labs --project <slug>-YYYYMMDDvercel deploy --yesVERCEL_TOKEN~/.local/share/com.vercel.cli/auth.jsonbenchmark-agentsclaude --print-p-pwriteFiles()Sandbox.create({ env: { ... } })runCommandsandbox.writeFiles()/home/user//home/vercel-sandbox/-YYYYMMDDHHMM# One-time setup: link project for OIDC sandbox auth
npx vercel link --scope vercel-labs -y
npx vercel env pull .env.local
# Auth (auto-resolved from macOS Keychain + Vercel CLI auth):
# - ANTHROPIC_API_KEY: from Keychain "ANTHROPIC_AUTH_TOKEN" (vck_* key) or env var
# - VERCEL_TOKEN: from ~/.local/share/com.vercel.cli/auth.json (vca_* token) or env var
# - ANTHROPIC_BASE_URL: defaults to https://ai-gateway.vercel.sh# Generate scenarios as JSON, then run
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios-file /tmp/my-scenarios.json
# With all phases + keep-alive for overnight
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios-file /tmp/scenarios.json --keep-alive --keep-hours 8
# Build-only, no verification or deploy
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios-file /tmp/scenarios.json --skip-verify --skip-deploy
# Filter to specific slugs from file or defaults
bun run .claude/skills/benchmark-sandbox/run-eval.ts --scenarios splitwise-clone,calendly-clone// List claimed skills
const claims = await sandbox.runCommand("sh", ["-c",
"ls /tmp/vercel-plugin-*-seen-skills.d/ 2>/dev/null"
]);
// Check hook firing count
const hooks = await sandbox.runCommand("sh", ["-c",
"find /home/vercel-sandbox/.claude/debug -name '*.txt' -exec grep -c 'executePreToolHooks' {} +"
]);
// Check port 3000
const port = await sandbox.runCommand("sh", ["-c",
"curl -s -o /dev/null -w '%{http_code}' http://localhost:3000"
]);
// Get public URL (after ports: [3000] in Sandbox.create)
const url = sandbox.domain(3000);~/dev/vercel-plugin-testing/sandbox-results/<run-id>/<run-id>/
results.json # Aggregate results (complete: false until all done, then true)
report.md # Markdown report with scores, coverage, URLs
<slug>/
result.json # Per-scenario result (written immediately on completion)
source.tar.gz # Project source archiveslugsandboxIdsuccessdurationMsclaimedSkills[]expectedSkills[]projectFiles[]appUrlhttps://sb-XXX.vercel.rundeployUrlhttps://xxx.vercel.apppollHistory[]verification{ ran, exitCode, stories: [{ index, status }], output }buildScoredeployScorereport.md.reports/<timestamp>.md| Metric | Best | Typical |
|---|---|---|
| Skills per scenario | 31 (ai-interior-designer) | 12-24 |
| Expected skill coverage | 100% (pet-adoption-board 4/4, apartment-hunting-copilot 7/7, splitwise-clone 6/6) | 50-86% |
| User stories verified | 3/3 PASS (ai-dream-journal, ai-gift-finder, ai-resume-roaster, ai-music-mood-radio, team-standup-bot, pet-adoption-board) | varies |
| Files built per scenario | 37 (student-study-groups) | 6-25 |
| Build time | 5-11 min | 5-7 min |
ai-sdkshadcnnextjsvercel-functionscron-jobsrouting-middlewaresession-end-cleanupsandbox.snapshot()@vercel/sandbox@2.0.0-beta.3sandbox.stop()vercel-sandbox/home/vercel-sandbox//home/user//root/--dangerously-skip-permissionsrunCommand{ signal: AbortSignal.timeout(ms) }{ timeout }*.vercel.app