cmux NEXT: Settings > Machines' Dedicated agent machine switch on a Cloud machine (vmHost)

Merged
TE
10d ago
+851-2

The ask

claude-sr2, word for word: "vmHost. Leo answered your [taste] question: the coordinator's pick wins (on by default for VMs cmux itself creates, with a per-machine toggle, and personal or SSH-added machines never get it). His words: "Yours is fine. people mildly like sandboxing and security but we wanna make it ergo and just like… shit just works. few permissions. worst cases something happens but you just recover and learn". Build it that way. Codex review, then land it."

This is PR 2 of 2: the per-machine toggle in Settings > Machines. PR 1 (#25749, merged as a18cb1f2) adds the daemon default and the acpmux host vm-host [on|off] toggle this switch drives.

Gallery: pages.settings/accounts, pages.settings/long-machines

What changed

  • Settings > Machines. A Cloud machine cmux made shows a "Dedicated agent machine" switch under its row, with the help text "Agents here run without the sandbox and don't ask first. The machine is the boundary." The row follows the existing Full access row on Devices.
  • How it reads and sets. The app runs the machine's own cmux-tui acp --json host vm-host [on|off] through the Cloud API exec (POST /api/vm/{id}/exec). It runs as the machine's work user, or as root on an image from before the work user. The machine's answer ({vmHost, cmuxVm}) is what the switch shows.
    • The read happens once, when the row first shows while the machine is connected. A machine that is not connected is never woken.
    • A failed read is tried again a minute later, the next time the row is drawn. No timer, observer or loop is added.
  • Who sees it. Only machines where the machine says cmuxVm (or it is already on), on a legacy Cloud link that allows exec. Personal machines, SSH machines and app-server machines have no exec route and never show the switch.
  • Turning it on takes the person's click in the page, like Full access. Page script alone is refused (userOnly). Turning it off needs no click. The machine refuses on itself if cmux did not create it (PR 1).
  • An older machine. An older cmux-tui with no vm-host returns a usage error. The row shows no switch until the machine has it.
  • Strings: settingsWindow.vmHost and settingsWindow.vmHostHelp in all 21 locales. Non-English entries are marked needs_review.

Tests (red on the test-only commit 82f3683e, green at the head)

  • webviews/src/pages/settings/hostSections.test.tsx: a Cloud machine cmux made has its switch and other machines have none. Clicking it sends the op.
  • Tests/CmuxNextAppTests/CloudVmHostTests.swift:
    • the command runs as the work user
    • the parser reads acpmux's pretty JSON and its error envelope, and an older cmux-tui's usage error
    • the store reads once, only while connected, and a set answers with the new state
    • a refused set keeps the last state and says why
    • page script alone never turns it on, and off needs no click
    • from the Codex review (tests in 93f1a9a1, which do not compile against 715b0d0b):
      • a set on a machine that is not reachable now runs nothing (the switch is disabled while it is not)
      • a machine forgotten during a read or a set keeps no state, and is read again when it comes back
      • a failed read is tried again only after a minute (injected clock)
      • one click turns on one setting, across Full access and this switch

Red, at 82f3683e (tests only), bun test src/pages/settings/hostSections.test.tsx on cmux-ci (Linux):

EXIT=1
(fail) a Cloud machine cmux made has its Dedicated agent machine switch; other machines have none
 12 pass
 1 fail

Green, at the head (cmux-ci, Linux): bun test src/pages/settings 328 pass / 0 fail. vp fmt, vp lint (0 warnings, 0 errors), vp check --no-fmt --no-lint (no type errors), acpmux-protocol:check and check-concurrency are all clean. The Swift package compiles and its tests run on this PR's Mac CI.

The Swift store's lifecycle came from the Codex review (gpt-6-astra, xhigh): it now drops a removed machine's state and anything still under way for it (a per-machine generation, and forget cancels the read's task), and every set checks the same reach as the row.

Screenshots

These are gallery renders of Settings > Accounts, which holds the Machines card, using the gallery's sample fixture. They come from this head's gallery advisory capture (run 40426848367, base 6b45cc16, head 8256bd26).

BeforeAfter
before, darkafter, dark
before, lightafter, light

After: the sample Cloud machine shows the "Dedicated agent machine" switch, on. The local-network machine shows none.

No capture: the switch shows only for a connected Cloud machine that cmux created, on a legacy Cloud link, and a capture mini has no such machine, so a native tour would show Settings without it. The renders above are the same React Settings page the app hosts, drawn from the gallery's sample Cloud machine.

Size

851 added lines, a little over the ~800 guideline. 264 of them are the two strings in 21 locales (Localizable.xcstrings) and about 290 are tests, so the code itself is about 300 lines. The strings, the switch and its store only work together, so they aren't split.

Risk

  • UI only: one switch on Cloud machine rows. Machines without exec, and personal or SSH machines, are unchanged.
  • Each read or set is one Cloud exec with a 15 s budget. The read happens once for each machine per app run, plus one retry a minute after a failure, only while the row is drawn.

Agent session: agent session
🤖 Generated with Claude Code