You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Remote Mobile Companion: control agents from your phone (LAN + optional tunnel) #420
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.
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
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)
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
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.
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).
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).
Activity
Juliusolsson05 commentedon Jul 6, 2026
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 throughIpcSessionFeed, renderer provably runs against a fake feed with no Electron.Phase 1 — LAN companion:
1787608auth core (HMAC secret, durable device registry = revocation authority, QR pairing codes) + v1 wire protocol where the zod union IS the allow-list2b6f69dSessionFeedSource (parallel manager tap, raw PTY never forwarded) + RemoteTransport seam / LanTransport420c76aRemoteServer: 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 tests65b569cdesktop surface: RemoteController lifecycle,remote:*IPC, RemotePanel (palette → "Remote Control") with locally-rendered QR + device revoke6eca4d5phone client:WebSocketSessionFeed(same SessionFeed contract over WS, capped-backoff reconnect), scan-to-pair via#codefragment, 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:
third_party/convention) + RemotePanel toggleJuliusolsson05 commentedon Jul 6, 2026
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.
Juliusolsson05 commentedon Jul 6, 2026
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).
Juliusolsson05 commentedon Jul 9, 2026
Closing — the phone-control experience is complete end-to-end, which was this umbrella's own merge bar.
Phase 0 (SessionFeed refactor) ✅ — the
SessionFeedcontract insrc/shared/sessionFeed/withIpcSessionFeed(desktop) andWebSocketSessionFeed(phone); the desktop routed through it with zero user-visible change. The isolation wall held: core compiles withoutsrc/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) ✅ —
cloudflaredbundled underthird_party/per the tmux/mitmproxy manifest convention,CloudflaredTunneltransport 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
/dictatereusing the desktop STT engine; secure-context + STT-key gating). Capstone PR: #497 (merged 2026-07-09), on top of thefeat/remote-mobile-companionintegration-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).