P1 — migrate CLI and desktop surfaces onto the daemon client with reconnect and session continuity #133

Closed
opened 2026-08-21 23:42:15 +00:00 by lost-rob0t · 1 comment
lost-rob0t commented 2026-08-21 23:42:15 +00:00 (Migrated from github.com)

Parent epic: #127
Depends on: #129, #130, #131
Voice path additionally depends on: #132
Research: docs/research/001-daemon-zeromq-voice-service.md

Goal

Make Zara's supported user-facing surfaces consume one reusable daemon client library rather than starting duplicate assistant runtimes, while preserving an explicit standalone fallback during migration.

CLI behavior

Define and implement:

zara --connect <endpoint> ...
zara --standalone ...
zara ...                 # daemon-capable default after parity gate

Expected daemon-capable default flow when enabled:

  1. resolve configured endpoint;
  2. load client identity/server pin;
  3. perform bounded connect + hello handshake;
  4. select/resume the correct principal session/conversation;
  5. execute through the shared daemon client;
  6. surface a clear disconnected/auth/protocol error when unavailable;
  7. use standalone fallback only according to an explicit configured policy.

Never silently start a second mic-owning runtime when another daemon may already own the assistant service.

Desktop integration

  • desktop Quick Copilot/Full Chat continue using existing conversation models and runtime events;
  • replace in-process runtime ownership with an adapter backed by the shared daemon client library;
  • do not create desktop-specific wire message types;
  • keep Qt main thread free of blocking ZeroMQ/provider/Prolog/audio work;
  • desktop tray shows connected/authenticating/reconnecting/degraded/disconnected state;
  • existing tool approval/context/voice controls map to canonical daemon commands/events.

Reconnect/session continuity

  • bounded exponential reconnect/backoff;
  • reconnect does not duplicate submitted side effects;
  • durable conversation survives daemon/client restart;
  • client may request session.resume where the negotiated protocol supports it;
  • in-flight audio/model turn is not falsely represented as resumed unless daemon explicitly confirms it;
  • stale events from an old connection/session cannot mutate the active conversation;
  • explicit user-facing distinction between transport reconnect and conversation continuity.

Identity UX

  • client/device key enrollment/configuration is shared across CLI/desktop library boundaries;
  • selecting another user/principal requires actual credentials/key material, not editing a user_id string;
  • diagnostics show safe principal/device/session identifiers without private keys/secrets.

Background behavior

  • daemon remains alive when normal desktop windows close;
  • client can disconnect without killing daemon runtime unless explicitly authorized to request daemon shutdown;
  • wake/background voice behavior uses the local client audio edge from #132;
  • daemon administrative shutdown/restart is a separately authorized action.

Tests

  • CLI text turn through daemon;
  • explicit standalone mode remains functional during migration;
  • missing daemon and configured fallback policies;
  • auth/protocol mismatch rendering;
  • desktop adapter receives the same event semantics as current RuntimeHost-backed UI;
  • repeated reconnect does not duplicate messages/tool side effects;
  • daemon restart preserves durable conversation;
  • stale old-session events ignored;
  • two clients for one principal and two distinct principals behave according to defined session policy;
  • closing desktop leaves daemon alive;
  • client disconnect releases audio/device resources;
  • focused client migration script plus full repo/Nix gate.

Acceptance

CLI and desktop can use the same authenticated daemon/client service boundary with reliable reconnect and durable conversation continuity. No supported client surface needs a private parallel provider/Prolog/memory runtime.

Non-goals

  • no removal of standalone mode until #134 proves parity;
  • no separate Qt-specific networking protocol;
  • no implicit user impersonation based on a string/flag;
  • no client authority to shut down the daemon unless explicitly granted.
