diff --git a/web/app/api/vm/route.ts b/web/app/api/vm/route.ts index c940a1c2e022..b9a0415de726 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, }); @@ -463,6 +487,7 @@ export async function POST(request: Request): Promise { persistentHome: candidate.persistentHome === true, perMachineHome: candidate.perMachineHome === true, memoryMb, + imageSize: imageSelection.size ?? undefined, modelPlane, timing, })); @@ -536,6 +561,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 cf33cafb62a6..1da508d0d42e 100644 --- a/web/services/vms/drivers/freestyle.ts +++ b/web/services/vms/drivers/freestyle.ts @@ -733,11 +733,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 662c01b9930b..663e8c46edfd 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 delivered at create time (the coderouter * model-plane env: OPENAI_BASE_URL plus placeholder keys). Treat values as 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/services/vms/timings.ts b/web/services/vms/timings.ts index 2aa499af5aef..9d4d7a48ad37 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 4609b688e2d6..bb8054c0e790 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, @@ -374,6 +376,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"]; /** * Wires the machine to coderouter. Provisioned after the row exists (its id * is the token binding) and before the provider call; a failure fails the @@ -388,20 +392,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; @@ -468,6 +487,7 @@ export function createVm(input: { ? homeVolumeNameForUser(input.userId) : undefined, memoryMb: input.memoryMb, + imageSize: input.imageSize, envs: materials?.envs, edgeRules: materials?.edgeRules, ...(network ? { network: { id: network.providerNetworkId } } : {}), 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 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); });