Repository navigation
Cloud: a new chat's machine gets the person's own Claude login before the first prompt (DEV builds) - #29771
Conversation
… (DEV builds)
Leo: "A TOKEN GETS PASSED". A chat started on a Cloud machine never replied:
the launcher fell back to an empty team plane ("Not logged in"), and the
personal-login transfer ran only as recovery and refused every creator-owned
team machine on its guest vm-kind.
- cloud.startChat installs the person's cached claude setup-token login (one
confirm the first time) before the chat's first prompt; Debug builds or the
cmux.cloud.passHarnessLogin opt-in only.
- CloudCredentialTransfer authorizes on the caller's attach answer
(vmKind == "owner", #28788), as the Codex broker does; the installer no
longer reads the guest's vm-kind.
- agent-auth.sh: no CodeRouter origin means own mode (no-team-plane).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Seen on other PRs too (likely flaky): CI failed on
Failures:
A web test can import code this PR changes without being one of its files: Matched log linesNot re-run automatically: Written by red: web / Web status; a job it gathers is red on this change (its line here); do not merge until green; see lane-rules Default changesThis pull request changes 2 defaults at
Needs a review. It passes with an independent review receipt on this commit: a comment or review whose first line is Merge receipt: merged as Regenerate the snapshot with Shipped in:
|
|
Owned by an agent session · session |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
…ance is the DEV first chat's only Review (gpt-6.1-sol, BLOCK at 1c5016b): teammates on a team machine run as the same Unix user, so the production recovery path keeps requiring an owner guest. Only cloud.startChat's DEV/opt-in login passes creatorTeamMachine. The account is checked again after the login's wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…required Two reviews (gpt-6.1-sol at 1c5016b, gpt-6-astra at 1e2624f) block a personal token on a team guest, where teammates share the Unix user. That allowance moves to its own held PR for Leo's security call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
…ss-login-first-send # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift
|
Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over. Awareness from the Cloud E2E verifier: #29771 fixes the missing Claude login path, but its |
…r origin A machine that persisted only the OpenAI pair has a CodeRouter URL and no CMUX_CODEROUTER_URL. In no-team-plane own mode, a base URL paired with the placeholder key is the model plane's and is dropped, so an own login never reaches that endpoint. A user's own endpoint with their own key stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
The transfer's preflight refuses a guest whose /etc/cmux/vm-kind is not owner (teammates share its Unix user), so a creator's team machine could only end in that refusal after the first-login confirm. Probe the guest's kind first and skip there; team machines are cmux#29774. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
|
Review (codex gpt-6.1-sol xhigh, router-review): BLOCK at e014d05 BLOCK at e014d05 Visual verdict: no images; Leo tries the build
Validation: 26 launcher cases passed through a Node/Babel adapter; shell syntax checks passed. Swift compilation was unavailable. Follow-ups:
|
Automation (debug.dialog) may dismiss it, never press Set Up: the confirm caches and installs a year-long Claude login, as the other credential dialogs already guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
There was a problem hiding this comment.
5 issues found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="web/tests/vm-devbox-agent-auth.test.ts">
<violation number="1" location="web/tests/vm-devbox-agent-auth.test.ts:681">
P2: This setup never exercises the persisted-only model-plane fallback described by the test, so the regression can remain broken while this test passes. Remove both OpenAI variables from `env` so `agent-config.sh` must source `m.modelPlane` before the launch.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/CloudHandlers+Account.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/CloudHandlers+Account.swift:168">
P2: This wizard path has no DEV or opt-in gate, yet the new `callerOwnsVM` argument adds a platform owner check to production credential transfers. Confirm that is intended, or gate it, since the PR describes production as unchanged.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift:108">
P2: The login task is still running when `run()` throws, and Swift waits for it at scope exit. If the copy fails while the `Set Up Claude` confirm is open, the failure toast and the page's error reply wait for the user to answer it. Await the login only on the success path, or cancel it on the failure paths and make `confirmFirstLogin` dismiss its dialog on cancellation, as `presentCodexDeviceCode` does.</violation>
</file>
<file name="web/services/vms/images/devbox/agent-auth.sh">
<violation number="1" location="web/services/vms/images/devbox/agent-auth.sh:189">
P2: The no-origin test in `cmux_agent_drop_model_plane_env` (`-z "$cmux_aa_origin"`) does not match the https-only test in `cmux_agent_auth_resolve`. A non-https `CMUX_CODEROUTER_URL` puts the launch in own mode as `no-team-plane`, but the placeholder-paired base URL rule never applies, so the model-plane URL can survive into own mode. Gate both on the same https check so the no-origin rule covers every case the resolver treats as no-team-plane.</violation>
</file>
<file name="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat+Login.swift">
<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat+Login.swift:54">
P2: `machineLoginProbe` checks the wrong home on classic `/api/vm` machines, so existing Claude logins are missed and may be unnecessarily replaced. Run the probe with the launcher user’s `HOME=/home/cmux`/`CLAUDE_CONFIG_DIR` (or switch to `cmux`) before calling `cmux_agent_has_own_login`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| // ("Not logged in"). With no origin there is no team plane to choose. | ||
| const m = machine(); | ||
| const env = bootEnv(m); | ||
| for (const name of ["CMUX_CODEROUTER_URL", "ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY"]) delete env[name]; |
There was a problem hiding this comment.
P2: This setup never exercises the persisted-only model-plane fallback described by the test, so the regression can remain broken while this test passes. Remove both OpenAI variables from env so agent-config.sh must source m.modelPlane before the launch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At web/tests/vm-devbox-agent-auth.test.ts, line 681:
<comment>This setup never exercises the persisted-only model-plane fallback described by the test, so the regression can remain broken while this test passes. Remove both OpenAI variables from `env` so `agent-config.sh` must source `m.modelPlane` before the launch.</comment>
<file context>
@@ -672,6 +672,47 @@ describe("the default for every agent", () => {
+ // ("Not logged in"). With no origin there is no team plane to choose.
+ const m = machine();
+ const env = bootEnv(m);
+ for (const name of ["CMUX_CODEROUTER_URL", "ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY"]) delete env[name];
+ const result = await runChild("/bin/bash", ["-c", `. ${m.config}; ${m.launchers.claude} --resume x`], { env });
+ expect(result.status).toBe(0);
</file context>
| for (const name of ["CMUX_CODEROUTER_URL", "ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY"]) delete env[name]; | |
| for (const name of ["CMUX_CODEROUTER_URL", "ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "OPENAI_BASE_URL", "OPENAI_API_KEY"]) delete env[name]; |
| try await transfer.run(api: cloud.machineAPI, currentTeamID: { cloud.auth.teamID }, | ||
| currentUserID: { cloud.auth.user?.id }, confirmed: true) | ||
| currentUserID: { cloud.auth.user?.id }, confirmed: true, | ||
| callerOwnsVM: { [machineID = session.machineID] in try await cloud.callerOwnsVM(machineID) }) |
There was a problem hiding this comment.
P2: This wizard path has no DEV or opt-in gate, yet the new callerOwnsVM argument adds a platform owner check to production credential transfers. Confirm that is intended, or gate it, since the PR describes production as unchanged.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Handlers/CloudHandlers+Account.swift, line 168:
<comment>This wizard path has no DEV or opt-in gate, yet the new `callerOwnsVM` argument adds a platform owner check to production credential transfers. Confirm that is intended, or gate it, since the PR describes production as unchanged.</comment>
<file context>
@@ -164,7 +164,8 @@ extension CloudHandlers {
try await transfer.run(api: cloud.machineAPI, currentTeamID: { cloud.auth.teamID },
- currentUserID: { cloud.auth.user?.id }, confirmed: true)
+ currentUserID: { cloud.auth.user?.id }, confirmed: true,
+ callerOwnsVM: { [machineID = session.machineID] in try await cloud.callerOwnsVM(machineID) })
}
}
</file context>
| try Self.checkAccount(account, now: services.cloud.account, listCurrent: services.cloud.hasLoadedCurrentMachines) | ||
| // The chat's harness login (DEV and dogfood builds, +Login): it runs alongside the copy and is | ||
| // awaited before the chat opens, so its first prompt starts signed in. It never fails the start. | ||
| async let loginPassed = passHarnessLogin(start, machine: machine) |
There was a problem hiding this comment.
P2: The login task is still running when run() throws, and Swift waits for it at scope exit. If the copy fails while the Set Up Claude confirm is open, the failure toast and the page's error reply wait for the user to answer it. Await the login only on the success path, or cancel it on the failure paths and make confirmFirstLogin dismiss its dialog on cancellation, as presentCodexDeviceCode does.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat.swift, line 108:
<comment>The login task is still running when `run()` throws, and Swift waits for it at scope exit. If the copy fails while the `Set Up Claude` confirm is open, the failure toast and the page's error reply wait for the user to answer it. Await the login only on the success path, or cancel it on the failure paths and make `confirmFirstLogin` dismiss its dialog on cancellation, as `presentCodexDeviceCode` does.</comment>
<file context>
@@ -103,6 +103,9 @@ struct CloudStartChat {
try Self.checkAccount(account, now: services.cloud.account, listCurrent: services.cloud.hasLoadedCurrentMachines)
+ // The chat's harness login (DEV and dogfood builds, +Login): it runs alongside the copy and is
+ // awaited before the chat opens, so its first prompt starts signed in. It never fails the start.
+ async let loginPassed = passHarnessLogin(start, machine: machine)
let size = AgentPaneCloudStart.sizeText(machine.machine.size)
report(.init(name: "folder", folder: folderName, machine: name, size: size))
</file context>
| fi | ||
| case "${CMUX_CODEROUTER_URL-}" in | ||
| https://*) ;; | ||
| *) cmux_agent_auth_mode=own; cmux_agent_auth_reason=no-team-plane; return 0 ;; |
There was a problem hiding this comment.
P2: The no-origin test in cmux_agent_drop_model_plane_env (-z "$cmux_aa_origin") does not match the https-only test in cmux_agent_auth_resolve. A non-https CMUX_CODEROUTER_URL puts the launch in own mode as no-team-plane, but the placeholder-paired base URL rule never applies, so the model-plane URL can survive into own mode. Gate both on the same https check so the no-origin rule covers every case the resolver treats as no-team-plane.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At web/services/vms/images/devbox/agent-auth.sh, line 189:
<comment>The no-origin test in `cmux_agent_drop_model_plane_env` (`-z "$cmux_aa_origin"`) does not match the https-only test in `cmux_agent_auth_resolve`. A non-https `CMUX_CODEROUTER_URL` puts the launch in own mode as `no-team-plane`, but the placeholder-paired base URL rule never applies, so the model-plane URL can survive into own mode. Gate both on the same https check so the no-origin rule covers every case the resolver treats as no-team-plane.</comment>
<file context>
@@ -180,6 +184,10 @@ cmux_agent_auth_resolve() {
fi
+ case "${CMUX_CODEROUTER_URL-}" in
+ https://*) ;;
+ *) cmux_agent_auth_mode=own; cmux_agent_auth_reason=no-team-plane; return 0 ;;
+ esac
if cmux_agent_team_access "$1"; then cmux_aa_rc=0; else cmux_aa_rc=$?; fi
</file context>
|
|
||
| /// The probe for a login already on the machine: the image's own detection, with its config | ||
| /// sourced as the launcher does. Exits 0 when Claude has its own sign-in there. | ||
| static let machineLoginProbe = ". /etc/cmux/agent-config.sh >/dev/null 2>&1; . /etc/cmux/agent-auth.sh && cmux_agent_has_own_login claude" |
There was a problem hiding this comment.
P2: machineLoginProbe checks the wrong home on classic /api/vm machines, so existing Claude logins are missed and may be unnecessarily replaced. Run the probe with the launcher user’s HOME=/home/cmux/CLAUDE_CONFIG_DIR (or switch to cmux) before calling cmux_agent_has_own_login.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextApp/Cloud/CloudStartChat+Login.swift, line 54:
<comment>`machineLoginProbe` checks the wrong home on classic `/api/vm` machines, so existing Claude logins are missed and may be unnecessarily replaced. Run the probe with the launcher user’s `HOME=/home/cmux`/`CLAUDE_CONFIG_DIR` (or switch to `cmux`) before calling `cmux_agent_has_own_login`.</comment>
<file context>
@@ -0,0 +1,93 @@
+
+ /// The probe for a login already on the machine: the image's own detection, with its config
+ /// sourced as the launcher does. Exits 0 when Claude has its own sign-in there.
+ static let machineLoginProbe = ". /etc/cmux/agent-config.sh >/dev/null 2>&1; . /etc/cmux/agent-auth.sh && cmux_agent_has_own_login claude"
+
+ /// Gives the start's machine the person's Claude login when this build passes it. Returns
</file context>
| static let machineLoginProbe = ". /etc/cmux/agent-config.sh >/dev/null 2>&1; . /etc/cmux/agent-auth.sh && cmux_agent_has_own_login claude" | |
| static let machineLoginProbe = "HOME=/home/cmux CLAUDE_CONFIG_DIR=/home/cmux/.claude; export HOME CLAUDE_CONFIG_DIR; . /etc/cmux/agent-config.sh >/dev/null 2>&1; . /etc/cmux/agent-auth.sh && cmux_agent_has_own_login claude" |
…ss-login-first-send # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextApp/Control/DebugDialog.swift
|
Review (codex, router-review): SHIP at 15f2609 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 2. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at 15f2609 Visual verdict: no images; Leo tries the build
Follow-ups:
|
|
Found 3 test failures on Blacksmith runners: Failures
|
|
Review (codex, router-review): SHIP at da28d10 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 2. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at da28d10 Visual verdict: no images; Leo tries the build
Follow-ups:
|
…(DEV builds) (#29962) * Cloud credential transfer: DEV builds pass the login to the creator's team machine Every machine on a team account has guest kind "team", including one Leo just made, so #29771's first-chat login and the "Transfer and restart" recovery both end in "Personal credentials can only be transferred to a Cloud machine you own." In DEV and dogfood builds (cloud.startChat's existing gate), a team guest whose creator is the caller (the platform's vmKind == "owner" answer, still checked before and after the login) may take the login. Production keeps requiring an owner guest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5 * credential recovery: the restart accepts the creator's team guest under the same DEV allowance Review (router-review, SHIP at 2997fae, P1 follow-up): the transfer installed on a team guest but the restart still required an owner guest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
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):team / team-unknownwith no endpoint and no key, andclaude -pprinted "Not logged in · Please run /login".vm-kindisteam. 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.startChatpasses 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.claude setup-tokenlogin (the Keychain cache from Cloud: confirmed owner-only provider credential transfer #26139), it installs with no prompt.setup-tokenonce in a visible local terminal. Every later machine of the account then gets it silently.cmux.cloud.passHarnessLogindefault. Production keeps today's sign-in design (the terms concern above).CloudCredentialTransfernow 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.startChatchecks 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 Cloud harness login follow-ups from #29771: rebaked image guard, model-plane clobber, Codex first chat #29774.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).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
Agent session: agent session
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith 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-kindisteam.cloud.startChatinstalls the person'sclaude setup-tokenlogin before the chat opens, for chats with a project and chats without one. A cached login installs silently; otherwise one confirm runssetup-tokenonce 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.startChatprobes the guest's ownvm-kindfirst 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.CloudCredentialTransferauthorizes 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.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.passHarnessLoginopt-in. The launcher change awaits an image rebake.Written for commit da28d10. Summary will update on new commits.