P1 — migrate the zara wake listener onto the daemon ZARA/1 voice client with principal-scoped memory #227

Closed
opened 2026-09-02 17:27:43 +00:00 by nsaspy · 0 comments
Owner

Parent epic: #127
Depends on: #134
Builds on: #130 (CURVE/ZAP auth), #131 (principal-scoped memory/conversations), #132 (ZARA/1 live voice, transcript/assistant events, barge-in), #133 (client migration pattern)

Context

The wake listener (zara/wake.py) is the last major surface that still owns a private assistant runtime. Today it runs the whole stack in-process: mic capture, wake word, Silero VAD, faster-whisper STT, its own AgentManager (zara/wake.py:920), its own TTS engine, and its own barge-in logic. The desktop copilot and CLI are already daemon clients via #133, so wake is now the only path with a second brain: separate conversation context, separate memory, no durable persistence, and a duplicated Prolog-first routing stack.

The daemon voice surface from #132 is complete: audio.input.start/chunk/commit/cancel commands, partial/final transcript events, streaming assistant text/audio events, canonical barge-in cancellation, and per-principal isolation on authenticated routes.

Goal

Wake becomes a thin daemon voice client. It keeps only what is physically client-owned — wake word detection, VAD, microphone capture, barge-in capture, and local audio playback — and streams utterances to the daemon over the existing audio.input.* contract. STT transcripts, Prolog-first routing, agent turns, tools, memory, and conversation persistence all happen daemon-side, exactly as they do for the desktop copilot.

