Repository navigation
Description
The platform already knows the words it is mishearing
buildTranscribePrompt(surfaces, extra) (packages/sdk/src/voice/prompt.ts:27) takes an extra
array specifically for "any extra proper nouns, e.g. attached repo names". The one call site
(InstanceDetail.tsx:237) passes:
buildTranscribePrompt(surfaces, instance?.name ? [instance.name] : [])One string. The parameter's own doc-comment names repo names as the use case and repo names are
not passed.
Meanwhile the proper nouns the user actually says out loud are all sitting in the instance, already
fetched by the page:
| source | already available as | words it would supply |
|---|---|---|
| tmux / terminal targets | terminal_list_targets — the Tmux tab lists them |
tmux-operator-runner, platform, session + window names |
| attached coding repos | coding_repos, injected into chat context by agent-think.ts |
heartfull, platform, owner/repo |
| indexed repos (Repo Chat) | repoMember: keys |
repo + owner names |
| subordinate instances | the supervision graph | the names of agents the user says aloud to delegate |
| collections | list_instance_collections |
collection names used in commands |
Every one is a proper noun, which is exactly the category generic ASR mangles and exactly what a
bias prompt is for.
Why this should land before the user-facing list (#373)
It costs no UI, no settings, no migration, and no user effort — and it fixes the reported case. In
#371 the user said tmux and the browser returned Timo; tmux and every session name were
knowable from a call the agent had already made three times in that same conversation.
Asking a user to type tmux into a settings box to fix a word the platform can already enumerate is
the wrong first move. The manual list (#373) should exist for what cannot be derived — a person's
own name, a product, an initialism — not as the primary mechanism.
Notes for implementation
- This is the same one-line class of bug as [bug] "Do it" is transcribed "Duet" and "go, send" becomes "context:" — a tmux agent gets no vocabulary bias, and the divergence guard is off below four words #371's root cause. There,
CODING_TERMSwas skipped
because the gate asks aboutsurfacesand a tmux agent hassurfaces: ["tmux"]. Here,extrais
under-fed. Both are cases of the prompt builder being given less than the caller already holds. - Bound it. The
promptfield is finite and dilution degrades bias — cap the derived list (~30
terms), prefer the most recently used targets/repos, and dedupe against the static term lists so
platformis not sent twice. - Refresh it when it changes. Attaching a repo or creating a tmux target should change the next
turn's prompt; a value captured once at page load goes stale exactly when the user starts saying
the new name. - Split identifiers.
tmux-operator-runnersaid aloud is three words. Send both the raw token
and its split form, or the bias helps the written form and not the spoken one.
Related
#371 (the report), #373 (the user-editable list this precedes).
Activity
feat(voice): a vocabulary the platform derives, and one the user owns (…
serge-ivo commentedon Aug 7, 2026
Fixed in bfbfd29.
workers/api/src/lib/voice-vocabulary.tsderives, per instance: the display name, the agent's name, attachedcoding_repos(name +owner/repo+ the repo half of it) newest-first, and subordinate instance names from the supervision graph. Capped at 20, deduped, ordered most-relevant-first so the cap truncates the tail.Delivered on the voice-settings response, not assembled in the console. That response is already re-fetched on every mic start (
getVoiceConfig, background revalidate), so attaching a repo changes the next turn's bias — which is the refresh requirement in the ticket, and better than a value captured at page load.derivedVocabularyis read-only and is dropped by the sanitizer if echoed back through PUT.Split identifiers:
spokenFormsends both spellings —tmux-operator-runner+tmux operator runner,ProAgentStore/platform+Pro Agent Store platform(camel/Pascal boundaries too). Deduped against the static term lists, soplatformandconsoleare not sent twice.The entry rule, and what was left out. A term qualifies only if it is both likely to be said aloud and likely to be mis-heard. So excluded, though every one is a column away: clone URLs,
workdirpaths, branch names, session ids (enumerable, never spoken in that form); collection and file names (said sometimes, but ordinary words far more often — "notes", "leads", "resume" — and biasing an ordinary word bends real speech toward it).Not done: tmux/terminal targets, which the ticket lists first. They live on the runner, not in D1, so enumerating them means a relay round trip on the voice-config path — the one path #284 spent effort taking round trips out of. The static
TMUX_TERMSlist (#371) covers the vocabulary that was actually failing that agent's turns; live target names can follow if they turn out to be the residual failure.#332 interaction: every term added here is a proper noun, the exact class that echoes back off a near-silent clip.
isTranscribeBiasEchostill catches multi-word echoes only (single words stay unfiltered, or a real "commit" gets swallowed). What makes the trade acceptable is that each multi-token identifier ships its multi-word spoken form beside it, so an echo of either is caught — and that the caps are hard, because a longer list biases worse. A one-word repo or agent name echoed alone remains uncaught; that is the standing price of the bias existing.