Skip to content

Cloud: a machine's creator can start chats on it from the app, team machines included - #29107

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
leo/owner-machine-chat
Oct 31, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
leo/owner-machine-chat

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-31, on a chat on his own Cloud machine refused with "Agent chats cannot be started from this Mac on a team VM yet.": "let's do option 1" / "but what's the sharing flow? we can't simply block always. we need to be able to get stuff running asap."

Why

In the DEV build on production, Leo picked his own machine (fs-firstsend-exercise, which he created) and sent a chat. It was refused with agent_session.team_vm_blocked. Every machine on his account sits on a Stack team, so cmux-host writes team to /etc/cmux/vm-kind. cmux-tui's start gate refused a team record before it read the caller's word.

The caller's word is already right. Since #28788 (main) and #28789, the Cloud API's attach answer is vmKind: "owner" only for the machine's creator, team machines included. Every other teammate gets team. The app sends vm_kind: "owner" per start only on that answer (#26323). The guest's team record was the only thing still blocking.

What changes

AgentSessionStarts::host_gate (cmux-tui-core): a team VM now lets a call through on the caller's vm_kind: "owner", the same as any other Cloud VM. The same gate guards three calls: agent-session-start, agent-session-trust and the chat move-in (agent-transfer-receive/-finish).

What still refuses:

  • On a team VM, a caller without the owner word (a teammate, or no word at all) still gets agent_session.team_vm_blocked.
  • On any other Cloud VM, a caller without the owner word still gets agent_session.host_unverified.
  • A word other than owner is still refused on any host.
  • The guest's own records still never allow a call by themselves.

No new capability: the param and its meaning are unchanged; only the team record no longer overrides it. The app bundles and pushes its own cmux-tui to the VM, so the next DEV or nightly build carries this. Nothing to deploy.

Risk: the creator's own chat on a team machine now runs there. The VM boundary (unsandboxed at the VM) already followed that same owner answer (#28788), so this adds nothing new on that axis. Teammates, the shared case #26323 protects, still can't start chats there; that goes to the grant flow (a later PR: "Share machine…", with grants in the attach answer).

Prior art: #26323 (the boundary), #26330, #28479 and #28788 (the owner answer for the creator on a team), #28613 (open, Codex on owner VMs; it doesn't touch host_gate's team arm).

Which link

The app's default link is classic (CloudConfiguration.linkSource = .legacy: /api/vm/{id}/attach-endpoint, web deployed from main), and there #28788 answers owner to the creator. That is Leo's DEV build, so this PR is all his case needs. The app-server link (CMUX_NEXT_CLOUD_LINK=app, cmux-api callerVmKind) still answers team to a shared-team creator until #28789 (open, another lane) lands. Until then that path stays refused, as it is today.

Review: router-review SHIP at 8ec04cd (gpt-6.1-sol). Its one follow-up (P1) is the app-server link above, which #28789 covers.

Tests

  • a_call_on_a_cloud_vm_needs_the_callers_owner_word (unit): a team VM with the owner word goes on; without it, it is refused.
  • a_cloud_vm_start_needs_the_callers_owner_word_not_the_vms_record (CLI, real daemon with a team record): owner gets past the host gate (next comes untrusted_folder); no word and team give team_vm_blocked.
  • agent_session_trust_on_a_cloud_vm_needs_the_callers_owner_word: the same on a team VM for trust.
  • Linux fleet (cmux-ci test --setup rust, job 1382a4dc7688347c089972e8, at 8ec04cd): cargo fmt --check; core agent_session_start (1) and agent_transfer (48); CLI agent_session_st* (37) and agent_session_trust (2). All pass.

No UI change.

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

Allows a machine's creator to start chats on their own team Cloud VM from the app; previously the start gate refused it with agent_session.team_vm_blocked even when the caller sent the correct vm_kind: "owner" word.

  • The host gate for agent-session-start, agent-session-trust, and chat move-in now honors the caller's vm_kind: "owner" on team VMs, matching the Cloud API's per-caller answer that grants owner to the machine's creator only.
  • Teammates and callers without the owner word are still refused: agent_session.team_vm_blocked on team VMs, agent_session.host_unverified on other Cloud VMs.
  • No capability change or deploy needed; the next DEV or nightly build carries the fix.

