agent-voice-input
Context-aware voice input for CLI coding agents (such as Codex, Claude Code, and Antigravity CLI / agy).
Instead of treating terminal dictation like generic chat input, agent-voice-input pairs fast speech recognition with lightweight refinement grounded in your active coding session—fixing homophone identifiers, mixed Chinese–English technical terms, and spoken self-corrections before pasting safely into the terminal.
Early Release (
v0.2.0): Verified on Windows Terminal + WSL2 (includingtmuxsplit panes and Remote Desktop) and macOS + Ghostty (includingtmux). Broader terminal coverage is actively in progress.
How It Works
- Active Session Context: Locates the foreground agent session—including the active pane inside
tmuxwithout editing~/.tmux.conf(via optional terminal/pane title tags, runtime session registry, Ghostty splittty/cwdmatching on macOS, or/proc/psroot-session scanning while ignoring background subagents and sibling panes)—and incrementally extracts recent dialogue plusgit statusfilenames as context. - Stage 1 — Speech Recognition: Applies a local silence gate (
-50 dBFS) and RMS loudness normalization (-20 dBFS), then transcribes audio via built-in cloud or local-compatible providers (dashscope,doubao,openrouterdual-protocol STT & multimodal audio,gemini,openai,groq,siliconflow, oropenai-compatible). - Stage 2 — Lightweight Context Refinement: Cleans up technical terms, identifier casing, and explicit spoken self-corrections against the extracted session context. Defaults to local Codex CLI (
codexwithgpt-6-luna), and also supportsdashscope,volcengine,openrouter,gemini,openai,groq,siliconflow,openai-compatible, oroff(automatically skipped when Stage 1 already performed single-hop multimodal refinement). - Focus-Safe Paste: Shows recording/recognition/refinement stages in a non-activating top HUD pill (Windows & macOS, automatically matching Chinese or English desktop UI language), locks onto the origin terminal window/split when recording begins, never auto-presses Enter, and falls back to clipboard-only if focus drifted elsewhere.
- Zero Extra Python Dependencies: Uses only the Python 3.10+ standard library plus host
ffmpeg(built-inpowershell.exeon WSL2; cached nativemac_shim.swifthelper via/usr/bin/swiftcon macOS).
Quick Start
1. Prerequisites
- Python
>= 3.10 - ffmpeg:
- Ubuntu / WSL2:
sudo apt install ffmpeg - macOS:
brew install ffmpeg
- Ubuntu / WSL2:
- macOS native hotkey & HUD helper (recommended on macOS): Xcode Command Line Tools (
xcode-select --install) providing/usr/bin/swiftc.mac_shim.swiftis compiled idempotently and cached on first start; ifswiftcis absent,agent-voice-inputautomatically falls back to CLItoggle/oncecommands.
2. Install
uv tool install agent-voice-input
# Or with pipx / pip:
# pipx install agent-voice-input
For local development from source:
git clone https://github.com/xiaohuaxi/agent-voice-input.git ~/repos/agent-voice-input
mkdir -p ~/.local/bin
ln -s ~/repos/agent-voice-input/bin/agent-voice-input ~/.local/bin/agent-voice-input
3. Configure Credentials
Create ~/.config/agent-voice-input/agent-voice-input.env (or export variables in your shell) with at least one provider key—for example, Alibaba Bailian (DASHSCOPE_API_KEY) or a single OPENROUTER_API_KEY covering both stages:
mkdir -p ~/.config/agent-voice-input && chmod 700 ~/.config/agent-voice-input
cat > ~/.config/agent-voice-input/agent-voice-input.env <<'EOF'
DASHSCOPE_API_KEY="<your-dashscope-api-key>"
EOF
chmod 600 ~/.config/agent-voice-input/agent-voice-input.env
To customize hotkeys (trigger_mode = "tap_toggle" or "ptt"), HUD language ([ui] language = "auto" | "zh" | "en", or AGENT_VOICE_INPUT_UI_LANGUAGE), macOS audio input ([audio] macos_device = "default" or AGENT_VOICE_INPUT_MACOS_AUDIO_DEVICE), providers, models, or Stage 2 refinement, copy config.example.toml to ~/.config/agent-voice-input/config.toml (see config.example.env for all credential variables).
4. Verify & Run
# Check audio bridge, mac_shim/win_shim status, resolved providers/models, credentials, and session detection
agent-voice-input doctor
# Start background daemon (defaults to isolated Right Ctrl tap on WSL2 & macOS)
agent-voice-input start
# Other common commands
agent-voice-input status
agent-voice-input toggle
agent-voice-input once --duration 4 --dry-run
agent-voice-input stop
macOS + Ghostty Experience & Permissions
On macOS with Ghostty, agent-voice-input works out of the box without terminal hooks or invasive input permissions:
- Hotkey & Top Capsule HUD: Tap Right Ctrl (
RControl) to toggle recording—or settrigger_mode = "ptt"to hold-to-speak—with a non-activating top HUD pill showing recording, recognition, and refinement progress in Chinese or English matching your desktop locale. - Native Split Targeting & Anti-Drift Guard: Locks onto the exact focused Ghostty split surface when recording starts and injects bracketed-paste text directly into that split without Accessibility synthetic keystrokes; if you switch splits or apps mid-transcription, it safely copies to the clipboard instead of pasting into the wrong target.
- Zero-Hook Split &
tmuxSession Binding: Uses the focused Ghostty split'sttyandcwd(and the attached client's active pane when insidetmux) to match the activecodex,claude, oragysession in that pane, keeping multiple side-by-side splits isolated even without title hooks. - Audio Input Selection: Defaults to
[audio] macos_device = "default"(system default microphone); override viamacos_deviceinconfig.tomlorAGENT_VOICE_INPUT_MACOS_AUDIO_DEVICE(device index or name, inspectable viaagent-voice-input doctor). - macOS Permissions (TCC):
- Microphone: Required for your terminal / background Python environment so
ffmpeg(avfoundation) can record audio. - Automation (Ghostty): Prompted once on first split query / text injection into Ghostty.
- Accessibility & Input Monitoring: Not required for the default
RControl+ Ghostty setup. Only needed when pasting into non-Ghostty apps via simulatedCmd+V(Accessibility) or when binding non-modifier hotkeys / runningagent-voice-input key-test(Input Monitoring / Accessibility).
- Microphone: Required for your terminal / background Python environment so
Configuration & Providers
Configuration uses a clean two-file layout (CLI flags > AGENT_VOICE_INPUT_* env vars > config.toml > defaults):
~/.config/agent-voice-input/config.toml: Structured[hotkey],[ui],[audio],[asr], and[refiner]settings (provider,model,api,effort,base_url,api_key_env, etc.).~/.config/agent-voice-input/agent-voice-input.env: API keys (and optionalAGENT_VOICE_INPUT_*overrides).
Structured [asr] & [refiner] Fields
provider&model:modelalways keeps the upstream model ID verbatim (e.g."qwen/qwen3-asr-1.7b"or"google/gemini-2.5-flash"). Leavemodel = ""to use the provider's built-in default. Whenprovideris overridden at a higher-priority layer (env or CLI) without specifyingmodel, any lower-layermodelis cleared so the new provider's default model applies cleanly.asr.api("auto" | "audio-transcriptions" | "chat-completions"):"auto"(default) uses native adapters fordashscope,doubao, and nativegemini. Onopenrouter,openai,groq,siliconflow, andopenai-compatible, it classifies known dedicated STT models (qwen3-asr,whisper,*-transcribe,sensevoice,deepgram/*,assemblyai/*,elevenlabs/*, etc.) to"audio-transcriptions"+ Stage 2 refinement, and known multimodal audio models (gemini-*,gpt-audio*,gpt-4o*audio*,*-omni*,mimo-*,voxtral-small*, etc.) to"chat-completions"for single-hop transcription + context refinement (skipping Stage 2 by default unlessrefiner.force_on_single_hop = true). Note thatdashscopeqwen3-asr-flashuses a chat-compatible endpoint for pure STT and still runs Stage 2.- When testing a newly released, uncataloged model on
openrouter, setasr.api = "audio-transcriptions"or"chat-completions"explicitly.
asr.effort&refiner.effort: Unified reasoning/thinking control ("auto" | "default" | "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max").asr.effortdefaults to"auto"(suppressing default thinking latency on supported multimodal models such as Gemini 2.5 while omitting reasoning fields on pure STT/non-reasoning audio models);refiner.effortdefaults to"low";"default"omits reasoning parameters completely.
Built-in Providers
provider |
Stages | Default asr.model |
Default refiner.model |
Recommended Credential Env |
|---|---|---|---|---|
dashscope |
asr, refiner |
qwen3-asr-flash |
qwen-turbo-latest |
DASHSCOPE_API_KEY (compat: ALI_API_KEY) |
doubao |
asr |
bigmodel |
— | DOUBAO_ASR_APP_ID + DOUBAO_ASR_API_KEY |
volcengine |
refiner |
— | doubao-seed-1-6-flash |
ARK_API_KEY (compat: VOLCENGINE_API_KEY, HUOSHAN_API_KEY) |
openrouter |
asr, refiner |
qwen/qwen3-asr-1.7b |
google/gemini-2.5-flash-lite |
OPENROUTER_API_KEY |
gemini |
asr, refiner |
gemini-2.5-flash |
gemini-2.5-flash-lite |
GEMINI_API_KEY |
openai |
asr, refiner |
gpt-4o-mini-transcribe |
gpt-4o-mini |
OPENAI_API_KEY |
groq |
asr, refiner |
whisper-large-v3-turbo |
llama-3.3-70b-versatile |
GROQ_API_KEY |
siliconflow |
asr, refiner |
FunAudioLLM/SenseVoiceSmall |
Qwen/Qwen2.5-7B-Instruct |
SILICONFLOW_API_KEY |
codex |
refiner |
— | gpt-6-luna |
Local codex CLI login (never enables Fast mode) |
openai-compatible |
asr, refiner |
gpt-4o-mini-transcribe (needs base_url) |
(needs base_url + model) |
api_key_env or OPENAI_API_KEY (loopback URLs need no key) |
- Special values:
asr.provider = "auto"(default): selects the first configured credential in orderdashscope -> doubao -> openrouter -> gemini -> openai -> groq -> siliconflow.refiner.provider = "off": disables Stage 2 refinement.
- Prefix-style credential aliases (
API_KEY_OPENROUTER,API_KEY_GEMINI,APP_ID_DOUBAO_ASR+API_KEY_DOUBAO_ASR, etc.) are also supported; seeconfig.example.env.
Common Configuration Recipes (config.toml)
Recipe A — Low-Latency Mainland China Setup (dashscope + codex / volcengine / dashscope)
[asr]
provider = "dashscope"
model = "qwen3-asr-flash"
[refiner]
# Resident local Codex app-server (or switch provider to "volcengine" / "dashscope" for pure API refinement)
provider = "codex"
model = "gpt-6-luna"
effort = "low"
Recipe B — OpenRouter Single Key (Two-Stage Dedicated STT + Refiner)
Uses OPENROUTER_API_KEY for both fast dedicated speech-to-text (/audio/transcriptions) and session-aware Stage 2 cleanup:
[asr]
provider = "openrouter"
model = "qwen/qwen3-asr-1.7b"
[refiner]
provider = "openrouter"
model = "google/gemini-2.5-flash-lite"
effort = "low"
Recipe C — OpenRouter Single Key (Multimodal Single-Hop)
Routes audio + session context in a single /chat/completions hop with anti-instruction-hijacking guards and automatically skips Stage 2:
[asr]
provider = "openrouter"
model = "google/gemini-2.5-flash"
api = "auto"
effort = "auto"
Recipe D — Native Gemini Single-Hop or Ultra-Fast Groq Whisper
# Option 1: Native Google Gemini (:generateContent single-hop audio + context; skips Stage 2)
[asr]
provider = "gemini"
model = "gemini-2.5-flash"
effort = "auto"
# Option 2: Groq Whisper STT + Groq Stage 2 Refiner
[asr]
provider = "groq"
model = "whisper-large-v3-turbo"
[refiner]
provider = "groq"
model = "llama-3.3-70b-versatile"
CLI Flags & Environment Overrides
Common runtime flags accepted across daemon, start, doctor, once, status, toggle, stop, and key-test:
| CLI Flag | Env Override (AGENT_VOICE_INPUT_*) |
Description |
|---|---|---|
--config PATH |
AGENT_VOICE_INPUT_CONFIG |
Path to config.toml or config.json |
--asr, --asr-provider |
AGENT_VOICE_INPUT_ASR_PROVIDER |
Stage 1 provider (auto, dashscope, doubao, openrouter, gemini, openai, groq, siliconflow, openai-compatible) |
--asr-model |
AGENT_VOICE_INPUT_ASR_MODEL |
Stage 1 upstream model ID |
--asr-api |
AGENT_VOICE_INPUT_ASR_API |
Stage 1 protocol (auto, audio-transcriptions, chat-completions) |
--asr-effort |
AGENT_VOICE_INPUT_ASR_EFFORT |
Stage 1 thinking budget (auto, default, none, minimal, low, medium, high, xhigh, max) |
--refiner, --refiner-provider |
AGENT_VOICE_INPUT_REFINER_PROVIDER |
Stage 2 provider (codex, dashscope, volcengine, openrouter, gemini, openai, groq, siliconflow, openai-compatible, off) |
--refiner-model |
AGENT_VOICE_INPUT_REFINER_MODEL |
Stage 2 upstream model ID |
--refiner-effort, --effort |
AGENT_VOICE_INPUT_REFINER_EFFORT |
Stage 2 thinking budget (auto, default, none, minimal, low, medium, high, xhigh, max) |
(Shorter VOICE_INPUT_* environment variable aliases are also accepted, alongside AGENT_VOICE_INPUT_HOTKEY, AGENT_VOICE_INPUT_TRIGGER_MODE, AGENT_VOICE_INPUT_UI_LANGUAGE, AGENT_VOICE_INPUT_MACOS_AUDIO_DEVICE, AGENT_VOICE_INPUT_ASR_BASE_URL, AGENT_VOICE_INPUT_REFINER_BASE_URL, and AGENT_VOICE_INPUT_FORCE_REFINE.)
Optional: Multi-Window & tmux Session Title Hooks
tmux active-pane detection works out of the box without modifying ~/.tmux.conf. If you run multiple agent windows or panes side by side in the same directory on WSL2, inside tmux, or in terminals that do not expose per-split tty/cwd, see integrations/ for optional terminal/pane title hooks (agent-voice-input session-hook) for Codex, Claude Code, and Antigravity CLI.
Development
PYTHONPATH=src python3 -m unittest discover -s tests -v
Acknowledgments
Early exploration for this project was inspired by Einstellung/voice-asr, particularly around pairing Qwen3-ASR with technical terminology context biasing for mixed Chinese–English speech, as well as pre-transcription silence gating and loudness normalization.
License
Metadata
Release files for agent-voice-input 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| agent_voice_input-0.2.0.tar.gz | 240.5 kB | With provenance | Details for agent_voice_input-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| agent_voice_input-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for agent_voice_input-0.2.0-py3-none-any.whl |
Total release size: 381.0 kB
Release files / agent_voice_input-0.2.0.tar.gz
| Download URL | agent_voice_input-0.2.0.tar.gz |
|---|---|
| Size | 240.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04487d9c1b21f2efb9427b1c42461b9bedc5b551941ef13c66a47e23bf596510
|
|
BLAKE2b-256 checksum How to use checksums |
cf745b9326ee755ad8ca6ef15b1c670ddc83306b8a1df465e228019e75697fca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 14, 2026.
Transparency logRelease files / agent_voice_input-0.2.0-py3-none-any.whl
| Download URL | agent_voice_input-0.2.0-py3-none-any.whl |
|---|---|
| Size | 140.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9d2c700ac7eb4c328df4ce168be65580d9f7239238c3695177efbe093230dfdf
|
|
BLAKE2b-256 checksum How to use checksums |
c6518ff8ec942663064d601e021964f089171e31ab0f09b6c63b11dcc6c1e536
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 14, 2026.
Transparency logSecurity for agent-voice-input 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for agent_voice_input-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 14, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.