Parent epic: #127 Depends on: #129, #130, #131 Voice path additionally depends on: #132 Research: `docs/research/001-daemon-zeromq-voice-service.md` ## Goal Make Zara's supported user-facing surfaces consume one reusable daemon client library rather than starting duplicate assistant runtimes, while preserving an explicit standalone fallback during migration. ## CLI behavior Define and implement: ```text zara --connect <endpoint> ... zara --standalone ... zara ... # daemon-capable default after parity gate ``` Expected daemon-capable default flow when enabled: 1. resolve configured endpoint; 2. load client identity/server pin; 3. perform bounded connect + `hello` handshake; 4. select/resume the correct principal session/conversation; 5. execute through the shared daemon client; 6. surface a clear disconnected/auth/protocol error when unavailable; 7. use standalone fallback only according to an explicit configured policy. Never silently start a second mic-owning runtime when another daemon may already own the assistant service. ## Desktop integration - desktop Quick Copilot/Full Chat continue using existing conversation models and runtime events; - replace in-process runtime ownership with an adapter backed by the shared daemon client library; - do not create desktop-specific wire message types; - keep Qt main thread free of blocking ZeroMQ/provider/Prolog/audio work; - desktop tray shows connected/authenticating/reconnecting/degraded/disconnected state; - existing tool approval/context/voice controls map to canonical daemon commands/events. ## Reconnect/session continuity - bounded exponential reconnect/backoff; - reconnect does not duplicate submitted side effects; - durable conversation survives daemon/client restart; - client may request `session.resume` where the negotiated protocol supports it; - in-flight audio/model turn is not falsely represented as resumed unless daemon explicitly confirms it; - stale events from an old connection/session cannot mutate the active conversation; - explicit user-facing distinction between transport reconnect and conversation continuity. ## Identity UX - client/device key enrollment/configuration is shared across CLI/desktop library boundaries; - selecting another user/principal requires actual credentials/key material, not editing a `user_id` string; - diagnostics show safe principal/device/session identifiers without private keys/secrets. ## Background behavior - daemon remains alive when normal desktop windows close; - client can disconnect without killing daemon runtime unless explicitly authorized to request daemon shutdown; - wake/background voice behavior uses the local client audio edge from #132; - daemon administrative shutdown/restart is a separately authorized action. ## Tests - CLI text turn through daemon; - explicit standalone mode remains functional during migration; - missing daemon and configured fallback policies; - auth/protocol mismatch rendering; - desktop adapter receives the same event semantics as current RuntimeHost-backed UI; - repeated reconnect does not duplicate messages/tool side effects; - daemon restart preserves durable conversation; - stale old-session events ignored; - two clients for one principal and two distinct principals behave according to defined session policy; - closing desktop leaves daemon alive; - client disconnect releases audio/device resources; - focused client migration script plus full repo/Nix gate. ## Acceptance CLI and desktop can use the same authenticated daemon/client service boundary with reliable reconnect and durable conversation continuity. No supported client surface needs a private parallel provider/Prolog/memory runtime. ## Non-goals - no removal of standalone mode until #134 proves parity; - no separate Qt-specific networking protocol; - no implicit user impersonation based on a string/flag; - no client authority to shut down the daemon unless explicitly granted.
Owner

Core closeout revalidation on current canonical master after Android-critical security merge 7cb047a390644c7da96cfb4620bfb8d569450fff:

  • historical #133 client migration already proved durable conversation reopen, stale queued pre-reconnect event draining/fencing, bounded/coalesced reconnect, idempotent request replay, and one ZaraClient boundary;
  • regression recovery PR #318 merged as da515c80a2ba18e889f62dc8f1816c8c95249f43, restoring ordinary CLI commands to daemon-client by default with explicit --standalone only;
  • regression recovery PR #319 merged as 16b1eda8dd4a0b1e4a3dba7742787e8a75192347, restoring normal desktop startup to the same daemon-backed ZmqZaraClient boundary;
  • current ZmqZaraClient still preserves selected conversation across reconnect and reopens it only after the replacement handshake succeeds; old queued subscription events are drained before the new generation is exposed;
  • current gateway request replay is keyed by authenticated principal + request id, rejects conflicting replay ids, and only delivers completed receipts/events to the live initiating principal/session route;
  • C5 remains the canonical typed text path: SubmitTurn -> ZARA/1 turn.submit -> RuntimeSupervisor -> assistant/turn runtime events; no private provider/Prolog/memory loop is involved;
  • current master includes production authenticated CURVE/ZAP TCP via #331, and exact candidate fabea77133914976bd0da2373ad9392d42a46a1e passed CI #1138 (scripts/test-all.sh, nix flake check, packaged Zara, Android APK gate, Arch/Ubuntu shared-mic) before merge 7cb047a….

The two regressions that caused #133 to be reopened are therefore landed and current-head compatible. Closing #133 as completed; future newly observed client/session regressions should get a focused reopened iteration/blocker rather than keeping this acceptance issue artificially open.

Core closeout revalidation on current canonical `master` after Android-critical security merge `7cb047a390644c7da96cfb4620bfb8d569450fff`: - historical #133 client migration already proved durable conversation reopen, stale queued pre-reconnect event draining/fencing, bounded/coalesced reconnect, idempotent request replay, and one `ZaraClient` boundary; - regression recovery PR #318 merged as `da515c80a2ba18e889f62dc8f1816c8c95249f43`, restoring ordinary CLI commands to daemon-client by default with explicit `--standalone` only; - regression recovery PR #319 merged as `16b1eda8dd4a0b1e4a3dba7742787e8a75192347`, restoring normal desktop startup to the same daemon-backed `ZmqZaraClient` boundary; - current `ZmqZaraClient` still preserves selected conversation across reconnect and reopens it only after the replacement handshake succeeds; old queued subscription events are drained before the new generation is exposed; - current gateway request replay is keyed by authenticated principal + request id, rejects conflicting replay ids, and only delivers completed receipts/events to the live initiating principal/session route; - C5 remains the canonical typed text path: `SubmitTurn -> ZARA/1 turn.submit -> RuntimeSupervisor -> assistant/turn runtime events`; no private provider/Prolog/memory loop is involved; - current master includes production authenticated CURVE/ZAP TCP via #331, and exact candidate `fabea77133914976bd0da2373ad9392d42a46a1e` passed CI #1138 (`scripts/test-all.sh`, `nix flake check`, packaged Zara, Android APK gate, Arch/Ubuntu shared-mic) before merge `7cb047a…`. The two regressions that caused #133 to be reopened are therefore landed and current-head compatible. Closing #133 as completed; future newly observed client/session regressions should get a focused reopened iteration/blocker rather than keeping this acceptance issue artificially open.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/zara#133
No description provided.