Written for commit f14d2ba. Summary will update on new commits.

View guided diff Turn on auto-fix

… API's owner word

The start, folder trust and chat move-in gate refused a team VM before it
read the caller's vm_kind. Since #28788 the Cloud API answers vmKind owner
per caller for a machine's creator only, so that word now lets the creator
in on a team VM; every other caller still gets agent_session.team_vm_blocked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 8ec04cd: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr29107 -f ref=leo/owner-machine-chat -f platform=macos -f backend_mode=local

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)/
test_a_picked_signing_label_has_a_rerun_alternative (main.RunsOnFallbackTests.test_
a_picked_signing_label_has_a_rerun_alternative)
View Logs
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)/
test_every_listed_hook_is_one_glaeda_released_with_the_check (main.SigningHooksInSt
epWithGlaedaTests.test_every_listed_hook_is_one_glaeda_released_with_the_check)
View Logs
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)/
test_no_runs_on_chain_ends_on_the_reserved_side_label (main.RunsOnFallbackTests.tes
t_no_runs_on_chain_ends_on_the_reserved_side_label)
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at f14d2ba

Reviewer: codex gpt-6-astra high, fast tier.

Follow-ups: 1. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward.

SHIP at f14d2ba

Visual verdict: no images; Leo tries the build.

  1. [P1][follow-up] The app-server link still reports vm_kind: "team" for a creator of a shared-team VM (tree/backend/apps/api/src/cloud-connect.ts:53), so the app omits the owner word and the new TeamVm gate continues returning agent_session.team_vm_blocked. This path remains unsupported until Cloud: the machine creator is owner on shared teams (next) #28789.

Follow-ups:

  • [P1][follow-up] Make the app-server link return the creator’s owner answer for shared-team machines.

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 31, 2026
@teamleaderleo
teamleaderleo merged commit c8af668 into feat-cmux-next Oct 31, 2026
282 of 302 checks passed
@teamleaderleo
teamleaderleo deleted the leo/owner-machine-chat branch October 31, 2026 09:06
teamleaderleo added a commit that referenced this pull request Oct 31, 2026
… still refuses Codex there

After #29107 a team VM passes host_gate on vm_kind owner, so a Codex or
opencode start there is refused by the family check (not_asking), not
team_vm_blocked. Without the word it is still team_vm_blocked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c
cmux-bot Bot added a commit that referenced this pull request Oct 31, 2026
* test: Codex and opencode start and send on the person's own Cloud VM only (red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* Cloud: Codex and opencode chats run on the person's own Cloud VM

On a Cloud VM with no team evidence and the caller's vm_kind owner (the
Cloud API's answer on the link), agent-session-start skips the
refused-family check and the asking-mode settle, so Codex and opencode
start in their default mode with policy ask
(agent-session-start-owner-cloud-v1). The app's remote wire names
ownerCloud in initialize while the link's owner answer holds, and the
composer lets those chats send. A Mac, an SSH host, a team VM, a remote
browser and paired devices keep D10's refusal; acpmux's Web rules are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* test: drop an unused frame list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* gallery: a Codex chat on a team Cloud machine and on the person's own

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* webviews: format remoteComposer's signature

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* Cloud owner chats: the owner answer's wire closure lives in CmuxNextAgentPane, not the App

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* Cloud: the link owner answer's closure moves from the App to CloudLinkSession

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* Cloud owner chats: list agent-session-start-owner-cloud-v1 in the daemon capabilities export

The export (plans/cmux-next/daemon-capabilities.json) is generated from
DaemonEndpoint.swift by check-daemon-capabilities-fresh.py --write; the
"Daemon capabilities export is fresh" check was red on the new capability.
Also rustfmt the server.rs re-export list the capability joined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* Cloud owner chats: advertise agent-session-start-owner-cloud-v1 only on an owner Cloud VM

The daemon advertised the capability on every host that serves
agent-session-start, so the app's ownerCloud rested on the Cloud API's owner
answer alone for sends to an attached Codex or opencode session. Now the
daemon advertises it only where its own host read is a Cloud VM with no team
evidence (OwnerCloud/UnknownCloud), so a Mac, an SSH host or a VM with team
evidence never lets such a chat send, whatever the Cloud API says.

