Files
pi-gui/specs/TAURI_CLIENT_ARCHITECTURE.md
alex b7fea83ed6 feat(bridge): establish local pi status bridge
Deliver the initial local bridge, Noctalia v4/v5 adapters, desktop client, service unit, tests, and implementation documentation for persistent Pi status and control.
2026-07-27 14:51:45 +02:00

5.1 KiB
Raw Permalink Blame History

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 --toggle are routed to the existing instance using Tauris 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.