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.
## 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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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 thettstool /xd://ttsdevice and writes a WAV — wired and verified 2026-10-18.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 consultsmodels.ymlor the speech role (synthesizeStream(voice, speed, signal)indist/cli.js). [INFERENCE: bundle read, not a runtime trace]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.--modelis a local TTS model key, so frank-tts is not reachable fromomp sayeither.So cloned voice + speak-aloud is a feature request against the harness — unless the extension API can carry it.
Mechanism found
~/.omp/agent/extensions/*.tsis auto-discovered and loaded at startup: a probe file dropped there on 2026-10-19 was loaded by a freshomp -prun (loaded 2026-10-19T02:36:55Z), andomp --helpshows--no-extensionsdisables that discovery (explicit-e/--extensionand--hookstill 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.tsthat 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 atagent_end; the footer status shows the active voice while armed./say voice <name>//say voices— voice selection against the endpoint's own/v1/voiceslist.agent/models.yml(the provider block carryingapi: openai-speech), withDANKVOICE_URL,DANKVOICE_VOICE,DANKVOICE_AUTOoverrides. One source of truth, no new secret, no new config file.omp-docs/engineering/dankvoice.md+ an entry inomp-docs/engineering/index.md; black-box bats seam (tests/dankvoice.bats).Out of scope
agent/models.ymlor to the serverAppConfigs deployment — frank-tts already answers.cwd: /home/blank/.omp/wt/wt-20261017-181502-6d1fc1f · session: 01a14c93-8870-75d9-9900-9a731559feef