Two complaints kept coming back after 1.2.0: "it spells my colleague's name wrong every single time" and "Enhance is either on or off — I don't always want the same cleanup."

1.3.0 is out today. It fixes both, and does something about the other recurring annoyance: meeting transcripts where every remote line shows up twice because you weren't wearing headphones.

Here's what changed.

Custom vocabulary

Every speech engine has a set of words it reliably gets wrong for you specifically — a surname, a product name, an internal acronym, the name of the tool you use forty times a day. Parakeet has never heard of your startup. "Claude Code" comes back as "clod code" until you stop noticing and start manually fixing it every time.

Settings now has a Vocabulary section. Each rule is a pair: what the recognizer produces, and what it should have produced. clod code → Claude Code. kubernets → Kubernetes. Sergey Nikolaev instead of whatever it heard.

The details that matter in practice:

  • Whole-word matching by default, so a rule for cat never rewrites the middle of "category". Turn it off for substring fixes.
  • Case-insensitive by default, with a per-rule toggle when you need API to match only API.
  • Per-rule enable switch, so you can park a rule without deleting it.
  • Multi-word phrases work — the left side doesn't have to be a single token.

Rules run in three places, not one. Your terms are fed to the recognizer as a biasing hint before transcription, applied as replacements on the raw transcript afterward, and passed into the LLM prompt as a preserve instruction — so the cleanup pass can't quietly rewrite the spelling you just corrected. That last part was the whole reason the earlier naive version of this feature didn't ship: fixing a name and then having Enhance paraphrase it back is worse than not fixing it.

If you dictate into coding agents, this is the feature to set up first. Ten rules covering your stack's proper nouns removes most of the editing you were doing by hand.

Dictation modes replace the Enhance toggle

Enhance used to be a checkbox: clean up the text, or don't. That's the right default and the wrong ceiling — the same spoken paragraph should come out differently depending on where it's going.

1.3.0 replaces the toggle with a mode picker:

  • Raw — paste exactly what you said, no LLM pass at all. Fastest path; live translation still works here.
  • Cleanup — grammar, punctuation, filler words removed, wording preserved. The old Enhance behavior, and still the default.
  • Email — reshapes the dictation into a structured, professional email without inventing a subject line or a sign-off you didn't speak. See dictating email on Mac for how that reads in practice.
  • Message — concise, casual chat message.
  • Bullets — reorganized into grouped bullet points.
  • Formal — polished, formal tone.
  • Custom — your own instruction, stored in Settings and applied to every dictation.

Custom mode is the interesting one. It's a free-text prompt, so "always write in British English and never use em dashes" or "translate to Spanish and keep it under three sentences" is a valid mode. Everything still runs locally through the on-device model.

Across all modes, numbers, code identifiers, URLs, file paths, quoted strings, and your vocabulary terms are protected from being rewritten. Reshaping should change the prose, not the load-bearing tokens inside it.

Meetings recorded on speakers: the duplicate-lines problem

If you take calls without headphones, the remote participants play out of your Mac's speaker and straight back into your microphone. Vext captures the mic and the system audio as separate streams, so the same sentence gets transcribed twice — once cleanly from the system stream, once badly from the mic — and the transcript shows it as you saying everything the other person just said.

We tried solving this in the audio domain twice. First with a DTLN acoustic echo canceller running on CoreML, then with Apple's own Voice Processing I/O. Both work, and both were removed: VPIO changes shared microphone state, so enabling it bleeds automatic gain control and noise suppression into every other app reading the same mic — your voice gets worse in Zoom while Vext is recording. Not an acceptable trade.

The version that shipped works on the transcript instead. Echo audio is degraded enough to break voiceprint matching, but it says the same words at the same time, and both streams share one meeting clock. So the deduplicator matches text against time: a mic chunk whose tokens are mostly contained in a system chunk within a two-second window is an echo, and it's dropped. A second pass merges the tiny same-speaker fragments that word-boundary splits leave behind.

On top of that, the offline refinement pass now uses the remote speakers' voice embeddings to identify and drop leaked audio, with overlap detection to catch what similarity alone misses.

