Voice
Voice commands for window management and the lats voice reference, including speech output forwarded to the Voice app
Voice commands let you control Lattices by speaking. Press Hyper+D to open the voice command window, hold Option to speak, release to stop. Lattices transcribes your speech via its embedded Hudson Voice / Vox runtime, matches it to an intent, and executes it.
The voice domain has two directions with two owners:
| Direction | Verbs | Owner | Transport |
|---|---|---|---|
| Input: voice commands | voice.listen, voice.stopListening, voice.simulate, voice.status |
Lattices menu bar app | lattices.sock, voice.sock |
| Output: spoken text | voice.say, voice.stop, voice.pause, voice.resume, voice.skip, voice.seek, voice.list, voice.select, voice.lease, voice.release |
Voice.app, reached through the Lattices daemon |
lattices.sock, speech.sock |
This page is the reference for both directions of lats voice. Speech output
itself (providers, queue and playback rules, the direct speech.* API) is
documented in the Voice docs.
Embedded voice runtime
Lattices hosts the voice engine inside the menu bar process. You do not
need a separate voxd for normal use. On app boot, Lattices starts an
in-process live-session server and tears it down on quit.
Lattices 0.15.1 and Voice 0.3.0 use private local connections. Install the Lattices app and CLI at the same version.
Private connection
| Service | Endpoint |
|---|---|
| Agent API / daemon | ~/.lattices/run/lattices.sock |
| Voice runtime | ~/.lattices/run/voice.sock |
Voice.app |
~/.lattices/run/speech.sock |
The voice runtime uses Vox's private transport: a Unix socket that accepts only
same-user peers and requires the x-lattices-token header on the WebSocket
upgrade. Hudson Voice connects with the token for you. The capability file at
~/Library/Application Support/Lattices/Voice/hudson-voice-runtime.json
points at the socket. These sockets are local only;
remote voice input is not exposed through the tailnet gateway.
Quick start
- Grant microphone access when prompted (Settings → Voice if you skipped it)
- Optionally connect a voice provider in Settings > Voice for provider-backed interpretation and speech services
- Press Hyper+D to open the voice command window
- Hold Option and speak a command
- Release Option. Lattices transcribes and executes
Voice API
The voice domain covers listening and speaking. The daemon serves
listening. Voice.app speaks: the daemon renames each output verb
to its speech.* method and forwards it to Voice.app through speech.sock. Install
or open Voice.app from the Lattices menu under Apps.
| Verb | CLI | Does |
|---|---|---|
voice.say |
lats voice say "text" |
Speak text. Returns the job id; the job is queued, not finished. |
voice.stop |
lats voice stop |
Stop speaking and clear the queue. |
voice.pause / voice.resume |
lats voice pause / resume |
Pause or resume the current job. |
voice.skip |
lats voice skip |
Skip to the next queued job. |
voice.seek |
lats voice seek <seconds> |
Seek the current job. |
voice.list |
lats voice list |
List voices. |
voice.select |
lats voice select <voice> |
Choose the default voice for a provider. |
voice.lease / voice.release |
none | Hold the speaker so other agents do not talk over you. A lease also ends when the connection closes. |
voice.listen |
lats voice listen |
Start voice capture. |
voice.stopListening |
lats voice stopListening |
Stop voice capture and run the transcript. |
voice.simulate |
lats voice simulate "text" |
Parse and run a command as if spoken. |
voice.status |
lats voice status |
Listening state, plus the Voice helper under helper. |
voice.stop used to stop listening. It now stops speaking. For one
release, a voice.stop made while Lattices is listening and nothing is
speaking fails with voice_stop_changed and points to
voice.stopListening.
If the Voice helper is not installed, the speaking verbs fail with
helper_not_installed and the fix: Lattices › Apps › Install Voice.
If it is installed but not running, they fail with helper_unreachable;
open Voice from Lattices › Apps. There is no fallback. Speaking verbs
need the helper's local capability
(~/Library/Application Support/Speech/RPC/capability), which exists only
while Voice runs; the lats CLI sends it for you, and a client that
does not gets helper_unauthorized. The daemon still accepts the older speech.* names, and
sends each speech.changed event again as voice.changed.
Keyboard shortcuts
| Key | Action |
|---|---|
| Hyper+D | Open/close voice command window |
| ⌥ (hold) | Push-to-talk: hold to record, release to stop |
| Tab | Arm/disarm the mic |
| Escape | Cancel recording or dismiss window |
Built-in commands
Search
Find windows by app name, title, content, or category.
"Find all vox windows"
"Find terminals" → expands to iTerm, Terminal, Warp, etc.
"Show me all browsers" → expands to Safari, Chrome, Firefox, Arc, etc.
"Where is my editor?" → expands to VS Code, Cursor, Xcode, etc.Category synonyms are built in. Saying "terminals", "browsers", "editors", "chat", "music", "mail", or "notes" automatically expands to search for the actual app names.
Tile
Move windows to screen positions.
"Tile this left"
"Snap to the right half"
"Maximize the window"
"Put this in the top right corner"Voice tiling should resolve into the same canonical daemon mutation used
by other agent surfaces: windows.place.
Focus
Bring a window or app to the front.
"Focus Safari"
"Switch to Slack"
"Go to the lats window"Open / Launch
Open applications or project workspaces.
"Open Spotify"
"Launch the vox project"Kill
Close windows or quit applications.
"Kill this window"
"Close Safari"
"Quit Spotify"Scan
Trigger an OCR scan of visible windows.
"Scan the screen"
"Read what's on screen"Other
"List all windows"
"Show my sessions"
"Switch to layer 2"
"Help"AI advisor
Every voice command can ask the selected voice provider for commentary and follow-up suggestions in the AI corner (bottom-right of the voice command window).
When local matching handles the command well, the AI corner shows "no AI needed" with an optional "ask AI" button. When the advisor has something useful, it shows a one-line comment and an actionable suggestion button.
How it works
- You speak a command
- Local intent matching runs immediately (fast, free)
- The selected voice provider runs in parallel when connected
- If the advisor suggests something, a button appears in the AI corner
- Click the suggestion to execute it
- If you engage with a suggestion that the local matcher missed,
it's recorded in
~/.lattices/advisor-learning.jsonlfor future improvement
Hands-off inference
Hands-off voice uses the shared inference wrapper in bin/infer.ts.
By default it chooses the lowest-latency configured provider and, when
Groq credentials are present, uses groq/llama-3.1-8b-instant.
Provider keys are read from the process env (GROQ_API_KEY,
OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY,
XAI_API_KEY, MINIMAX_API_KEY), then from the macOS Keychain under the
service lattices.inference, one account per provider. Lattices does not
read .env files. Store a key without putting it on a command line:
security add-generic-password -U -s lattices.inference -a groq -wEvery variable Lattices reads is declared in .env.schema.
Override the voice engine if needed:
LATTICES_VOICE_PROVIDER=groq
LATTICES_VOICE_MODEL=llama-3.1-8b-instantConfiguration
Open Settings > AI to inspect Scout chat status, and Settings > Voice to configure the optional voice provider:
| Setting | Default | Description |
|---|---|---|
| Workspace chat | Scout | Uses the existing local Scout broker and Scout-owned authorization; Lattices does not store a separate chat key. |
| Voice provider | OpenAI | Optional direct HudsonAI provider used only by provider-backed voice interpretation and speech services. OpenAI, Anthropic, and OpenRouter are supported. |
| Voice provider credentials | Not authenticated | Save an optional voice-only API key locally in the macOS Keychain. |
Layout
The voice command window has four sections:
| Section | Position | Content |
|---|---|---|
| History | Left column | Past commands with expandable details |
| Voice Command | Center column | Current transcript, matched intent, results |
| Log | Top-right | Rolling diagnostic log (last 12 entries) |
| AI Corner | Bottom-right | Advisor commentary, suggestions, provider readiness |
Search architecture
Voice search uses the same backend as lats search:
- Quick search: window titles, app names, session tags (instant)
- Complete search: adds terminal cwd/processes + OCR content
- Synonym expansion: category terms like "terminals" expand to actual app names before searching
- Query cleanup: strips natural language qualifiers ("and sort by...", "please", "for me") before searching
Processing resilience
- 15-second timeout: if processing doesn't complete, returns to idle
- Cancellation on dismiss: closing the window cancels in-flight work
- Double-execution prevention: streaming and stop callbacks can't both fire the intent
Advisor learning
When the local matcher fails but the AI advisor suggests something that you engage with, the interaction is recorded:
~/.lattices/advisor-learning.jsonlEach line is a JSON object:
{
"timestamp": "2026-03-15T18:30:00.000Z",
"transcript": "find all terminals",
"localIntent": "search",
"localSlots": {"query": "terminals"},
"localResultCount": 0,
"advisorIntent": "search",
"advisorSlots": {"query": "iterm"},
"advisorLabel": "Search iTerm"
}This dataset captures where the local system falls short and what the right answer was. Future work can mine it for automatic synonym mappings and phrase pattern improvements.
Requirements
- Embedded voice runtime: the in-process Vox runtime on
voice.sockprovides speech-to-text. It ships inside builds that include Hudson Voice; no separatevoxdinstall is needed - Microphone permission: for voice capture
- Voice provider: enables provider-backed AI suggestions from Settings > Voice (optional, voice commands still work without it)
- Accessibility permission: for window tiling and focus
- Screen Recording permission: for window discovery
Voice.app: only for the output verbs such aslats voice say. See Voice quickstart