Principal gating (multi-user)

  • The listener authenticates to the daemon as a principal; local wake uses the local-owner principal.
  • Every authenticated user account (CURVE identity from the #130 keystore) gets its own conversation, memory, and persistence scope per #131. Wake must never read, write, or observe another principal's memory, conversations, or events.
  • Two wake clients authenticating as different principals on the same daemon must have fully isolated sessions — verified by regression tests, not by convention.
  • Explicitly out of scope: account management UI, registration flows, or new identity APIs. Accounts are authenticated principals; enrollment/keystore lifecycle stays with #130 and the Android enrollment line (#171-#173).

Non-goals

  • No new ZARA/1 message types. Reuse the #132 command/event contract; any gap must be justified against that contract in research.
  • No TTS provider changes: the daemon emits output events; the client renders them with the user's existing [tts] config.
  • No standalone/private-runtime fallback. Follow #133's fallback policy: daemon unavailability fails closed with an explicit, user-facing error — never a silent private runtime.
  • No Android/remote client work (#171-#173) beyond keeping the client surface compatible.

Acceptance criteria

  1. With the daemon running, a spoken wake phrase plus utterance produces: daemon-side final transcript event, Prolog-first resolution for command utterances, agent turns for conversational utterances, and local playback driven by assistant text/audio events. The wake process owns no AgentManager, no STT engine, and no TTS synthesis pipeline.
  2. With the daemon unavailable, wake fails closed per the #133 fallback policy with a clear user-facing error; a test pins the behavior (no silent in-process runtime).
  3. Two authenticated principals using wake concurrently observe strictly isolated conversations, memory, and events; covered by daemon-level regression tests plus store-level principal-scoping tests.
  4. Barge-in during daemon-side TTS/assistant output maps to canonical turn cancellation (audio.input.cancel / runtime cancellation); stale transcript/assistant events after cancellation are filtered client-side.
  5. Latency: wake-to-first-audio and wake-to-ack budgets are preserved on the daemon path and recorded by the existing deterministic latency harness; trace_id continuity from client audio ingress through routing/model/output events is asserted.
  6. The packaged zara-wake binary and the dotfiles systemd unit keep working unchanged (unit points at the same binary; no new required flags).

Verification gate

  • Focused TDD for each criterion, then nix develop -c bash scripts/test-all.sh (ZARA-021) plus nix flake check at the exact candidate head; CI green for the merged SHA.
  • Optional live smoke with a real microphone behind an explicit opt-in script (deterministic gate must not require audio hardware).
  • RAGE iteration per AGENTS.md: research and design artifacts before implementation, work log under rage/ with the consumed issue and start commit.
Parent epic: #127 Depends on: #134 Builds on: #130 (CURVE/ZAP auth), #131 (principal-scoped memory/conversations), #132 (ZARA/1 live voice, transcript/assistant events, barge-in), #133 (client migration pattern) ## Context The wake listener (`zara/wake.py`) is the last major surface that still owns a private assistant runtime. Today it runs the whole stack in-process: mic capture, wake word, Silero VAD, faster-whisper STT, its own `AgentManager` (`zara/wake.py:920`), its own TTS engine, and its own barge-in logic. The desktop copilot and CLI are already daemon clients via #133, so wake is now the only path with a second brain: separate conversation context, separate memory, no durable persistence, and a duplicated Prolog-first routing stack. The daemon voice surface from #132 is complete: `audio.input.start/chunk/commit/cancel` commands, partial/final transcript events, streaming assistant text/audio events, canonical barge-in cancellation, and per-principal isolation on authenticated routes. ## Goal Wake becomes a thin daemon voice client. It keeps only what is physically client-owned — wake word detection, VAD, microphone capture, barge-in capture, and local audio playback — and streams utterances to the daemon over the existing `audio.input.*` contract. STT transcripts, Prolog-first routing, agent turns, tools, memory, and conversation persistence all happen daemon-side, exactly as they do for the desktop copilot. ## Principal gating (multi-user) - The listener authenticates to the daemon as a principal; local wake uses the local-owner principal. - Every authenticated user account (CURVE identity from the #130 keystore) gets its own conversation, memory, and persistence scope per #131. Wake must never read, write, or observe another principal's memory, conversations, or events. - Two wake clients authenticating as different principals on the same daemon must have fully isolated sessions — verified by regression tests, not by convention. - Explicitly out of scope: account management UI, registration flows, or new identity APIs. Accounts are authenticated principals; enrollment/keystore lifecycle stays with #130 and the Android enrollment line (#171-#173). ## Non-goals - No new ZARA/1 message types. Reuse the #132 command/event contract; any gap must be justified against that contract in research. - No TTS provider changes: the daemon emits output events; the client renders them with the user's existing `[tts]` config. - No standalone/private-runtime fallback. Follow #133's fallback policy: daemon unavailability fails closed with an explicit, user-facing error — never a silent private runtime. - No Android/remote client work (#171-#173) beyond keeping the client surface compatible. ## Acceptance criteria 1. With the daemon running, a spoken wake phrase plus utterance produces: daemon-side final transcript event, Prolog-first resolution for command utterances, agent turns for conversational utterances, and local playback driven by assistant text/audio events. The wake process owns no `AgentManager`, no STT engine, and no TTS synthesis pipeline. 2. With the daemon unavailable, wake fails closed per the #133 fallback policy with a clear user-facing error; a test pins the behavior (no silent in-process runtime). 3. Two authenticated principals using wake concurrently observe strictly isolated conversations, memory, and events; covered by daemon-level regression tests plus store-level principal-scoping tests. 4. Barge-in during daemon-side TTS/assistant output maps to canonical turn cancellation (`audio.input.cancel` / runtime cancellation); stale transcript/assistant events after cancellation are filtered client-side. 5. Latency: wake-to-first-audio and wake-to-ack budgets are preserved on the daemon path and recorded by the existing deterministic latency harness; `trace_id` continuity from client audio ingress through routing/model/output events is asserted. 6. The packaged `zara-wake` binary and the dotfiles systemd unit keep working unchanged (unit points at the same binary; no new required flags). ## Verification gate - Focused TDD for each criterion, then `nix develop -c bash scripts/test-all.sh` (ZARA-021) plus `nix flake check` at the exact candidate head; CI green for the merged SHA. - Optional live smoke with a real microphone behind an explicit opt-in script (deterministic gate must not require audio hardware). - RAGE iteration per AGENTS.md: research and design artifacts before implementation, work log under `rage/` with the consumed issue and start commit.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#227
No description provided.