agent_session_start_starts_codex_on_the_callers_own_cloud_vm_only now also
checks that the SSH and team daemons do not advertise it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* RemoteAcpmuxOwnerCloudTests: build the owner closures outside #require

The swift-test job failed to compile the suite (run 41164536291, job
124435466356): #require's expansion passed the literal closures on without
the parameter's @sendable ("passing non-Sendable parameter '$1' to function
expecting a '@sendable' closure" at lines 55 and 57). The closures are now
typed @sendable values and each call's answer is computed before the macro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* cloud: owner-cloud start is a Cloud-only capability, not a DaemonCapability case

The bundled-daemon probe (check-daemon-capabilities.sh) starts a Mac daemon,
which never serves agent-session-start-owner-cloud-v1: only a Cloud VM with no
team evidence advertises it. Like fs-v1, the app now gates on a constant
(RemoteAcpmuxWire.ownerCloudCapability) listed in CONDITIONAL, so the probe
no longer requires it of the Mac daemon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* cmux-tui test: a team VM takes the creator's owner word (#29107), and still refuses Codex there

After #29107 a team VM passes host_gate on vm_kind owner, so a Codex or
opencode start there is refused by the family check (not_asking), not
team_vm_blocked. Without the word it is still team_vm_blocked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

* arch-ratchet: allow #28613's 3 App lines of owner-Cloud wiring

