--- type: reference title: dankvoice description: Voice output ships as the dank/dankvoice package; this page keeps only the fleet integration facts and the cutover procedure. tags: - omp - extension - tts - voice - frank-tts - dankclaw verified: 2026-10-19 timestamp: 2026-10-19 --- # dankvoice Voice output lives in the **`dank/dankvoice` package**: `pi install git:http://192.168.0.143:3000/dank/dankvoice.git`. This page owns only fleet integration and the cutover. The package README () owns the `/say` command reference, the settings/env knob list, the segmentation state machine, the enhancer port, and mood control — never duplicate those facts here. ## Fleet wiring * **TTS endpoint** — resolved from the provider block whose model row carries `api: openai-speech` in [agent/models.yml](/core/index.md): **frank-tts** on Dankclaw `:7772`. LAN-only — no Caddy vhost fronts it. * **Narration humanizer** — the harness `@tiny` role (`modelRoles.tiny` in `agent/config.yml`), on this fleet **frank-zealot** (Dankclaw `:7771`). * **Hybrid-endpoint trap (load-bearing)** — a raw `fetch` to a thinking-by-default model spends the token budget on `reasoning_content` and answers `content: ""`. The package pins `chat_template_kwargs: { enable_thinking: false }` on the rewrite call; do not remove it. On a hybrid endpoint, empty content beside non-empty `reasoning_content` means the flag was ignored — that is a failed rewrite (fall back to the mechanical text), never the legitimate "nothing to say". * **Voice contract** — omitting `voice` is legal: frank-tts fills its `DEFAULT_VOICE` (2026-10-18 voice-pack work). `GET {base}/v1/audio/voices` answers a bare JSON array; an unknown voice answers 400 with `available_voices`, which the package uses as roster fallback. On this shim `GET /v1/voices` and `GET /v1/models` are 404. ## Cutover (Refs #34) The `agent/extensions/dankvoice.ts` drop-in and its 26-test bats suite are retired here (the package suite is a strict superset; test names ported 1:1). Per box: 1. `pi install git:http://192.168.0.143:3000/dank/dankvoice.git` 2. `/say status`, then "Speak a test phrase" from the `/say` menu — the audible proof a silent session needs. 3. Pull `main` (the drop-in disappears with it) and restart the session. Menu choices survive the cutover: both transports persist to the same gitignored settings file, `~/.omp/cache/dankvoice.json` (`DANKVOICE_CONFIG` relocates it). ## Why not the built-in vocalizer The harness speak-aloud path (`speech.enabled` / `speech.voice`, `omp say`) synthesizes with bundled Kokoro ONNX against a **compile-time voice enum** — cloned voices can never reach it, and no setting routes it to frank-tts. `dankvoice` is a second transport beside it, not a patch: Kokoro stays for quick local speech, the package carries cloned-voice narration. The `tts` tool / `xd://tts` device reach frank-tts too, but produce files, not audio playback. ## Arming auto-speak Arming is **session-scoped by design**: `/say on` arms the running session and is deliberately never persisted. A session that starts silent is a config state, not a fault — set `"auto": true` in `~/.omp/cache/dankvoice.json` (this fleet does) or export `DANKVOICE_AUTO=1` to start armed. Editing the settings file mid-session does nothing; the plugin reads it at startup. Two checks prove the chain, in order: `/say status` must report `auto-speak on` with a real endpoint (not `NO ENDPOINT`), and `pgrep -a pw-play` during a turn must show one player per spoken segment, `--target` set to the saved sink. Thinking narration rides the same arming (`thinking: true`). ## Limits * Auto-speak narrates **each turn** — the assistant's prose and thinking while the agent is still working — plus the settled final answer, spoken once. A turn that lands while audio is still playing is dropped, not queued or interrupting; tool-only turns stay silent; `print`/`json` modes never speak, so benches stay quiet. Narration goes through the humanizer, so it lags its turn by a rewrite round trip; `/say humanize off` removes the lag. * Arming is read at startup only — see **Arming auto-speak** above. A session that never speaks is unarmed, not broken; `/say on` before debugging. * frank-tts deployment, voice packs and the registry are owned by the `serverAppConfigs` repo (`serverAppConfigs-docs/Dankclaw/Frank TTS.md`). ## Citations * Package repo `dank/dankvoice` (README, `docs/decisions/`) — behaviour and knob truth. `docs/decisions/2026-10-19-turn-narration.md` records why narration rides `turn_end` and not only `agent_end`. * Gitea issues #21, #23, #27, #32 (drop-in history) and #34 (this cutover) on `dank/command-center`. * Live endpoint probes 2026-10-18/19 documented in those issues. ## See Also * [Core Documentation](/core/index.md) — the live harness configuration. * [Handy](/tools/handy.md) — offline speech-to-text, the input half of voice.