Cloned-voice vocalization in the TUI: a dankvoice extension over frank-tts #21

Open
opened 2026-10-18 19:40:29 -07:00 by dank · 0 comments
Owner

Problem

Frank's cloned voices reach omp only through the file-producing speech path. The TUI can never speak with them.

  • modelRoles.speech → frank-tts (dankclaw) (:7772) powers the tts tool / xd://tts device and writes a WAV — wired and verified 2026-10-18.
  • The TUI speak-aloud feature (speech.enabled / speech.mode / speech.voice) is a bundled local worker: it calls the bundled ONNX runtime with a hardcoded Kokoro voice enum and never consults models.yml or the speech role (synthesizeStream(voice, speed, signal) in dist/cli.js). [INFERENCE: bundle read, not a runtime trace]
  • The CLI equivalent is gated the same way, and the gate is explicit (verified 2026-10-19): omp say --voice narrator1 "test" -o /tmp/say-a.wav → error: Expected --voice to be one of: af_heart, af_bella, af_nicole, af_kore, af_sarah, am_michael, am_fenrir, am_puck, bf_emma, bm_george, bm_fable. --model is a local TTS model key, so frank-tts is not reachable from omp say either.

So cloned voice + speak-aloud is a feature request against the harness — unless the extension API can carry it.

Mechanism found

~/.omp/agent/extensions/*.ts is auto-discovered and loaded at startup: a probe file dropped there on 2026-10-19 was loaded by a fresh omp -p run (loaded 2026-10-19T02:36:55Z), and omp --help shows --no-extensions disables that discovery (explicit -e/--extension and --hook still load). The extension API exposes exactly the primitives needed: pi.on("agent_end" | "message_end" | …), pi.registerCommand, pi.registerShortcut, ctx.ui.notify, ctx.ui.setStatus, pi.exec — no npm packaging, no lock-file surgery.

Deliverable

A tracked extension agent/extensions/dankvoice.ts that speaks through frank-tts:

  • /say <text> — synthesize and play through the speakers (player detect: pw-play → paplay → ffplay).
  • /say on / /say off — auto-speak the assistant's final text at agent_end; the footer status shows the active voice while armed.
  • /say voice <name> / /say voices — voice selection against the endpoint's own /v1/voices list.
  • Endpoint resolved from agent/models.yml (the provider block carrying api: openai-speech), with DANKVOICE_URL, DANKVOICE_VOICE, DANKVOICE_AUTO overrides. One source of truth, no new secret, no new config file.
  • Markdown is stripped and the text truncated before synthesis — code blocks should not be read aloud.
  • Docs page omp-docs/engineering/dankvoice.md + an entry in omp-docs/engineering/index.md; black-box bats seam (tests/dankvoice.bats).

Out of scope

  • Patching the built-in Kokoro vocalizer (harness code, not ours).
  • Any change to agent/models.yml or to the serverAppConfigs deployment — frank-tts already answers.

cwd: /home/blank/.omp/wt/wt-20261017-181502-6d1fc1f · session: 01a14c93-8870-75d9-9900-9a731559feef

## Problem Frank's cloned voices reach omp only through the file-producing speech path. The TUI can never speak with them. - `modelRoles.speech` → `frank-tts (dankclaw)` (:7772) powers the `tts` tool / `xd://tts` device and writes a WAV — wired and verified 2026-10-18. - The TUI **speak-aloud** feature (`speech.enabled` / `speech.mode` / `speech.voice`) is a bundled local worker: it calls the bundled ONNX runtime with a **hardcoded Kokoro voice enum** and never consults `models.yml` or the speech role (`synthesizeStream(voice, speed, signal)` in `dist/cli.js`). [INFERENCE: bundle read, not a runtime trace] - The CLI equivalent is gated the same way, and the gate is explicit (verified 2026-10-19): `omp say --voice narrator1 "test" -o /tmp/say-a.wav` → `error: Expected --voice to be one of: af_heart, af_bella, af_nicole, af_kore, af_sarah, am_michael, am_fenrir, am_puck, bf_emma, bm_george, bm_fable`. `--model` is a *local* TTS model key, so frank-tts is not reachable from `omp say` either. So cloned voice + speak-aloud is a feature request against the harness — unless the extension API can carry it. ## Mechanism found `~/.omp/agent/extensions/*.ts` is auto-discovered and loaded at startup: a probe file dropped there on 2026-10-19 was loaded by a fresh `omp -p` run (`loaded 2026-10-19T02:36:55Z`), and `omp --help` shows `--no-extensions` disables that discovery (explicit `-e/--extension` and `--hook` still load). The extension API exposes exactly the primitives needed: `pi.on("agent_end" | "message_end" | …)`, `pi.registerCommand`, `pi.registerShortcut`, `ctx.ui.notify`, `ctx.ui.setStatus`, `pi.exec` — no npm packaging, no lock-file surgery. ## Deliverable A tracked extension `agent/extensions/dankvoice.ts` that speaks through frank-tts: - `/say <text>` — synthesize and play through the speakers (player detect: `pw-play` → `paplay` → `ffplay`). - `/say on` / `/say off` — auto-speak the assistant's final text at `agent_end`; the footer status shows the active voice while armed. - `/say voice <name>` / `/say voices` — voice selection against the endpoint's own `/v1/voices` list. - Endpoint resolved from `agent/models.yml` (the provider block carrying `api: openai-speech`), with `DANKVOICE_URL`, `DANKVOICE_VOICE`, `DANKVOICE_AUTO` overrides. One source of truth, no new secret, no new config file. - Markdown is stripped and the text truncated before synthesis — code blocks should not be read aloud. - Docs page `omp-docs/engineering/dankvoice.md` + an entry in `omp-docs/engineering/index.md`; black-box bats seam (`tests/dankvoice.bats`). ## Out of scope - Patching the built-in Kokoro vocalizer (harness code, not ours). - Any change to `agent/models.yml` or to the serverAppConfigs deployment — frank-tts already answers. --- cwd: /home/blank/.omp/wt/wt-20261017-181502-6d1fc1f · session: 01a14c93-8870-75d9-9900-9a731559feef
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dank/command-center#21