# Tauri Pi Client Architecture ## Purpose Replace the Noctalia panel with a testable, status-bar-agnostic desktop Pi client. Noctalia becomes an optional status indicator and launcher only. ## Decision Use a **Tauri v2 desktop application** with a web frontend and a small Rust host. The bridge remains the only owner of Pi processes, sessions, recovery, and the owner-only Unix socket. Tauri is appropriate because its frontend talks to a native host through typed asynchronous IPC, and its WebDriver support gives the client a real E2E test surface. Reference: and . ## Boundaries ```text Pi Status Bridge systemd service └─ Unix JSONL socket: state, events, commands └─ Tauri Rust host ├─ validates/serializes bridge requests ├─ owns subscribe connections and emits UI events └─ exposes typed Tauri commands └─ React frontend: desktop UI Optional Noctalia compatibility shim ├─ reads compact state through existing client/bridge protocol └─ launches or focuses `pi-status-ui --toggle` ``` ### Security - The frontend cannot run shell commands or open sockets. - Only the Tauri Rust host reads the per-user Unix socket. - The UI never receives or stores bridge lock tokens. - No TCP listener is introduced. - Pi remains authoritative for model, thinking, queues, tool approvals, and extension request IDs. ## Tauri host API The host maps the existing JSONL protocol into typed commands. It must reject unknown operations and preserve agent scoping. | Command | Bridge operation | Result | | --- | --- | --- | | `listAgents` | `list_agents` | current agent summaries | | `selectWorktree` | `select_agent` | explicitly selected agent | | `loadAgent` | state, transcript, commands, models | one snapshot | | `submitPrompt` | `submit_prompt` | Pi-owned prompt/follow-up queueing | | `abort`, `retry`, `restart` | same | command response | | `setModel`, `setThinkingLevel` | same | command response | | `respondToExtension` | `extension_response` | command response | | `subscribeAgent` | `subscribe` | emits `bridge-event` to this window | The Rust host owns reconnection and cursor replay. Its view-model event stream is normalized to: ```ts type BridgeEvent = { agentId: string; seq: number; kind: "state" | "message" | "tool" | "queue" | "extension" | "recovery"; payload: unknown; }; ``` ## Frontend ### Window behavior - One persistent window; close hides it, quit is explicit. - `--show`, `--hide`, and `--toggle` are routed to the existing instance using Tauri’s single-instance capability. - The optional Noctalia widget runs `pi-status-ui --toggle`; it never renders the primary UI or handles bridge commands. ### Screens 1. **Conversation** (default): selected agent, streaming/queue/tool status, transcript, extension dialog, and one composer. 2. **Agents**: Home plus remembered explicit folders. Selecting a remembered folder invokes bridge `select_agent`; no folder starts merely because it is remembered. 3. **Session settings**: model, thinking level, session metadata, retry, and restart. ### Composer invariant The frontend has one normal submit action. It invokes `submit_prompt`; it does not decide between prompt, steering, or follow-up. Slash-command selection inserts text only. ### Extension invariant Render Pi `select`, `confirm`, `input`, and `editor` requests. Present any other method as unsupported without sending an approval. ## Persistence | Data | Owner | | --- | --- | | Pi session/recovery | bridge | | remembered worktree paths + last selected path | UI app data | | bridge agent IDs | memory only; invalid after bridge restart | | compact launcher state | Noctalia only, disposable | At startup the UI resolves its stored path through `select_agent`; it never persists or trusts a bridge agent ID. ## Implementation sequence 1. Add `ui/` (React/Vite) and `src-tauri/`; leave current Node bridge intact. 2. Implement Rust JSONL client plus typed command/snapshot contract with unit tests against a temporary Unix socket server. 3. Implement the conversation store and pure reducer tests for event replay. 4. Build the conversation screen; add WebDriver E2E for initial load, composer submission, and busy follow-up behavior. 5. Build agent/settings/extension screens and tests. 6. Replace Noctalia panel entry with a small launcher compatibility shim; retain its compact state indicator. 7. Remove the primary Luau panel only after client E2E and manual desktop checks pass. ## Acceptance checks - Launching `pi-status-ui --toggle` opens/focuses one window. - Restarting the bridge recovers to Home or a saved worktree path; it never issues a command to a stale agent ID. - A normal submit, busy submit, model change, and extension response route through typed host commands only. - A custom extension UI cannot be approved from the client. - Noctalia can be disabled/uninstalled while the desktop client remains fully usable. - Rust host tests, frontend unit tests, WebDriver E2E, bridge tests, and a manual desktop run pass.