Skip to content

feat(voice): Kokoro local-TTS backend, take 2 — TypeScript, zero resident footprint (+ mute toggle & statusline indicator) - #1956

Open
HotSauceHacker wants to merge 2 commits into
danielmiessler:mainfrom
HotSauceHacker:feat/kokoro-local-tts-backend
Open

feat(voice): Kokoro local-TTS backend, take 2 — TypeScript, zero resident footprint (+ mute toggle & statusline indicator)#1956
HotSauceHacker wants to merge 2 commits into
danielmiessler:mainfrom
HotSauceHacker:feat/kokoro-local-tts-backend

Conversation

@HotSauceHacker

Copy link
Copy Markdown
Contributor

What and why

Follow-up to #1465, which was held out of v7.0.0 on dependency footprint (Python/ONNX runtime + a resident LaunchAgent). This resubmission keeps the same opt-in design and removes both concerns:

  • No Python. The daemon is now TypeScript/Bun (kokoro_daemon.ts) on kokoro-js. No pip, no manual model download — the ~90MB quantized ONNX model fetches itself to a local cache on first use.
  • Nothing resident. voice.ts lazy-spawns the daemon on the first utterance; the daemon exits itself after 10 idle minutes (LIFEOS_KOKORO_IDLE_SECONDS). No LaunchAgent, no systemd unit, no manage step.
  • No default-install cost. kokoro-js is deliberately not added to PULSE's package.json; opting in is bun add kokoro-js plus LIFEOS_VOICE_BACKEND=kokoro. With the env var unset, the ElevenLabs path is byte-for-byte unchanged and nothing is installed or spawned.

As before: fully local and private — no API key, no text or audio leaves the machine.

What's included

  • PULSE/VoiceServer/kokoro_daemon.ts — local daemon (GET /health, POST /speak), serialized playback, idle self-exit, env-configurable player for non-macOS (LIFEOS_KOKORO_PLAYER).
  • PULSE/VoiceServer/voice.ts — backend selection (LIFEOS_VOICE_BACKEND=kokoro), lazy daemon spawn with health polling, live mute gate read from voice-mute.json on every notification (fail-open; desktop notifications unaffected).
  • TOOLS/VoiceMute.tson|off|toggle|status CLI, bindable to a hotkey.
  • LIFEOS_StatusLine.sh — 🔊/🔇 glyph in the header, read live from the same state file.
  • DOCUMENTATION/Notifications/KokoroVoiceBackend.md — setup (two steps), env vars, optional skhd hotkey guide.

Tested

  • Daemon: health, synthesis + playback (cold ~4s incl. model download on a fast link, warm ~1s), 400/404/500 paths, idle self-exit, model caching to $KOKORO_CACHE (survives bun install).
  • Lazy spawn from voice.ts: cold start → health poll → utterance retry, end-to-end.
  • Rebased onto current main; statusline conflict with the new LifeOS wordmark + ascent chip resolved in upstream's favor.

HotSauceHacker added 2 commits August 22, 2026 14:32
…ve mute toggle, statusline indicator

Fully-local private alternative to the ElevenLabs path, opt-in via
LIFEOS_VOICE_BACKEND=kokoro. Reworked from PR danielmiessler#1465 to address the
dependency-footprint concern: the daemon is now TypeScript/Bun via
kokoro-js (no Python/pip), lazy-spawned by voice.ts on the first
utterance, and exits itself after an idle timeout (no LaunchAgent,
nothing resident). kokoro-js stays out of PULSE dependencies; opt-in
installs it explicitly.
skhd and similar hotkey daemons run with a minimal PATH, so binding the
bun-based VoiceMute.ts breaks for most installs. voice-mute-toggle.sh
flips the same voice-mute.json state file in pure bash; the setup doc's
skhd example now points at it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant