Skip to content

feat(runtime-vm): per-agent runtime and a smolvm VM runtime (ZOD109 cycle 1) - #127

Merged
orivibes merged 1 commit into
mainfrom
feat/zod109-vm-runtime
Oct 13, 2026
Merged

orivibes merged 1 commit into
mainfrom
feat/zod109-vm-runtime

Conversation

@orivibes

@orivibes orivibes commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Part of #109 (cycle 1 of 2; cycle 2, the unattended smoke tester, completes it).

What

  • Per-agent runtime:. Each agent can set runtime: local | docker | podman | vm, and inherits the workforce value when it doesn't. --runtime now sets only the workforce default. Container rules now follow each agent's resolved runtime: a container: block on a vm agent is rejected, a workforce container: block is allowed only when some agent runs in a container, and one workforce can use docker or podman but not both.
  • @zooid/runtime-vm. A new package that runs a runtime: vm agent's ACP process in its own smolvm microVM, over smolvm machine exec -i:
    • The VM boots from vm.image, or the preset's image if that isn't set.
    • The workdir is mounted read-only at /workspace, and it is the only host mount.
    • No --net, so the guest has no network.
    • vm: takes image, cpus, memory, disk.
  • Daemon lifecycle. At startup the daemon creates and starts each vm agent's machine (provisionVms). It recreates a machine when its vm: settings hash changes, refuses to start if a vm workdir holds .env*, .dev.vars* or .claude/settings.local.json, and stops the machine (without deleting it) on shutdown. Image prepull now covers only container agents.
  • Context. vm agents are left out of the context sockets, and the daemon logs why: socket=(unavailable: runtime vm has no host socket…).

Spike (Phase 0, smolvm 1.25.2)

  • Duplex exec stream: 41/41 pings over 10 minutes with no drops. Stdin EOF reaches the guest and its exit code comes back.
  • Host SIGTERM: kills the guest process.
  • :ro mount: stays read-only even after a successful remount,rw as root, because smolvm enforces it host-side.
  • Egress allowlist: works.
  • Nested Docker: partial. The first docker run hung.

The full table is in the PLAN.

The spike also turned up two things the plan didn't anticipate:

  • A non-interactive machine delete needs --force.
  • smolvm keeps its own storage at /workspace. Our mount shadows it.

Done criterion (manual, laptop)

Workforce: lead (local, Claude) and vm-echo (vm, node:22-alpine), run with zooid dev.

  • @vm-echo hello got the in-thread reply echo: … (cwd /workspace, workdir read-only), and @lead answered in the same room.
  • With the image cached, the first turn into a fresh VM took 0.40s and a warm turn 0.31s.
  • These also checked out: attachments land on the host and are readable in the guest, .env refusal, recreate on memory change, and stop-not-delete.

Note for review: test:vm, not test:infra

The integration tier script is named test:vm. CI's required test-infra job runs pnpm -r test:infra on Linux runners, which have no smolvm. The suite would skip there, and the tier guard fails a skipped suite. To run it locally: pnpm -C packages/runtime-vm test:vm.

Tests

  • pnpm test, pnpm typecheck, pnpm build: all green.
  • pnpm -C packages/runtime-vm test:vm: 5/5 against a real smolvm machine.

🤖 Generated with Claude Code

…ycle 1)

Agents may set their own `runtime:` (local | docker | podman | vm),
inheriting the workforce value otherwise. `runtime: vm` runs the agent's
ACP process in its own smolvm microVM, booted from an OCI image, with the
workdir as its only host mount (read-only at /workspace) and no network.
The daemon creates/starts each vm agent's machine at startup, recreates it
when its vm: settings change, refuses a workdir holding secret files, and
stops (not deletes) it on shutdown. vm agents get no context tools yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@orivibes
orivibes force-pushed the feat/zod109-vm-runtime branch from 455bf5b to b290fb6 Compare October 13, 2026 21:33
@orivibes
orivibes added this pull request to stack #139 October 13, 2026 21:34
@orivibes
orivibes merged commit aa7699f into main Oct 13, 2026
2 checks passed
@orivibes
orivibes deleted the feat/zod109-vm-runtime branch October 13, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant