A mobile-first remote for the pi coding agent.
Run pi --mode rpc on your laptop via a Bun bridge, then monitor and control the session from Safari (or any browser) over Tailscale.
Phone / Browser ── WebSocket ── bridge.ts (Bun) ── pi --mode rpc ── agent
(Tailscale)
bridge.ts is a thin RPC multiplexer:
- Spawns
pi --mode rpcas a child process - Forwards JSONL RPC commands from WebSocket clients to pi
- Broadcasts pi events to all connected clients
- Routes RPC responses back to the requesting client (or broadcasts when needed)
- Serves the static web UI from
public/
On client connect, the bridge bootstraps UI state with:
get_stateget_messagesget_commandsget_available_models
It also sends bridge-side metadata:
- persisted model recents (
prefsmessage) - repo context (
session_info: folder + git branch)
- Live streaming assistant output (
message_update) - Markdown rendering for completed assistant text
- Tool execution cards (start/update/end)
- Collapsible thinking blocks
- Slash command picker (
/) backed byget_commands - Send modes: Prompt, Steer, Follow-up
- Abort current run
- File reference autocomplete with
@...(list_filesbridge command) - Session management (
list_sessions,switch_session,new_session) - Conversation forking (
get_fork_messages,fork) - Export conversation (HTML)
- Manual compact (
compact) - Model picker + recent models persistence
- Thinking level controls (
set_thinking_level) persisted across restarts - Extension UI request/response round-trips
- Optional terminal input passthrough (
prompt,follow_up,abort) - PWA install support (manifest + service worker)
- Optional Web Push notifications on
agent_end - Mobile polish: safe areas, haptics, unread/finish indicator, copy buttons on code blocks
- Image attachment support from the UI
cd ~/repos/pi-remote
bun install# run in current dir
bun run bridge.ts
# or target another repo for the agent working directory
AGENT_CWD=~/repos/monorepo bun run bridge.ts
# custom port (default: 7700)
PORT=8080 bun run bridge.tshttp://<tailscale-ip>:7700
While bridge is running, you can send commands from terminal:
| Input | Behavior |
|---|---|
some text + Enter |
prompt with streamingBehavior: "steer" |
> some text + Enter |
follow_up |
abort + Enter |
abort |
The WebSocket payloads are pi RPC objects plus a few bridge-only message types.
{ "type": "prefs", "recentModels": [{ "id": "...", "name": "...", "provider": "..." }] }
{ "type": "session_info", "folder": "repo-name", "branch": "main" }{ "type": "list_files", "id": "...", "forceRefresh": false }
{ "type": "list_sessions", "id": "..." }{ "type": "response", "command": "list_files", "success": true, "id": "...", "data": { "files": ["README.md"] } }
{ "type": "response", "command": "list_sessions", "success": true, "id": "...", "data": { "sessions": [{ "path": "...", "name": "...", "mtime": 0 }] } }GET /api/push/public-keyPOST /api/push/subscribePOST /api/push/unsubscribePOST /api/push/testGET /api/push/status
pi-remote/
├── bridge.ts
├── public/
│ ├── index.html
│ ├── style.css
│ ├── client.js
│ ├── manifest.json
│ ├── sw.js
│ ├── icon.svg
│ └── icons/
│ ├── bell-ringing.svg
│ └── bell-slash.svg
├── prefs.json
├── push-prefs.json
├── package.json
├── README.md
├── AUTOCOMPLETE.md
├── AUTOCOMPLETE_QUICK_REF.md
├── TROUBLESHOOTING.md
├── IMPLEMENTATION_SUMMARY.md
├── PROJECT_PLAN.md
└── TODO.md
- Preferences are persisted locally in
prefs.jsonandpush-prefs.json. - The bridge exits when the child pi process exits.
- On
agent_end, web push notifications are sent to subscribed clients that are not currently active.
- Pi SDK docs:
~/.bun/install/global/node_modules/@mariozechner/pi-coding-agent/docs/sdk.md - Pi RPC docs:
~/.bun/install/global/node_modules/@mariozechner/pi-coding-agent/docs/rpc.md - Tailscale: https://tailscale.com