Deliver the initial local bridge, Noctalia v4/v5 adapters, desktop client, service unit, tests, and implementation documentation for persistent Pi status and control.
5.1 KiB
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: https://v2.tauri.app/concept/architecture/ and https://v2.tauri.app/develop/tests/webdriver/.
Boundaries
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:
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--toggleare 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
- Conversation (default): selected agent, streaming/queue/tool status, transcript, extension dialog, and one composer.
- Agents: Home plus remembered explicit folders. Selecting a remembered
folder invokes bridge
select_agent; no folder starts merely because it is remembered. - 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
- Add
ui/(React/Vite) andsrc-tauri/; leave current Node bridge intact. - Implement Rust JSONL client plus typed command/snapshot contract with unit tests against a temporary Unix socket server.
- Implement the conversation store and pure reducer tests for event replay.
- Build the conversation screen; add WebDriver E2E for initial load, composer submission, and busy follow-up behavior.
- Build agent/settings/extension screens and tests.
- Replace Noctalia panel entry with a small launcher compatibility shim; retain its compact state indicator.
- Remove the primary Luau panel only after client E2E and manual desktop checks pass.
Acceptance checks
- Launching
pi-status-ui --toggleopens/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.