Net effect: recording a Zoom or Meet call on laptop speakers now produces a transcript close to what you'd get with headphones. Headphones are still better. They're just no longer required.

Word-level speaker splitting

Related, and the structural fix behind a class of transcript weirdness.

Voice activity detection cuts audio into chunks. When two people alternate quickly, a single chunk can contain three or four speaker turns, and the whole thing got transcribed as one block under one label. The offline pass would fix attribution afterward, but the fused text stayed fused.

1.3.0 splits at the token level. Each recognized word carries a timestamp, the diarization timeline says who was speaking at that moment, and the chunk is cut into per-speaker runs before it ever reaches the transcript — with carry-forward gap filling for words the timeline doesn't cover. Alternating speakers come out as separate lines while the meeting is still running.

Speaker recognition that doesn't collapse

Three fixes to speaker identification, all from real recordings that went wrong:

One-to-one matching. Cluster-to-profile assignment was greedy, so two different people could both match the same known speaker and get merged into one identity. It's now a unique mapping — one cluster, one profile.

Stable voice profiles. Embeddings are L2-normalized and blended with an exponential moving average as new samples arrive, instead of being overwritten. Profiles get better with each meeting rather than drifting toward whatever was recorded last.

A collapse guard. A five-person meeting came back from the offline pass as "Me" plus one other person: the offline diarizer under-clustered a 590-segment stream into a single cluster, and because that pass is authoritative, it erased four speakers the live pass had correctly separated. Now, when the offline pass yields one cluster or fewer for a stream where the live pass established two or more speakers, the live labels are kept. Over-segmented speakers are mergeable in the Speakers tab; speakers an authoritative pass deletes are gone forever.

Hotkeys, rebuilt

The hotkey system was rewritten from the storage layer up. Five actions now have independent bindings: push-to-talk dictation, hands-free dictation, notes, meetings, and screenshots.

Assigning a combination that's already in use is detected and resolved instead of silently producing two actions on one key. Any binding can be cleared with the Delete key or a button, and the recorder gives real feedback while it's capturing. The legacy manager, with its scattered AppStorage properties and manual decoding, is gone.

German and Dutch

The interface now ships in seven languages: English, Spanish, German, Dutch, Russian, Hindi, and Thai — plus Auto, which follows your macOS system language. Switch in Settings → General; it applies instantly, no restart.

Design: Liquid Glass on macOS 26

On macOS 26 Tahoe, onboarding, the welcome guide, the license screen, the model loading view, and the recording pill use native Liquid Glass surfaces with adaptive tinting. On earlier macOS versions everything falls back to the previous materials — nothing changes for you.

Animation curves are now shared primitives rather than per-view constants, so transitions feel consistent across the app, and the recording indicators respect the system Reduce Motion setting.

Stability and data fixes

The unglamorous half of the release:

  • Cancelled transcription jobs could busy-spin a core at 100% CPU. Fixed.
  • Editing a meeting title or transcript while background finalization was running could silently revert your edit. Writes are now partial updates that don't clobber user-editable fields.
  • The text processor could mangle legitimate transcript text while removing filler words — hyphenated tokens and punctuation are now protected.
  • The database no longer erases itself on a schema change. That path could destroy meeting history on update; it's disabled.
  • Crashes from invalid VAD ranges and an audio-level race condition are fixed.
  • System audio downsampling uses AVAudioConverter with proper anti-aliasing instead of naive linear interpolation — measurably cleaner input into the recognizer.
  • The clipboard is preserved across text and image paste injections.
  • The audio engine is only allocated when recording actually starts, so idle Vext no longer holds the microphone hardware.
  • Meeting titles and live transcriptions are flushed before app termination.
  • Deleting a speaker now asks first.

Update

brew upgrade muvon/tap/vext

Or download Vext 1.3.0 directly. Your meetings, speaker profiles, and settings carry over — the Enhance toggle maps to Cleanup mode, and your existing hotkeys migrate to the new bindings.

Everything still runs entirely on your Mac: no cloud, no account, no subscription.

Download Vext 1.3.0