feat(agents): Luke's own sandbox template for native agents
P08b of the rollout in "Bringing Codex or Claude Code Max plans into Luke" (https://tryluke.dev/stage/plan/89f19bfc-88b5-471a-886a-84b00180aa09). It replaces P08's hand-set LUKE_AGENT_SANDBOX_SNAPSHOT with Luke's own template of the coding agent's image, so a native launch always opens on the current image. Nothing routes to native agents yet (that's P11).
What it does
The image module, split out (agent-runtime/sandbox-image.ts)
PREPARE_SCRIPT, its pinned installers, andprepareImagemove out ofsandbox.ts, unchanged byte for byte.sandbox.tsimports them.sandbox.tsreaches eve's sandbox runtime (eve/sandbox/vercel), which a web function can't bundle. The new module reaches only the code extension's tooling installer.- eve's own
LUKE_SANDBOX_IMAGEis unchanged: same script, same function text, same digest.
The template (native-sandbox-template.ts)
- Key:
prepareImageis run against a recording session, which keeps the files it writes and the commands it runs. That is the approachtools/codex-probealready uses. The key is the sha256 of all of it plus eve's base image. It changes wheneverPREPARE_SCRIPT, a pin,prepareImage, eve's tooling, or eve's base image changes.- This differs from the literal
LUKE_SANDBOX_IMAGE. That value hashesprepareImage.toString(), which can differ between esbuild bundles if identifiers are renamed. The sweep and the launch live in different function bundles, so they could disagree on the key and the sweep would delete the live template.
- This differs from the literal
- Lock: the template is a persistent sandbox named
luke-native-template-<key>, created from eve's base image (vcr.vercel.com/vercel/eve/base:0.74.0; a test holds it to the installed eve). Vercel refuses a second create of the same name with 409, so concurrent launches build it once and the others wait. - No function waits on the build: the recorded preparation is uploaded and run as a detached command, whose id goes in a tag.
- Ready: a reader that finds the build exited 0 tags it
lukeTemplateBuilt, then snapshots it with no expiration.- A template is ready when it is marked built, stopped, and holds a snapshot.
- The mark is what tells a whole image apart, because a persistent sandbox also snapshots itself on stop, including mid-build.
- A snapshot stops the session, so when two readers (or two sweeps) race, Vercel takes the first snapshot and refuses the second. The loser reads on and finds the template ready.
- Failure and recovery:
- A failed build is stopped and marked. It answers failed for 30 minutes, then is rebuilt.
- A template stopped unready, or created but never started for 5 minutes, is deleted and rebuilt.
- Tags in use: 4–5 of Vercel's 5.
The seam (agent-sandbox-image.ts)
AgentSandboxImagekeepssnapshotId: Effect<Option<string>, …>. The layer is nowagentSandboxImageFromTemplate.- A launch waits on a build for up to 3 minutes, polling every 10 s. If the image is still being prepared after that,
openanswersFAILEDwith the exportedIMAGE_PREPARING_STATUS(425). P11 shows it as "Preparing the agent's image…" and retries; this was agreed with the orchestrator. - The env-var filler is removed:
LUKE_AGENT_SANDBOX_SNAPSHOT,agentSandboxSnapshot, and its test fixtures.
The minute sweep
- It moves the build on and snapshots it whether or not a launch is waiting.
- Where
LUKE_NATIVE_AGENT_ACCOUNTSis non-empty, it builds a new image's template before the first launch asks. - It deletes other keys' templates once they're a day old, so a Preview of another image keeps its own template while it's being tested.
Vercel API (vercel-sandbox-api.ts)
- New calls, copied from
@vercel/sandbox3.8.0: a sandbox read by name without resume, a list by name prefix, a create from an image, and a session snapshot.
P08 follow-up from Devin's review of P09 (the launch race)
- A relaunch now tags its first event number before its command starts.
runnerEndedreads that tag on its own, so an END that the replaced launch posts late can't stop the new launch.- Ported onto P21's shared
runner-sessions.tsafter merging main. Its tag islukeRunnerLaunch(<firstSequence>-<stamp>), written before the command starts, andrunnerEndedreads it on its own. The sandbox still carries 5 tags.ImagePreparing→ 425 is in the sharedfailedStatus, so Claude Code and Codex both get it. - The other finding (the sweep retrying a failed stop) is split out as P08c.
Bundle evidence (eve stays out of the web functions' runtime)
From apps/web's real scripts/bundle-functions.ts metafile on this branch:
| function | reaches the template | eve inputs | eve runtime (execution/, runtime/, sandbox/providers, microsandbox, just-bash, compiled @vercel/sandbox) |
|---|---|---|---|
maintenance-sweep.js | yes | 7: extensions/code/extension/lib/{sandbox,tooling,shell,signed-commit,command-failure}.js, computer-use/extension/lib/computer-use.js, compiled/@vercel/connect | 0 |
default.js, pull-request-chapters.js, terminal/sessions.js, voice/sessions.js | no | 0 | 0 |
A probe bundle of claude-code-sessions.ts plus the seam (what P11's function will reach) has 15 eve inputs, all under extensions/code/extension/lib and computer-use/extension/lib, and no runtime. undeclaredImports passed.
Checks
native-sandbox-template.test.tsruns against a fake of the Sandbox REST API withTestClock. It covers:- reuse of a ready template;
- a build when one is missing, from eve's image with today's
PREPARE_SCRIPT; - a single build and a single snapshot under three concurrent launches;
- racing snapshotters, which take one snapshot;
- a new build after a key change, with the old template swept once it's a day old;
- the sweep pre-building;
- a failed build's retry window;
- a template stopped half-built being rebuilt, not opened;
- the seam's bounded wait failing as preparing, then answering the snapshot.
claude-code-sessions.test.tsadds two tests: a late END from the replaced launch while the new command starts is stale (this test fails without the fix), and a launch while the image is preparing answers 425 and opens nothing.- Typecheck passes for service, web, dev, luke and both probes. Biome, oxlint, discarded-effect and knip are clean.
pnpm check:affectedran lint green, then its parallel typechecks were OOM-killed (137) in my sandbox. P08 hit the same thing. I ran them one by one instead, and CI gates the rest.- Not run live: this sandbox has no Vercel credentials, so the template hasn't been built against real Vercel. The first native launch on Preview will be its first live build.
- The developer waived per-PR Preview runs for this rollout.
Follow-ups
- P08c: the sweep stops idle native sandboxes whose turn-end stop failed.
- A snapshot that Vercel deletes despite having no expiration would make the launch fail on create. The template isn't marked stale in that case.