cmux NEXT Session Info: the Remote Control switch is real (iPhone connections)

Merged
TE
6d ago
+558-4

Summary

Leo's words:

  • 2026-10-28: "you don't even have the, like, remote control toggle functionality."
  • 2026-10-31: "WHY THE F*** IS THERE NOTHING THAT FING TELLS ME IF IT'S LOCAL OR IF F" (comparing with Claude desktop's Session Info Environment: This computer, the folder, a Remote Control toggle) and "WHERE IS MY F***ING PARITY".

Before this PR, Session Info's Environment section had a Remote Control row that only the gallery drew. EnvironmentSection.tsx renders it when it gets remoteControl={on,set}, but App.tsx never passed it, and no host method existed. #22643 added the prop with the gallery only, and #23943 left the wiring for a follow-up ("no webview method, no Swift handler"). No PR ever wired it, so nothing was undone.

In Claude desktop, Remote Control lets you pick up a local session from your phone. cmux already has that backend: the iPhone host (IPhoneHostService, the bundled cmux-mobile-host) lets cmux on the same account's iPhone open this Mac's workspaces, terminals and agent chats while mobile.allowIPhoneConnections is on. The Mobile Pairing page and Settings > Mobile already show that switch. This PR binds the Session Info switch to it:

  • Native (AgentPaneRemoteControl): sends the page a remoteControl host event ({on}, or null) at load and handshake and again on every change (observing the setting, the Cloud account and the host). It also adds pane.setRemoteControl {on}, which writes the setting as the person's own change (by: .user). The write only goes through on a real gesture: the legacy bridge consumes the click credit, and the shared page host refuses a call with no gesture context (userOnly). It is also refused for a chat on another machine and while the row is hidden.
  • App (AgentPaneRemoteControl+App.swift): shows the row only while an account is signed in and this build bundles hostd (IPhoneHostService.servesPhones, now an observed property set by wire).
  • Page: remoteControl.ts keeps the pushed state. pageHost.ts applies the event, and App.tsx passes {on, set} through useChatInspector for a chat on this Mac only, so Quick Chat, peer chats, Cloud chats and Macs that cannot serve phones get no row, never a dead click. A click shows the new value right away and puts it back if the write is refused.
  • The tooltip says the switch covers every chat on this Mac. It is a new string with 21 locales: en is translated, the rest are marked needs_review.

[taste] The switch is Mac-wide, not per chat. I checked the iPhone host: it has no per-session exposure (phones list every workspace through the daemon). So the switch is bound to the Mac-wide iPhone connections setting, and the tooltip says it covers every chat on this Mac. A per-chat version would need the host and cmux-mobile-host to filter what phones see by session. That can follow as its own PR.

Layout untouched: the row's placement and the local vs remote machine label belong to ux-tabs-panes-r2 (#28744, #28885). This PR only supplies the data and the setter (plus the tooltip attribute on the existing switch).

Specs: leo-workspace design/session-info.md (its parity table lists "Remote Control toggle: #22643 prop and gallery only; #23943 deferred the wiring; never shown in the app; wire to the existing phone-access host") and design/session-panel.md. No "Must never" rule is touched: no title row and no Save button.

Testing

  • Packages/macOS/CmuxNext/Tests/CmuxNextAgentPaneTests/AgentPaneRemoteControlTests.swift: the request accepts one Bool and nothing else. The event carries the state or null. A click with no gesture writes nothing and a click with one writes once. A missing switch or a remote chat writes nothing. A script call through the shared page host is refused as userOnly.
  • webviews/src/agent-session/acpmux/remoteControl.test.ts: the store and its parsing, a click calling pane.setRemoteControl, a refused write rolling back, and no call when there is no switch.
  • Gallery: agent-pane.chat-inspector gets environment-remote-control (off) and environment-remote-control-on.

Observed / Not run

  • Ran: prepush-gate.py (no new violations); fleet compile-check --app passed at 0870a9b (53 s); bun run check on the Linux pool: every piece of this PR passed (type check: no errors in 3389 files); its two reds are base reds in files this PR does not touch (check:browser-lanes on test/composer-draft-overflow.test.ts:434, check:type-scale on pages/apps/styles.css). Results at the review-fix head follow in a comment.
  • Review follow-ups fixed at 1d2e4fc: the legacy (non page-host) agent pane now gets the switch through cmuxAcpmuxBridge.applyRemoteControl at its handshake and on every change; overlapping refused clicks roll back to the App's last pushed value (tests added for both).
  • Not run: a tagged-build click on a signed-in Mac with a paired iPhone.

Changelog

Added: Session Info's Environment section shows a Remote Control switch for chats on this Mac; it turns on iPhone connections so cmux on your iPhone can open this Mac's chats.

Proof

Gallery: agent-pane.chat-inspector/environment-folders, agent-pane.chat-inspector/environment-remote-control, agent-pane.chat-inspector/environment-remote-control-on

Before/after (Linux gallery, synthetic data): https://cmux-lawrences-mac-mini.tail137216.ts.net:18796/compare/feat-session-info-remote-control/?entry=agent-pane.chat-inspector. Before: the app never drew the row (environment-folders). After: environment-remote-control and environment-remote-control-on. Light and dark stills to follow in a comment.

Review and visual check

  • Review (codex gpt-6-astra high): SHIP at 774c7e2c796d2f7e4947f168ad61dfe0bd83e790. Follow-ups: #29139 (first observed change can be dropped). The per-chat version from the [taste] note is #29140.
  • Viewed after image: yes. The switch is 36x20 with a 16px knob and uses ds-switch colours: a text-colour track and a surface knob when on. Checked in Apple System Colors light and dark, Dracula and Nord, at 2x from the Linux gallery.
  • Head checks: fleet app compile passed at 774c7e2; webview tsc, remoteControl.test.ts (5/5) and fmt pass. The only reds are release-notary and its Guard status wrapper, both red on base.

Agent session: agent session

šŸ¤– Generated with Claude Code