From 64ec50fdd003e2b3e54b11bb46ed3fd3b9da2565 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Wed, 2 Sep 2026 18:59:52 -0700 Subject: [PATCH 1/2] cloud: minimal create path; sized devbox ladder as the defaults; per-stage Server-Timing Create no longer runs the Stripe-to-Stack plan reconcile before every request (150 to 360 ms). It runs inline only when the cached plan would block the create, and otherwise after the response via next/server after(). The owner network row and the create row are resolved concurrently. Sized ladder images (one Freestyle snapshot per size, promoted under cmux's key from the 20260903a bake with the baked cmux-tui daemon) reach the driver as imageSize, and the driver neither reads the machine back nor resizes it. Every create response carries Server-Timing with the same per-stage numbers the span holds. Local proof, same machine, main vs branch: provider_create 801 to 858 ms -> 273 to 308 ms, billing_reconcile 140 to 262 ms -> not in the request. --- web/app/api/vm/route.ts | 67 +++++++++--- web/services/vms/drivers/freestyle.ts | 21 +++- web/services/vms/drivers/types.ts | 7 ++ web/services/vms/images/manifest.json | 152 +++++++++++++++++++++++++- web/services/vms/timings.ts | 5 + web/services/vms/workflows.ts | 46 +++++--- web/tests/vm-image-resolver.test.ts | 15 +-- web/tests/vm-timings.test.ts | 10 ++ web/tests/vm-workflows.test.ts | 5 + 9 files changed, 288 insertions(+), 40 deletions(-) diff --git a/web/app/api/vm/route.ts b/web/app/api/vm/route.ts index d4ad8d7b2cf5..c975d79926e7 100644 --- a/web/app/api/vm/route.ts +++ b/web/app/api/vm/route.ts @@ -44,6 +44,7 @@ import { type VmImageKind, } from "../../../services/vms/images/resolver"; import { reconcileProPlanMetadata } from "../../../services/billing/pro"; +import { after } from "next/server"; import { getStackServerApp, isStackConfigured } from "../../lib/stack"; import { jsonResponse, @@ -182,6 +183,13 @@ export async function POST(request: Request): Promise { timing.record("auth", authDurationMs); setResponseFinalizer((response) => { timing.finish({ status: response.status }); + // Per-stage timings travel with the response too, so a client or a + // smoke run sees where a create spent its time without Axiom. + try { + response.headers.set("Server-Timing", timing.serverTimingHeader()); + } catch { + // Immutable headers on a passthrough Response: the span still has them. + } captureVmProvisionOutcome({ userId: initialUser.id, operation: "create", response, span }); }); let user: AuthedUser = initialUser; @@ -334,28 +342,43 @@ export async function POST(request: Request): Promise { // right before paid limits apply. Best-effort — billing reads must // not block VM creation, so the whole reconcile races a hard // deadline and VM create proceeds with current metadata on timeout. - try { - if (isStackConfigured()) { - const changed = await withBillingReconcileDeadline( - measureVmAsync(timing, "billing_reconcile", async () => { - const serverUser = await getStackServerApp().getUser(user.id); - return serverUser ? reconcileProPlanMetadata(serverUser) : false; - }) - ); - if (changed) { + // The Stripe-to-Stack plan reconcile (a Stack read plus our subscription + // table) used to run before every create and cost 150 to 360 ms. It can + // only change this request's outcome when the cached plan would block + // it, so it runs inline on that path alone. Otherwise it runs after the + // response, so the next request still sees fresh metadata. + const reconcileProPlan = (recordTiming: boolean) => + withBillingReconcileDeadline( + measureVmAsync(recordTiming ? timing : undefined, "billing_reconcile", async () => { + const serverUser = await getStackServerApp().getUser(user.id); + return serverUser ? reconcileProPlanMetadata(serverUser) : false; + }), + ); + let account = await measureVmAsync(timing, "entitlements", () => + resolveVmProvisioningAccountScope(user, request, { requestedBillingTeamId }) + ); + let reconcileMode: "off" | "deferred" | "inline" = isStackConfigured() ? "deferred" : "off"; + if (!account.ok && reconcileMode === "deferred") { + reconcileMode = "inline"; + try { + if (await reconcileProPlan(true)) { const reconciledUser = await measureVmAsync(timing, "auth", () => verifyRequest(request, { requestedTeamId: requestedBillingTeamId }) ); if (reconciledUser) user = reconciledUser; + account = await measureVmAsync(timing, "entitlements", () => + resolveVmProvisioningAccountScope(user, request, { requestedBillingTeamId }) + ); } + } catch (err) { + console.error("[VM] Pro plan reconcile failed", err); } - } catch (err) { - console.error("[VM] Pro plan reconcile failed", err); } - const account = await measureVmAsync(timing, "entitlements", () => - resolveVmProvisioningAccountScope(user, request, { requestedBillingTeamId }) - ); + setSpanAttributes(span, { "cmux.billing.reconcile_mode": reconcileMode }); if (!account.ok) return account.response; + if (reconcileMode === "deferred") { + runAfterResponse(() => reconcileProPlan(false).then(() => undefined)); + } const entitlements = account.entitlements; setSpanAttributes(span, { "cmux.billing.team_id_set": !!entitlements.billingTeamId, @@ -436,6 +459,7 @@ export async function POST(request: Request): Promise { "cmux.vm.image_set": image.length > 0, "cmux.vm.image_version": imageSelection.imageVersion, "cmux.vm.image_manifest": !!imageSelection.manifestEntry, + "cmux.vm.image_size": imageSelection.size?.name ?? "size-less", "cmux.idempotency_key_set": !!idempotencyKey, }); @@ -466,6 +490,7 @@ export async function POST(request: Request): Promise { persistentHome: candidate.persistentHome === true, perMachineHome: candidate.perMachineHome === true, memoryMb, + imageSize: imageSelection.size ?? undefined, envs: modelPlaneEnvs ?? undefined, timing, })); @@ -539,6 +564,20 @@ export async function POST(request: Request): Promise { // awaited) and VM create proceeds with the user's current plan metadata. const BILLING_RECONCILE_DEADLINE_MS = 5_000; +/** + * Run best-effort work once the response has been sent. Vercel keeps the + * function alive for `after` callbacks; outside a request scope (tests, a + * plain Node server) `after` throws, and the work runs detached instead. + */ +function runAfterResponse(work: () => Promise): void { + const guarded = () => work().catch((err) => console.error("[VM] deferred work failed", err)); + try { + after(guarded); + } catch { + void guarded(); + } +} + export async function withBillingReconcileDeadline( reconcile: Promise ): Promise { diff --git a/web/services/vms/drivers/freestyle.ts b/web/services/vms/drivers/freestyle.ts index 3e9f54b3775c..ab75ec5d0d5c 100644 --- a/web/services/vms/drivers/freestyle.ts +++ b/web/services/vms/drivers/freestyle.ts @@ -635,11 +635,22 @@ export class FreestyleProvider implements VMProvider { "cmux.vm.network.private": !!networkId, }); try { - // CreateVmOptions has no size: a VM boots at its snapshot's - // resources (the devbox snapshot is 2 vCPU / 4 GB / 16 GB) and - // only a grow-only resize raises them. Size first so the machine - // the daemon comes up on is the one that was sold. - await this.growToRequestedSize(fs, vm, vmId, options.memoryMb, span); + if (options.imageSize) { + // One snapshot per size: the machine already boots at the shape + // that was sold, so nothing is read back and nothing is grown. + setSpanAttributes(span, { + "cmux.vm.image_size": options.imageSize.name, + "cmux.vm.resources.cpu": options.imageSize.cpu, + "cmux.vm.resources.memory_mb": options.imageSize.memoryMb, + "cmux.vm.resources.storage_mb": options.imageSize.storageMb, + "cmux.vm.resize.requested": false, + }); + } else { + // A size-less image boots at its snapshot's resources and only a + // grow-only resize raises them. Size first so the machine the + // daemon comes up on is the one that was sold. + await this.growToRequestedSize(fs, vm, vmId, options.memoryMb, span); + } // The baked supervisor is already bringing the daemon up; the only // per-machine input it needs is the model-plane env file. if (options.envs) await this.writeModelPlaneEnv(vm, vmId, options.envs); diff --git a/web/services/vms/drivers/types.ts b/web/services/vms/drivers/types.ts index 81da0088937e..d324ebbbcbef 100644 --- a/web/services/vms/drivers/types.ts +++ b/web/services/vms/drivers/types.ts @@ -80,6 +80,13 @@ export type CreateOptions = { * this way). Providers without sizing ignore it. */ memoryMb?: number; + /** + * The snapshot's own shape when the image is a sized ladder entry + * (services/vms/images/sizes.ts): the machine boots at the shape that was + * sold and the driver must not read it back or resize. Absent for size-less + * images, which are grown to `memoryMb`. + */ + imageSize?: { readonly name: string; readonly cpu: number; readonly memoryMb: number; readonly storageMb: number } | null; /** * Machine-level environment injected at create time (e.g. the coderouter * model-plane env: OPENAI_BASE_URL + a per-machine route token). Values may diff --git a/web/services/vms/images/manifest.json b/web/services/vms/images/manifest.json index 7e11bf72f846..32630717ce1f 100644 --- a/web/services/vms/images/manifest.json +++ b/web/services/vms/images/manifest.json @@ -426,7 +426,157 @@ "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon f277fe6f46, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract).", "cmuxTuiCommit": "f277fe6f467225f82294298964ca8cb95d2f4dae", "cmuxTuiSha256": "cab5af6915128889f1ee512cbae9bc9f63914bcc724efbe3d9bfba12326370b7", - "defaultForKind": true + "defaultForKind": false + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903a-sm", + "imageId": "sh-c62ba13d5db544f6aa2d115adf8460dc", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "7437e1b1cd36417dd0b4dc7f004fa37f5d44bfea", + "builtAt": "2026-09-03T01:32:59.533Z", + "builderScriptVersion": "c0e51343f367d460f76fee47ce1de50d344132200243f7247caf3b88e17cb988", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon 4ea1bec84e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-c62ba13d5db544f6aa2d115adf8460dc, md=sh-24a152ab020144139fd4005c00df2c16, lg=sh-d504172d493146668ec9f0e92c4abb5f, xl=sh-17ee5c0950584ec8ae04cbd1089fa70c, 2xl=sh-dfdb35cf44c84e839a4800e42caab231.", + "cmuxTuiCommit": "4ea1bec84ec11673ec756327b16d4abf24af7611", + "cmuxTuiSha256": "f7701639460606f1e2e666871670315c5fb323937df108b99c82c165078e827d", + "defaultForKind": true, + "size": { + "name": "sm", + "cpu": 2, + "memoryMb": 4096, + "storageMb": 16384, + "freestyleBase": "freestyle/ubuntu-sm" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903a-md", + "imageId": "sh-24a152ab020144139fd4005c00df2c16", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "7437e1b1cd36417dd0b4dc7f004fa37f5d44bfea", + "builtAt": "2026-09-03T01:32:59.533Z", + "builderScriptVersion": "c0e51343f367d460f76fee47ce1de50d344132200243f7247caf3b88e17cb988", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon 4ea1bec84e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-c62ba13d5db544f6aa2d115adf8460dc, md=sh-24a152ab020144139fd4005c00df2c16, lg=sh-d504172d493146668ec9f0e92c4abb5f, xl=sh-17ee5c0950584ec8ae04cbd1089fa70c, 2xl=sh-dfdb35cf44c84e839a4800e42caab231.", + "cmuxTuiCommit": "4ea1bec84ec11673ec756327b16d4abf24af7611", + "cmuxTuiSha256": "f7701639460606f1e2e666871670315c5fb323937df108b99c82c165078e827d", + "defaultForKind": true, + "size": { + "name": "md", + "cpu": 4, + "memoryMb": 8192, + "storageMb": 32768, + "freestyleBase": "freestyle/ubuntu" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903a-lg", + "imageId": "sh-d504172d493146668ec9f0e92c4abb5f", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "7437e1b1cd36417dd0b4dc7f004fa37f5d44bfea", + "builtAt": "2026-09-03T01:32:59.533Z", + "builderScriptVersion": "c0e51343f367d460f76fee47ce1de50d344132200243f7247caf3b88e17cb988", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon 4ea1bec84e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-c62ba13d5db544f6aa2d115adf8460dc, md=sh-24a152ab020144139fd4005c00df2c16, lg=sh-d504172d493146668ec9f0e92c4abb5f, xl=sh-17ee5c0950584ec8ae04cbd1089fa70c, 2xl=sh-dfdb35cf44c84e839a4800e42caab231.", + "cmuxTuiCommit": "4ea1bec84ec11673ec756327b16d4abf24af7611", + "cmuxTuiSha256": "f7701639460606f1e2e666871670315c5fb323937df108b99c82c165078e827d", + "defaultForKind": true, + "size": { + "name": "lg", + "cpu": 8, + "memoryMb": 16384, + "storageMb": 65536, + "freestyleBase": "freestyle/ubuntu-lg" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903a-xl", + "imageId": "sh-17ee5c0950584ec8ae04cbd1089fa70c", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "7437e1b1cd36417dd0b4dc7f004fa37f5d44bfea", + "builtAt": "2026-09-03T01:32:59.533Z", + "builderScriptVersion": "c0e51343f367d460f76fee47ce1de50d344132200243f7247caf3b88e17cb988", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon 4ea1bec84e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-c62ba13d5db544f6aa2d115adf8460dc, md=sh-24a152ab020144139fd4005c00df2c16, lg=sh-d504172d493146668ec9f0e92c4abb5f, xl=sh-17ee5c0950584ec8ae04cbd1089fa70c, 2xl=sh-dfdb35cf44c84e839a4800e42caab231.", + "cmuxTuiCommit": "4ea1bec84ec11673ec756327b16d4abf24af7611", + "cmuxTuiSha256": "f7701639460606f1e2e666871670315c5fb323937df108b99c82c165078e827d", + "defaultForKind": true, + "size": { + "name": "xl", + "cpu": 16, + "memoryMb": 32768, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-xl" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903a-2xl", + "imageId": "sh-dfdb35cf44c84e839a4800e42caab231", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "7437e1b1cd36417dd0b4dc7f004fa37f5d44bfea", + "builtAt": "2026-09-03T01:32:59.533Z", + "builderScriptVersion": "c0e51343f367d460f76fee47ce1de50d344132200243f7247caf3b88e17cb988", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon 4ea1bec84e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-c62ba13d5db544f6aa2d115adf8460dc, md=sh-24a152ab020144139fd4005c00df2c16, lg=sh-d504172d493146668ec9f0e92c4abb5f, xl=sh-17ee5c0950584ec8ae04cbd1089fa70c, 2xl=sh-dfdb35cf44c84e839a4800e42caab231.", + "cmuxTuiCommit": "4ea1bec84ec11673ec756327b16d4abf24af7611", + "cmuxTuiSha256": "f7701639460606f1e2e666871670315c5fb323937df108b99c82c165078e827d", + "defaultForKind": true, + "size": { + "name": "2xl", + "cpu": 32, + "memoryMb": 65536, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-2xl" + } } ] } diff --git a/web/services/vms/timings.ts b/web/services/vms/timings.ts index 35f03fd21e0c..0483b54adb0c 100644 --- a/web/services/vms/timings.ts +++ b/web/services/vms/timings.ts @@ -61,6 +61,11 @@ export class VmTimingRecorder implements VmTimingSink { })); } + /** `Server-Timing` header value: one metric per recorded stage, milliseconds. */ + serverTimingHeader(): string { + return [...this.durations.entries()].map(([stage, duration]) => `${stage};dur=${duration}`).join(", "); + } + snapshot(): Record { return Object.fromEntries( [...this.durations.entries()].map(([stage, duration]) => [stage, duration]), diff --git a/web/services/vms/workflows.ts b/web/services/vms/workflows.ts index b82039538e7f..4fef1f556fd4 100644 --- a/web/services/vms/workflows.ts +++ b/web/services/vms/workflows.ts @@ -1,5 +1,7 @@ import { createHash, randomUUID } from "node:crypto"; import * as Effect from "effect/Effect"; +import * as Either from "effect/Either"; +import type { CreateOptions } from "./drivers/types"; import * as Layer from "effect/Layer"; import type { AttachEndpoint, @@ -345,6 +347,8 @@ export function createVm(input: { readonly perMachineHome?: boolean; /** Runtime memory requested by the caller, in MB. Providers may ignore it. */ readonly memoryMb?: number; + /** See CreateOptions.imageSize: a sized ladder image boots at its shape and is never resized. */ + readonly imageSize?: CreateOptions["imageSize"]; /** * Machine-level env injected at provider create (e.g. the coderouter * model-plane vars). May hold secrets: passed to the driver only, never @@ -358,20 +362,35 @@ export function createVm(input: { const providers = yield* VmProviderGateway; const billing = yield* VmBillingGateway; - // Resolved before the row is inserted and the credit reserved, so a - // provisioning failure here costs nothing to unwind. The network is an - // account-level resource — free, idempotent, and reused by every later - // machine — so resolving it for a create that then hits the active-VM - // limit is not waste, it is setup the next create no longer has to do. - // Null means the deployment or provider has no private networking, and the - // machine falls back to being reachable at its public address. - const network = yield* measureVmEffect( - input.timing, - "resolve_network", - resolveOwnerNetwork({ userId: input.userId, provider: input.provider }), + // The owner's network row and the create row do not depend on each other, + // so the request pays the slower of the two reads, not their sum. A network + // failure after the row was inserted marks that row failed instead of + // leaving it "creating" forever; the network itself is an account-level + // resource, so nothing there needs unwinding. + const [networkResult, create] = yield* Effect.all( + [ + Effect.either( + measureVmEffect( + input.timing, + "resolve_network", + resolveOwnerNetwork({ userId: input.userId, provider: input.provider }), + ), + ), + beginCreateWithLazyProviderRefresh(repo, providers, input), + ], + { concurrency: 2 }, ); - - const create = yield* beginCreateWithLazyProviderRefresh(repo, providers, input); + if (Either.isLeft(networkResult)) { + if (create.inserted) { + yield* repo.markCreateFailed({ + id: create.vm.id, + code: PROVIDER_CREATE_UNAVAILABLE_FAILURE_CODE, + message: errorMessage(networkResult.left), + }).pipe(Effect.catchAll(() => Effect.void)); + } + return yield* Effect.fail(networkResult.left); + } + const network = networkResult.right; if (!create.inserted) { const existing = create.vm; @@ -407,6 +426,7 @@ export function createVm(input: { ? homeVolumeNameForUser(input.userId) : undefined, memoryMb: input.memoryMb, + imageSize: input.imageSize, envs: input.envs, ...(network ? { network: { id: network.providerNetworkId } } : {}), }), diff --git a/web/tests/vm-image-resolver.test.ts b/web/tests/vm-image-resolver.test.ts index b3f1bb495fec..c648f75efe00 100644 --- a/web/tests/vm-image-resolver.test.ts +++ b/web/tests/vm-image-resolver.test.ts @@ -19,11 +19,12 @@ function captureImageConfigError(fn: () => unknown): VmImageConfigError { throw new Error("expected VmImageConfigError to be thrown"); } -// cmux's validated public-platform devbox (baked on cmux's Freestyle account): -// the manifest's base default. The manifest is the only source of truth for -// images; no env var selects or overrides one. -const validatedSnapshot = "sh-e4dc9393a82e4dfaaa8f90b01b0d247c"; -const validatedVersion = "freestyle-cmux-devbox-20260902e"; +// cmux's validated public-platform devbox ladder (baked on cmux's Freestyle +// account): one snapshot per size, each the base default for its size. With no +// memory given the resolver picks the smallest. The manifest is the only source +// of truth for images; no env var selects or overrides one. +const validatedSnapshot = "sh-c62ba13d5db544f6aa2d115adf8460dc"; +const validatedVersion = "freestyle-cmux-devbox-20260903a-sm"; // The desktop devbox from this PR: validated, listed under both kinds with one // image id, but baked on another Freestyle account, so not a default until // re-promoted under cmux's key. @@ -75,7 +76,7 @@ describe("VM image resolver: request by kind", () => { imageVersion: validatedVersion, kind: "base", }); - expect(listVmImageKinds("freestyle", deployed)).toEqual([{ kind: "base", image: validatedSnapshot }]); + expect(listVmImageKinds("freestyle", deployed)).toMatchObject([{ kind: "base", image: validatedSnapshot, size: { name: "sm" } }]); expect(resolveVmImage("freestyle", undefined, {})).toMatchObject({ image: validatedSnapshot, imageVersion: validatedVersion, @@ -160,7 +161,7 @@ describe("VM image resolver: request by kind", () => { expect(vmImageKindFor("freestyle", validatedSnapshot)).toBe("base"); // The base default is flagged in the manifest; the retired beta entry never is. - expect(listVmImageKinds("freestyle", deployed)).toEqual([{ kind: "base", image: validatedSnapshot }]); + expect(listVmImageKinds("freestyle", deployed)).toMatchObject([{ kind: "base", image: validatedSnapshot, size: { name: "sm" } }]); expect(listVmImageKinds("freestyle", deployed).map((entry) => entry.image)).not.toContain("sh-fb3dcf7b47894114889b10186626af5b"); }); }); diff --git a/web/tests/vm-timings.test.ts b/web/tests/vm-timings.test.ts index 78d13806a754..b47dd5e15a4f 100644 --- a/web/tests/vm-timings.test.ts +++ b/web/tests/vm-timings.test.ts @@ -25,6 +25,16 @@ describe("VM timing helpers", () => { expect(recorded[0]?.durationMs).toBeGreaterThanOrEqual(0); }); + test("Server-Timing lists every recorded stage in milliseconds", () => { + const attributes: Array<{ key: string; value: unknown }> = []; + const span = { setAttribute: (key: string, value: unknown) => attributes.push({ key, value }) } as unknown as Span; + const recorder = new VmTimingRecorder(span, "create", { debugTimings: false }); + recorder.record("auth", 12.345); + recorder.record("provider_create", 250); + recorder.record("provider_create", 50); + expect(recorder.serverTimingHeader()).toBe("auth;dur=12.35, provider_create;dur=300"); + }); + test("finish is idempotent", () => { const attributes: Array<{ key: string; value: unknown }> = []; const span = { diff --git a/web/tests/vm-workflows.test.ts b/web/tests/vm-workflows.test.ts index dce3fdd3b617..00b7e825e174 100644 --- a/web/tests/vm-workflows.test.ts +++ b/web/tests/vm-workflows.test.ts @@ -1663,6 +1663,7 @@ describe("VM Effect workflows", () => { }); let providerCreateCalls = 0; let providerMemoryMb: number | undefined; + let providerImageSize: { name: string; cpu: number; memoryMb: number; storageMb: number } | null = null; let usageEventAttempts = 0; const repo: VmRepositoryShape = { listUserVms: () => Effect.succeed([]), @@ -1705,6 +1706,7 @@ describe("VM Effect workflows", () => { Effect.sync(() => { providerCreateCalls += 1; providerMemoryMb = options.memoryMb; + providerImageSize = options.imageSize ?? null; return { provider: "freestyle" as const, providerVmId: "provider-vm-usage-events", @@ -1736,6 +1738,7 @@ describe("VM Effect workflows", () => { image: "snapshot-test", imageVersion: "test-version", memoryMb: 3072, + imageSize: { name: "sm", cpu: 2, memoryMb: 4096, storageMb: 16384 }, idempotencyKey: "usage-events", }).pipe(Effect.provide(layer)), ); @@ -1743,6 +1746,8 @@ describe("VM Effect workflows", () => { expect(created.providerVmId).toBe("provider-vm-usage-events"); expect(providerCreateCalls).toBe(1); expect(providerMemoryMb).toBe(3072); + // A sized image reaches the driver as the shape to boot at, never to resize to. + expect(providerImageSize).toEqual({ name: "sm", cpu: 2, memoryMb: 4096, storageMb: 16384 }); expect(usageEventAttempts).toBe(2); }); From 29f62a9737e88bc43579488cf7d42823c21e6a77 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:20:46 -0700 Subject: [PATCH 2/2] cloud: promote the 20260903b devbox ladder (baked cmux-tui daemon + coderouter edge) for both kinds edge1 (PR 11622) was baked before PR 11666 and ships no cmux-tui binary, so every machine from it waited for the attach heal to install and start the daemon. 20260903b is baked from this merged tree: desktop layer, edge agent-config, baked instance-bound daemon (up 375 ms after the first probe, distinct identities across two machines), one snapshot per ladder size, both kinds. edge1 and 20260902e stay listed for rollback. --- web/services/vms/images/manifest.json | 320 +++++++++++++++++++++++++- web/tests/vm-image-resolver.test.ts | 15 +- 2 files changed, 318 insertions(+), 17 deletions(-) diff --git a/web/services/vms/images/manifest.json b/web/services/vms/images/manifest.json index fc27e96fa4c6..b0d1c7e4a747 100644 --- a/web/services/vms/images/manifest.json +++ b/web/services/vms/images/manifest.json @@ -445,7 +445,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "sm", "cpu": 2, @@ -473,7 +473,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "md", "cpu": 4, @@ -501,7 +501,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "lg", "cpu": 8, @@ -529,7 +529,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "xl", "cpu": 16, @@ -557,7 +557,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "2xl", "cpu": 32, @@ -585,7 +585,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "sm", "cpu": 2, @@ -613,7 +613,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "md", "cpu": 4, @@ -641,7 +641,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "lg", "cpu": 8, @@ -669,7 +669,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "xl", "cpu": 16, @@ -697,7 +697,7 @@ }, "validationStatus": "passed", "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; cmux-tui transport. Validated 2026-09-02 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-1ad297617e5443e7a3a662151e1b5268, md=sh-6b3d51d2745840429c33960555e238b7, lg=sh-be104a485d2e460297a80c8dbf39adb8, xl=sh-81be73ea9ff94ee4b83df69ec3fe296b, 2xl=sh-d792f6144a3c40a7a7ac11b48081013a.", - "defaultForKind": true, + "defaultForKind": false, "size": { "name": "2xl", "cpu": 32, @@ -729,6 +729,306 @@ "cmuxTuiSha256": "cab5af6915128889f1ee512cbae9bc9f63914bcc724efbe3d9bfba12326370b7", "defaultForKind": false, "defaultForLocalDev": false + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-sm", + "imageId": "sh-36ede74d2c1845d2a9fd3453b0bff404", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "desktop", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "sm", + "cpu": 2, + "memoryMb": 4096, + "storageMb": 16384, + "freestyleBase": "freestyle/ubuntu-sm" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-md", + "imageId": "sh-3d0d4f89f55b4c749377121f4ec1426c", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "desktop", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "md", + "cpu": 4, + "memoryMb": 8192, + "storageMb": 32768, + "freestyleBase": "freestyle/ubuntu" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-lg", + "imageId": "sh-e66dfcdab7c0410ba8de295cee199bff", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "desktop", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "lg", + "cpu": 8, + "memoryMb": 16384, + "storageMb": 65536, + "freestyleBase": "freestyle/ubuntu-lg" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-xl", + "imageId": "sh-e11d652875604b25b5832d4943d893a7", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "desktop", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "xl", + "cpu": 16, + "memoryMb": 32768, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-xl" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-2xl", + "imageId": "sh-dedf3965adfd46a6954744f4d0163b5c", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "desktop", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "2xl", + "cpu": 32, + "memoryMb": 65536, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-2xl" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-sm-base", + "imageId": "sh-36ede74d2c1845d2a9fd3453b0bff404", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "sm", + "cpu": 2, + "memoryMb": 4096, + "storageMb": 16384, + "freestyleBase": "freestyle/ubuntu-sm" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-md-base", + "imageId": "sh-3d0d4f89f55b4c749377121f4ec1426c", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "md", + "cpu": 4, + "memoryMb": 8192, + "storageMb": 32768, + "freestyleBase": "freestyle/ubuntu" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-lg-base", + "imageId": "sh-e66dfcdab7c0410ba8de295cee199bff", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "lg", + "cpu": 8, + "memoryMb": 16384, + "storageMb": 65536, + "freestyleBase": "freestyle/ubuntu-lg" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-xl-base", + "imageId": "sh-e11d652875604b25b5832d4943d893a7", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "xl", + "cpu": 16, + "memoryMb": 32768, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-xl" + } + }, + { + "provider": "freestyle", + "version": "freestyle-cmux-devbox-20260903b-2xl-base", + "imageId": "sh-dedf3965adfd46a6954744f4d0163b5c", + "envVar": "FREESTYLE_SANDBOX_SNAPSHOT", + "kind": "base", + "cmuxdRemoteCommit": "none-cmux-tui", + "repoCommit": "cca46e23a6a48303490e11d0f5aa7b5c3ca8c6a8", + "builtAt": "2026-09-03T02:11:10.659Z", + "builderScriptVersion": "a2ace736064d10c16247df68bb3231206f82c16fafbc822f83beb0d67c6aa005", + "agentToolResolvedVersions": { + "@anthropic-ai/claude-code": "2.1.252", + "@openai/codex": "0.151.0", + "opencode-ai": "1.18.25", + "@earendil-works/pi-coding-agent": "0.84.4", + "agent-browser": "0.35.2" + }, + "validationStatus": "passed", + "notes": "cmux devbox epoch 2026-09-01-r3 Devbox on the Freestyle public platform (api.freestyle.sh) from freestyle/ubuntu-sm: the base's Node/Bun/Python/uv/Docker plus pinned agents, devtools, Chrome + cua-driver, ble.sh devshell, cmux login banner, and the desktop layer (openbox/TigerVNC 5901, noVNC 6901, Ghostty, Chrome, Thunar) run by the cmux-desktop systemd unit as ubuntu; ubuntu (uid 1000, NOPASSWD sudo) is the work user; baked cmux-tui daemon a9207e460e, identity bound to the instance id, no create-time bootstrap. Validated 2026-09-03 with verify-devbox-image.ts by promote-devbox-image.ts (toolchain, agent pins, daemon contract, desktop on 5901/6901). Sizes derived and re-booted by derive-devbox-sizes.ts: sm=sh-36ede74d2c1845d2a9fd3453b0bff404, md=sh-3d0d4f89f55b4c749377121f4ec1426c, lg=sh-e66dfcdab7c0410ba8de295cee199bff, xl=sh-e11d652875604b25b5832d4943d893a7, 2xl=sh-dedf3965adfd46a6954744f4d0163b5c.", + "cmuxTuiCommit": "a9207e460e738fe5ac08724c4b1cf37eac3849d7", + "cmuxTuiSha256": "d7baa742c5c7ac7422e8a5ed21c9dfa57527e510b4c827a6c2ff997fec6eced4", + "defaultForKind": true, + "size": { + "name": "2xl", + "cpu": 32, + "memoryMb": 65536, + "storageMb": 131072, + "freestyleBase": "freestyle/ubuntu-2xl" + } } ] } diff --git a/web/tests/vm-image-resolver.test.ts b/web/tests/vm-image-resolver.test.ts index eebb236aef33..99426e013701 100644 --- a/web/tests/vm-image-resolver.test.ts +++ b/web/tests/vm-image-resolver.test.ts @@ -22,19 +22,20 @@ function captureImageConfigError(fn: () => unknown): VmImageConfigError { throw new Error("expected VmImageConfigError to be thrown"); } -// The committed manifest default: the `freestyle-cmux-devbox-edge1` ladder, +// The committed manifest default: the `freestyle-cmux-devbox-20260903b` ladder +// (baked, instance-bound cmux-tui daemon plus the coderouter edge changes), // one snapshot per Freestyle size, listed under both kinds (a desktop image is // a superset of a base one, so the same snapshot id serves both; the base // listing's version carries a `-base` suffix). The manifest is the only source // of truth for images; no env var selects or overrides one, and the plan's // memory picks the size. -const ladderVersion = "freestyle-cmux-devbox-edge1"; +const ladderVersion = "freestyle-cmux-devbox-20260903b"; const ladder = { - sm: "sh-1ad297617e5443e7a3a662151e1b5268", - md: "sh-6b3d51d2745840429c33960555e238b7", - lg: "sh-be104a485d2e460297a80c8dbf39adb8", - xl: "sh-81be73ea9ff94ee4b83df69ec3fe296b", - "2xl": "sh-d792f6144a3c40a7a7ac11b48081013a", + sm: "sh-36ede74d2c1845d2a9fd3453b0bff404", + md: "sh-3d0d4f89f55b4c749377121f4ec1426c", + lg: "sh-e66dfcdab7c0410ba8de295cee199bff", + xl: "sh-e11d652875604b25b5832d4943d893a7", + "2xl": "sh-dedf3965adfd46a6954744f4d0163b5c", } as const; // cmux's validated pre-ladder public-platform devbox: still listed (base only, // size-less) so stored rows and explicit requests keep resolving, no longer a