Skip to content

Remote Mobile Companion: control agents from your phone (LAN + optional tunnel) #420

Closed

Description

@Juliusolsson05

What

A mobile companion for Agent Code: open a URL on your phone, see every session's live transcript (same custom React rendering as the desktop), send prompts, and answer permission prompts — driving the real agents. LAN by default; optional public tunnel (bundled cloudflared) for use away from the network.

Spec: docs/superpowers/specs/2026-07-06-remote-mobile-companion-design.md
Plan: docs/superpowers/plans/2026-07-06-remote-mobile-companion.md
Integration branch: feat/remote-mobile-companion — all work lands here; merges to main only when the phone-control experience is complete end-to-end.

Architecture in one paragraph

A SessionFeed contract abstracts "how the UI gets session events + sends input." The desktop implements it over Electron IPC (no behavior change); the phone is a second implementation over WebSocket, served by an isolated main-process RemoteServer that subscribes to the same SessionManager event stream the renderer forwarder uses. QR pairing mints revocable per-device HMAC tokens. The v1 wire protocol contains ONLY send-prompt / submit / interrupt / permission-reply — shell exec and session lifecycle literally cannot be expressed.

Isolation boundary (for contributors)

One-way dependency wall: core never imports from src/main/remote/** or src/remote-client/**; deleting the remote tree must leave core compiling. The only holes: the SessionFeed contract in src/shared/sessionFeed/, one construction line in src/main/index.ts, one IPC registration, one feed-selection point per client. Contributors live entirely inside src/main/remote/ + src/remote-client/.

Phases

  • Phase 0 — SessionFeed refactor (in progress): contract + IpcSessionFeed + context; desktop routed through it; zero user-visible change
  • Phase 1 — LAN companion: RemoteServer, QR device pairing, scope-limited WS protocol, phone web client (reuses desktop transcript components), desktop Remote panel
  • Phase 2 — Remote tunnel: bundled cloudflared in third_party/ (tmux/mitmproxy convention), tunnel toggle + ephemeral-URL QR

v1 scope line

Phone can: read all sessions, send prompts, submit/interrupt, answer permission/trust/question prompts.
Phone cannot (by protocol shape): run shell commands, spawn/kill sessions, switch providers.

🤖 Generated with Claude Code

Activity

  1. Juliusolsson05 commented on Jul 6, 2026

    @Juliusolsson05
    OwnerAuthor

    Progress: Phase 0 ✅ + Phase 1 ✅ (LAN companion functional end-to-end in CI)

    All on feat/remote-mobile-companion, every slice TDD'd, tsc-gated, pushed.

    Phase 0 — SessionFeed decoupling (43938b6…6dd1e18): contract in @shared/sessionFeed, desktop routed through IpcSessionFeed, renderer provably runs against a fake feed with no Electron.

    Phase 1 — LAN companion:

    • 1787608 auth core (HMAC secret, durable device registry = revocation authority, QR pairing codes) + v1 wire protocol where the zod union IS the allow-list
    • 2b6f69d SessionFeedSource (parallel manager tap, raw PTY never forwarded) + RemoteTransport seam / LanTransport
    • 420c76a RemoteServer: POST /pair, token at upgrade AND per message, pty actions validated against the live condition's own menu, late-joiner state replay — 11 socket-level integration tests
    • 65b569c desktop surface: RemoteController lifecycle, remote:* IPC, RemotePanel (palette → "Remote Control") with locally-rendered QR + device revoke
    • 6eca4d5 phone client: WebSocketSessionFeed (same SessionFeed contract over WS, capped-backoff reconnect), scan-to-pair via #code fragment, session list with live activity, session view (TUI text + condition tap-targets + composer). 7-case client↔server integration suite pins wire drift.

    Test totals: 65 remote-subsystem tests; 247 repo-wide green; both tsc projects clean.

    Remaining:

    • Interactive smoke on a real phone (desktop app boot is lock-gated by the running instance)
    • Phase 2: bundled cloudflared tunnel (third_party/ convention) + RemotePanel toggle
    • Rendering upgrade: mount the desktop's semantic feed components on the phone (needs a history-request message + window.api-free component audit) — v1 ships the provider's own TUI text, which is always correct
  2. Juliusolsson05 commented on Jul 6, 2026

    @Juliusolsson05
    OwnerAuthor

    All three build phases merged to main in #430 (merge commit cb17d56). Remaining open item tracked here: mount the desktop's semantic feed components on the phone (needs a history-request message + window.api-free component audit); v1 ships the provider's live TUI text.

  3. Juliusolsson05 commented on Jul 6, 2026

    @Juliusolsson05
    OwnerAuthor

    Semantic rendering merged in #438 (cf35658). The phone now renders the real desktop feed with history backfill; the rendering item on this issue is done. Remaining niceties are optional follow-ups (opencode work-indicator on phone, ProviderConditionOutlet adoption).

  4. Juliusolsson05 commented on Jul 9, 2026

    @Juliusolsson05
    OwnerAuthor

    Closing — the phone-control experience is complete end-to-end, which was this umbrella's own merge bar.

    Phase 0 (SessionFeed refactor) ✅ — the SessionFeed contract in src/shared/sessionFeed/ with IpcSessionFeed (desktop) and WebSocketSessionFeed (phone); the desktop routed through it with zero user-visible change. The isolation wall held: core compiles without src/main/remote/** / src/remote-client/**.

    Phase 1 (LAN companion) ✅ — RemoteServer + QR HMAC device pairing, the scope-limited WS protocol (send-prompt / submit / interrupt / condition replies — shell exec and lifecycle remain inexpressible by protocol shape), the phone web client, and the desktop Remote panel.

    Phase 2 (tunnel) ✅ — cloudflared bundled under third_party/ per the tmux/mitmproxy manifest convention, CloudflaredTunnel transport with tests, tunnel toggle.

    Beyond the original scope: the feed rendering was fully reworked to 1:1 desktop parity on the ownership-ledger pipeline (sub-issue #475, closed), and the phone gained voice dictation (token-gated /dictate reusing the desktop STT engine; secure-context + STT-key gating). Capstone PR: #497 (merged 2026-07-09), on top of the feat/remote-mobile-companion integration-branch work that preceded it.

    Known follow-ups live in their own issues: LAN bind/firewall UX (#495 A11 — improved in #507), phone committed-tail plumbing for lastJsonlEntryAt (flagged in the #493 review; minor, ghostless phone path is behavior-equivalent today).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions