Cloud: a new chat's machine gets the person's own Claude login before the first prompt (DEV builds)

TE
5d ago
+370-14

Leo, on a Cloud chat that never replied: "whenever i start a chat it should be able to send me to cloud automatically", "A TOKEN GETS PASSED", "IN THE ACTUAL PRODUCTION APP IT MAY BE AGAINST TOS SO THAT'S WHY IT'S DIFFERENT", "WE ALL HATE SETTING UP ENVIRONMENTS".

The bug

A chat started on a Cloud machine got its prompt but never replied. Read on two production machines (image cmux-devbox 2026-10-28-r1):

  • fs-firstsend-exercise: the persisted model plane held only the OpenAI pair, so there was no CodeRouter origin. The launcher resolved team / team-unknown with no endpoint and no key, and claude -p printed "Not logged in · Please run /login".
  • A fresh machine: it reached CodeRouter, but the team's pooled Claude account answered "OAuth token revoked".
  • The personal-login fallback (#26139, #26148) never ran on either. It runs only as recovery after a failed prompt, and its installer refused every machine whose guest vm-kind is team. That is every machine a person creates on a team with other members, the creator's own included.

What changes (DEV and dogfood builds)

  • cloud.startChat passes the person's own Claude login to the chat's machine before the first prompt (CloudStartChat+Login.swift). It runs alongside the folder copy and is awaited before the chat opens.
    • When cmux already holds this account's claude setup-token login (the Keychain cache from #26139), it installs with no prompt.
    • Otherwise one confirm runs setup-token once in a visible local terminal. Every later machine of the account then gets it silently.
    • A machine that already has a login is left alone. A teammate's machine, a cancel or a failure opens the chat anyway, and the turn card still offers recovery.
    • Gated to a Debug build or the explicit cmux.cloud.passHarnessLogin default. Production keeps today's sign-in design (the terms concern above).
  • Who may receive a login: the platform's answer, as for Codex, and an owner guest. CloudCredentialTransfer now checks the caller's attach answer (vmKind == "owner", or a Worker machine's owner link; CloudService.callerOwnsVM) before any credential is read and again before delivery, the rule the Codex broker already uses. The guest must also still be an owner machine (/etc/cmux/vm-kind): on a team guest every member's terminal runs as the same Unix user and could read a personal login. cloud.startChat checks the guest's kind before it asks anything, so a team machine (its creator's included) gets no confirm and no login; it keeps the turn card's recovery. Team machines are follow-up #29774.
  • Launcher (agent-auth.sh), own mode with no CodeRouter origin also drops a stale model-plane pair (a CodeRouter URL with the placeholder key), so an own login is never sent to that endpoint. A user's own endpoint with their own key stays. The image source awaits its rebake (awaiting-rebake.json).
  • Launcher (agent-auth.sh): no CodeRouter origin means own mode (no-team-plane), never an empty team plane. Claude then uses a delivered login or shows its own sign-in.

Codex is unchanged: its login is per machine, through the broker's device login (#26211). The alternative that keeps the token on the Mac, a model relay, is scoped in #29766 and not built.

Risk

  • Scope: owner machines only. A personal login goes only to a machine the platform says the caller owns and whose guest is an owner machine. A creator's team machine is not covered, because teammates share its Unix user (and the rebaked image forces team mode there anyway). That case, the model-plane persistence fix and Codex's first chat are #29774.
  • DEV/opt-in only; production unchanged.

Agent session: agent session

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes Cloud chats that started but never replied: a chat's machine now receives the person's own Claude login before the first prompt, in DEV and dogfood builds only.

Old behavior: the launcher fell back to an empty team plane, leaving Claude "Not logged in" or on a revoked team account, and the personal-login fallback only ran as recovery and refused every machine whose guest vm-kind is team.

  • cloud.startChat installs the person's claude setup-token login before the chat opens, for chats with a project and chats without one. A cached login installs silently; otherwise one confirm runs setup-token once in a visible local terminal, and later machines of the account get it with no prompt. The account is re-checked after the login's wait. Automation may dismiss the confirm but never press Set Up, like the other credential dialogs.
  • cloud.startChat probes the guest's own vm-kind first and skips a team guest before any confirm: teammates share its Unix user, so the transfer would refuse it anyway. Delivery stays limited to owner machines.
  • CloudCredentialTransfer authorizes delivery by the platform's answer to the caller's attach request (vmKind == "owner", or a Worker machine's owner link), as the Codex broker already does, checking before any credential is read and again before delivery.
  • The launcher resolves a machine with no CodeRouter origin to own mode (no-team-plane), so Claude uses a delivered login or shows its own sign-in instead of an empty team plane. Own mode also drops a stale model-plane pair (a base URL with the placeholder key) when there is no origin, so a delivered login never reaches that endpoint.

Production keeps today's sign-in design and Codex is unchanged; this is gated to Debug builds or the explicit cmux.cloud.passHarnessLogin opt-in. The launcher change awaits an image rebake.