cmux NEXT: Cloud as where a new chat runs, a row in the composer's environment picker

TE
6d ago
+668-18

HOLD: lands after #26791, whose fleet package tests have not run yet (the fleet controller is stalled).

What

Where a new chat runs becomes one choice in the composer: This Mac or Cloud. Behind Labs (preview features).

  • The control. A Cloud row at the top of the environment picker's Cloud section, with one line on what it does. Every existing row stays: the account's machines, New Cloud Machine, Open Cloud…, SSH… and the rest. Picking it makes the composer read Cloud; a machine picked after it makes the chat a chat on that machine again.
  • The default stays This Mac. Nothing flips it.
  • The first send with Cloud picked calls cloud.startChat (#26791) with the prompt, the folder the folder chip names, the agent and the model. The host picks and sizes a Cloud machine, copies the folder, opens the chat there and closes this tab. No machine, size or environment prompt.
  • Refusals keep the prompt. Attachments are refused on Cloud for now. A Cloud pick that is no longer available when the send happens (Labs went off, a chat started here) is refused too, never sent as a chat on this Mac.
  • One start at a time. While a Cloud start waits on the host (a new machine boots in minutes), another Return or New chat starts nothing.
  • Picking Cloud drops a saved environment or machine picked before it, so the folder sent is the one the chip shows. It also hands focus back to the prompt, so Return sends.
  • The row lives in the existing picker, so it opens the way that picker already does; this PR adds no menu or animation.

Gallery

Gallery: agent-pane.cloud-run-designs/row, agent-pane.cloud-run-designs/bare, agent-pane.cloud-run-designs/chip, agent-pane.composer-context/environment-picker-cloud-picked

Three designs side by side in agent-pane.cloud-run-designs (CloudRunDesigns.tsx, experimental). The two alternatives are gallery-only and wrap the real scope row; only the picked one ships.

  • row (picked): Cloud is a row at the top of the picker's Cloud section, with one line on what it does.
  • bare: the same row with no subtitle.
  • chip: a This Mac / Cloud switch beside the picker.

Why row: one control already answers where a chat runs, and the subtitle tells Cloud apart from a named machine.

States of the shipped row stay in agent-pane.composer-context: environment-picker-cloud-row, -cloud-picked, -cloud-search and -cloud-closed.

Why

Leo, 2026-10-28: "You can just say ... where should this chat run and stuff, and that's just about it ... it's somewhere in the cloud, and then it gets managed for you. You don't have to select an environment, you don't have to create an environment, you don't have to do all that nonsense".

Risk

  • Behind Labs. Without it the row does not show and the host method refuses with labs_off.
  • The environment picker's other rows are unchanged; persistent() now resets any non-persistent mode (Sandboxed or Cloud) when a machine is picked.
  • The model goes to the host, but the machine's start uses the agent's default model for now (#26791).
  • A folder-history default (a folder whose last chat ran in Cloud opens with Cloud picked) is not in this PR.

Tests

  • EnvironmentPicker.test.tsx (new): picking Cloud sets the mode and focuses the prompt on the first pick and on a repeat pick; a machine picked while Cloud is the mode goes back to a chat here.
  • NewTabScreen.test.tsx: a Cloud send goes to onCloud, not a new chat, and a refusal keeps the prompt; a saved environment picked then Cloud sends the chip's folder (/repo/A), not the tab's inherited home.
  • cloudRun.test.ts: the host call's params, its refusals, attachments refused, a Cloud pick that is no longer offered refused, and a second start while one waits refused.
  • Linux webview checks (bun run check) and these suites ran with cmux-ci test at this head.
  • test/gallery-coverage.test.ts fails the same two tests on feat-cmux-next (a2f8b65eac), with the same list of components; none is in this PR. A base red.

Review: an independent Codex (gpt-6-astra, high) review took three rounds and returned SHIP at 8098947800850328f0f9c7583c5c11752faaf9f4; a delta review of the gallery designs and the one-start guard returned SHIP at ca765c9e84de566864bf26ebf279b733b1b1672d. Since then only the gallery-only switch design changed (a fieldset for lint).

Stacked on #26791. It is retargeted to feat-cmux-next once that lands.

Captures: the Gallery: line above picks the states the gallery advisory run renders. Per #21400 its images stay in the run's artifact (gallery-advisory-<PR>-<HEAD_SHA>), not on this public PR.

Agent session: agent session

🤖 Generated with Claude Code