Base's #29365 moved the owner-answer helper out of CmuxNextApp itself
(CloudMachineLinks), which took away this PR's own offset. What's left is
AgentTabRemoteHost.ownerCloud and the one wiring line that builds it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-515e927c20142b82a32c

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
teamleaderleo added a commit that referenced this pull request Oct 31, 2026
…hine and starts once more (#29497)

* cmux NEXT Cloud: a first chat an older daemon refused updates the machine and starts once more

A fresh Cloud machine boots its image's cmux-tui (images/cmux-vm pins 308bd36, from
before #29107), so its daemon refuses its creator's chat on a team VM with
agent_session.team_vm_blocked. The app's automatic update of that machine runs in the
background from its handshake, so a chat started right after the machine came up
(cloud.startChat, cmux cloud start-session, the first send) reached the old daemon.

Now such a refusal (team_vm_blocked or host_unverified) on the caller's own machine (the
link's owner answer) runs the same update at the front of the shared DaemonUpdatePolicy
queue (DaemonUpdatePolicy.updateNow, joining an update of that machine that already
runs), waits for its turn to end, and starts once more. A teammate's machine is never
retried; any other refusal stands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5

* DaemonUpdatePolicy.updateNow: a capped wait stream; an explicit request is due now and outlasts a turn that never reached its update

Review of #29497 (follow-ups #29498, #29499) and the concurrency gate's unbounded AsyncStream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5

* cmux NEXT Cloud: updateNow uses the injected policy; joining a running update never runs it twice

Review of #29497 at 872a507: #28271 removed DaemonUpdatePolicy.shared (the
policy is services.daemonUpdates.policy), and a refusal that arrived while the
machine's update was inside target.update re-marked it, so a failed update ran
a second time. The policy now marks the target inside its update and updateNow
only waits for that turn.

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>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
austinywang added a commit that referenced this pull request Nov 1, 2026
Keep explicit folder-trust coverage in HOME/project, since the base now
auto-trusts an owner's HOME/work (#29556). Verify the creator can start
a chat on a team machine (#29107), including the machine session and
store binding. Refuse missing owner answers on both plain and team hosts.

Reproduced at 23adfaf via cmux-ci
step 64a6dadbad022f88b748f474:
scripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext CloudAgentChatTests,CloudLinkReconnectingTests,PlacementDocumentWriteOrderTests
Cloud chat failed 3/4 due to these stale expectations; reconnect passed
5/5. Placement's pre-operation ordering assertion also fails independently.

Scoped syntax, localization defaults, feature flags and namespace lint
pass. The workspace package-group finding is unchanged base debt.
Exact-head fleet verification follows the normal push; no local builds.
lawrencecchen pushed a commit that referenced this pull request Nov 1, 2026
…agent on the VM (#25680)

* test(next cloud): an ACP chat in a Cloud workspace runs its agent on the machine (#25583)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: frames collector off the main actor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a reconnected page reattaches to the machine's chat

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the GUI's default first send in a Cloud workspace

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: prove the agent runs with the machine's environment; check the default folder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the machine's HOME is the agent's (acpmux passes no ACPMUX_HOME)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the Cloud machine's link gives the owner answer a chat start needs (#26323)

Since #26323 the app starts a chat on a Cloud machine only on the Cloud API's
vmKind owner answer of the running link. The stand-in machine now runs a
stand-in link whose attach answer says owner; a new case checks that with no
owner answer no chat starts and the page says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(next cloud): an ACP chat in a Cloud workspace runs its agent on the machine (#25583)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: frames collector off the main actor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: a reconnected page reattaches to the machine's chat

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the GUI's default first send in a Cloud workspace

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: prove the agent runs with the machine's environment; check the default folder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the machine's HOME is the agent's (acpmux passes no ACPMUX_HOME)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the Cloud machine's link gives the owner answer a chat start needs (#26323)

Since #26323 the app starts a chat on a Cloud machine only on the Cloud API's
vmKind owner answer of the running link. The stand-in machine now runs a
stand-in link whose attach answer says owner; a new case checks that with no
owner answer no chat starts and the page says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: the stand-in Cloud machine's daemon gates on the owner word; setup always cleans up

Review fixes for the Cloud ACP chat tests:
- the machine's daemon is told it is a Cloud machine of unknown kind
  (CMUX_AGENT_START_HOST=unknown-cloud), so a start and a trust answer run
  there only with the app's vm_kind owner; the no-owner-link case stays on a
  plain host so only the app can refuse it
- the Cloud workspace's terminal starts in the machine's work folder, and the
  no-folder case checks the chat starts there
- both daemons end with the test process (exitWithProcess), and every launch
  sits inside the cleanup scope; this Mac's acpmux home is shut down too
- waits stop on cancellation; an unreadable acp ls answer is an issue, not an
  empty list; refusal text comes from AgentRemoteStartRefusal
- the stand-in link outlives the 2-minute limit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: isolate Cloud chat sidebar document ownership

CloudAgentChatTests binds AppActions and awaits live daemon work, which changes process-wide SidebarDocStore.current. Hold the existing suite ownership gate so concurrent sidebar suites retain their document. Swift syntax and diff checks pass; focused fleet execution follows on the pushed head.

* test: isolate Cloud chat harness startup

Use private homes for both test machines and disable login-shell import, live catalog fetches and unrelated model probes, matching MachineCatalogAppFixture. Login import and catalog reload precede the first remote-start preflight, whose individual RPCs are bounded at five seconds.

Fleet step 411470d1a2e191624d761bfc on 71e8a84 compiled all test targets, then CloudAgentChatTests failed 2/4 with agent_session.timeout before folder-trust refusal. Reconnect tests passed 5/5 and sidebar placement passed 1/1. The same focused Cloud command will be rerun through cmux-ci on this repair; no assertion or production timeout is changed.

* test: report Cloud chat timeout context

* test: follow current Cloud chat owner and folder rules

Keep explicit folder-trust coverage in HOME/project, since the base now
auto-trusts an owner's HOME/work (#29556). Verify the creator can start
a chat on a team machine (#29107), including the machine session and
store binding. Refuse missing owner answers on both plain and team hosts.

Reproduced at 23adfaf via cmux-ci
step 64a6dadbad022f88b748f474:
scripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext CloudAgentChatTests,CloudLinkReconnectingTests,PlacementDocumentWriteOrderTests
Cloud chat failed 3/4 due to these stale expectations; reconnect passed
5/5. Placement's pre-operation ordering assertion also fails independently.

Scoped syntax, localization defaults, feature flags and namespace lint
pass. The workspace package-group finding is unchanged base debt.
Exact-head fleet verification follows the normal push; no local builds.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>

This branch was previously deployed

3 inactive deployments
ci-routing-agent — f14d2ba6 Deployed Oct 31, 2026 by github-actions[bot] via cmux-next macOS placement #53552
artifacts — f14d2ba6 Deployed Oct 31, 2026 by teamleaderleo via publish tree-addressed cmux-tui #88531
sentry-dsyms — f14d2ba6 Deployed Oct 31, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #88531
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant