cmux NEXT Session Info: the Remote Control switch is real (iPhone connections)
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 aremoteControlhost event ({on}, ornull) at load and handshake and again on every change (observing the setting, the Cloud account and the host). It also addspane.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 bywire). - Page:
remoteControl.tskeeps the pushed state.pageHost.tsapplies the event, andApp.tsxpasses{on, set}throughuseChatInspectorfor 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 asuserOnly.webviews/src/agent-session/acpmux/remoteControl.test.ts: the store and its parsing, a click callingpane.setRemoteControl, a refused write rolling back, and no call when there is no switch.- Gallery:
agent-pane.chat-inspectorgetsenvironment-remote-control(off) andenvironment-remote-control-on.
Observed / Not run
- Ran:
prepush-gate.py(no new violations); fleetcompile-check --apppassed at 0870a9b (53 s);bun run checkon 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-lanesontest/composer-draft-overflow.test.ts:434,check:type-scaleonpages/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.applyRemoteControlat 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