feat(voice): on-device wake words with open-vocabulary phrases and multi-profile voice routing - #70509
Conversation
૮ >ﻌ< ა ci reviewran on f5ff8fa ℹ️ InfoDesktop E2E visual evidence · View test artifacts · View job1 visual diff. inline evidence upload failed. Failed to upload diff-665a0833239e-onboarding-overlay-diff.png with gh image (exit code 1): Error uploading /home/runner/work/_temp/e2e-evidence/diff-665a0833239e-onboarding-overlay-diff.png: step 0 (get upload token): uploadToken not found on repo page — do you have write access to NousResearch/hermes-agent? (or, if NousResearch enforces SAML SSO, authorize at https://github.com/orgs/NousResearch/sso) |
e067742 to
80e78ea
Compare
80e78ea to
4778ba2
Compare
|
Three additions since the last review round (all requested): Open-vocabulary wake phrases (new Desktop UX toggle. Ear button in the composer's voice-control row — highlighted while listening, muted when off, hidden when unavailable; tooltip shows the phrase and any start-refusal reason. Backed by a TUI toggle. Validation: wake/lazy-deps/metadata pytest 106 passed; desktop vitest 26 passed (wake-word + composer stores) + tsc clean; TUI vitest 117 passed (wake command + gateway handler + parity) + tsc clean; uv.lock regenerated. |
7f96831 to
1d96f00
Compare
Adds an opt-in, on-device hotword listener for the CLI. With wake_word.enabled (or /wake on), Hermes listens in the background for a wake phrase; on detection it starts a fresh session, captures one utterance through the existing voice pipeline, and answers — the "Hey Siri" pattern. - tools/wake_word.py: provider-pluggable detector (openWakeWord, free local default; Porcupine, premium) over the shared 16 kHz sounddevice capture path. Background daemon thread with pause/resume so it yields the mic during a voice turn. - CLI wiring: startup listener (off-thread), on-wake flow, an idle watchdog that resumes the detector after each turn, cleanup hook, and a /wake [on|off|status] command. - config.yaml wake_word section; PORCUPINE_ACCESS_KEY as an optional secret. Engines lazy-install via the [wake] extra. - Hands a transcript to the input queue exactly like voice mode, so no system-prompt/cache mutation. No new core model tool. - Tests (mocked, no live audio/network) + feature docs.
Makes the wake word a tri-surface feature with one configurable owner.
- wake_word.surface ("auto" | "cli" | "tui" | "gui") + shared
wake_surface_enabled() gate consulted by every surface, so exactly one
place owns the listener and the new session it opens.
- tui_gateway: wake.start/stop/pause/resume/status RPCs + a wake.detected
event, sharing one server-side detector for both TUI and desktop. The
detector yields the mic to voice.record (pause on capture start, resume
on terminal) and to the desktop's browser mic (wake.pause/resume).
- TUI (Ink): arm wake.start on gateway.ready; on wake.detected open a
fresh session and start voice capture.
- Desktop (Electron): arm wake.start on connect; on wake.detected open a
fresh session.
- CLI now gates on wake_surface_enabled("cli"); /wake status shows surface.
- Tests for the surface gate; docs cover the surface knob + cross-surface.
On wake, the desktop GUI now opens a fresh session AND starts the browser voice conversation (continuous, with TTS), matching the CLI/TUI hands-free flow instead of just opening a session. - Add an explicit requestVoiceStart() intent to the composer bus (idempotent start; toggle could stop an active loop). - Composer owns mic hand-off: pause the server-side wake detector while the browser voice loop is live, resume after (server no-ops when the wake word isn't armed) — via the $gateway store accessor. - Controller fires startFreshSessionDraft() + requestVoiceStart() on wake.detected.
The detector logged listen/detect/close at debug, invisible at the default level. Promote listen-start, phrase-detected, stream-closed, and the wake.start outcome (disabled / unavailable / listening) to INFO, and log wake.detected emission, so a non-triggering setup is diagnosable from gateway/gui.log without flipping global log levels.
The GUI armed the detector (wake.start) and the gateway fired
wake.detected, but the desktop never reacted: detection was wired through
a side-registered gatewayRef.current.on('wake.detected', …) listener that
was instance/timing-fragile (and silently dead across reconnects/HMR),
even though the raw events were arriving on the socket.
Route wake.detected through handleGatewayEventWithWake — the same onEvent
pipeline every gateway socket already feeds via useGatewayBoot — and open
a fresh session + start back-and-forth voice there. Drop the separate
.on() listener; the open-effect now only arms wake.start.
write_json routes via the request-scoped transport ContextVar, but the
wake detector's callback runs on a background thread where that var is
unset — so wake.detected fell back to _stdio_transport and was dumped to
the backend's stdout (visible as raw [hermes] {...} frames in desktop
logs) instead of crossing the desktop/dashboard websocket. The TUI was
unaffected because it IS stdio.
Capture the arming request's transport at wake.start and bind it around
the emit in _wake_on_detect so the background thread routes to the right
peer. Re-armed on each wake.start, so reconnects pick up the new socket.
Ending a voice conversation manually left the wake detector paused for good, so the wake word couldn't be used again. The composer paused the detector on voice start but only resumed on the voiceConversationActive -> false render; if ending voice tore the composer down first, that render never landed and the resume was skipped. Resume on unmount as well (latched on wakePausedRef so it fires exactly once), and stop early-returning when the $gateway atom is momentarily null. Add wake.pause/resume INFO logs for visibility.
…vent Wake opened a fresh session but voice didn't start: the start intent was a fire-once window CustomEvent, and the fresh-session remount tore down / recreated the composer's subscription, so the deferred dispatch landed in the gap and was lost. Replace it with a latched nanostore ($voiceConversationStartRequest + takeVoiceConversationStart): the controller sets it on wake.detected, and the composer claims it once on (re)mount when the gateway is open, waiting out any transient `disabled`. Drop the now-unused composer voice-start window event.
Two bugs surfaced by the desktop wake conversation:
1. Runaway loop: wake -> voice -> resume -> wake fired again within
~200ms. openWakeWord keeps its rolling feature buffer across
pause/resume, so on resume it immediately re-scored the "hey jarvis"
captured before the pause and re-fired, reopening a session and
restarting voice in a tight cycle. Reset the engine buffer on every
detector (re)start so resume begins from clean audio.
2. Empty-transcript toast: a silent re-listen returns
success:false / "… STT returned empty transcript", which the desktop
transcribe endpoint turned into a 400 -> thrown error -> "Voice
transcription failed" notification on every silent gap. Treat an empty
transcript as no-speech: return {ok, transcript: ""} so the voice loop
quietly re-listens. Real failures still 4xx/5xx.
start_new_session was respected only by the CLI; the TUI and desktop GUI always opened a fresh session on wake, ignoring the config. The gateway now carries the flag in the wake.detected payload and both clients honor it (open a fresh session vs. continue the current one), matching the CLI.
Regenerate uv.lock for the [wake] extra (openwakeword, pvporcupine, onnxruntime) so uv lock --check passes. Replace angle-bracket URLs in wake-word.md with markdown links — MDX treats <https://...> as JSX.
2b9de4d to
30ed3f8
Compare
The rebase's lock was produced by uv 0.9.28 (runtime venv); CI runs uv 0.11.33, whose resolver rejected it (uv sync --locked failed). Relocked with 0.11.33 — only wake-extra deps and their transitives added, no unrelated version churn.
A short, bright, rising two-note ding (G5 -> C6) plays the moment 'Hey Hermes' is detected, before voice capture starts, so it's obvious the wake registered. Deliberately distinct from the turn-end completion cue (that one settles; this one rises = 'listening'). Reuses the same lightweight WebAudio synthesis as completion-sound.ts — no asset to ship — and is gated by the shared sound-mute toggle ($hapticsMuted), so muting turn-end sounds silences it too. - apps/desktop/src/lib/wake-sound.ts: playWakeSound() - wiring.tsx: fire it at the top of the wake.detected handler - 3 tests (plays two-note chime, silent when muted, never throws with no WebAudio)
…e direction 'hey hor' triggered the wake word: the default sensitivity was 0.5, which for openWakeWord IS the raw per-frame score threshold — openWakeWord's own permissive baseline that near-misses clear. Raised the default to 0.6 so phonetic near-misses fall short while real 'hey hermes' (typically 0.9+) still fires easily. Also fixed a real cross-engine inconsistency found while checking: the sensitivity knob is documented 'higher = stricter' and behaves that way for openWakeWord (threshold = sensitivity) and sherpa (0.05 + 0.4*s), but Porcupine's own 'sensitivities' param runs the opposite way (higher = MORE false alarms, per Picovoice). Turning sensitivity up made Porcupine looser — backwards. Now inverted (1 - sensitivity) so 'higher = stricter' holds for every engine. - tools/wake_word.py: default 0.6; _sensitivity fallback uses _DEFAULTS; Porcupine sensitivity inverted with rationale - hermes_cli/config.py + docs: default + consistent-direction note - tests: Porcupine inversion, default>=0.6 regression, fallback-to-default
Saying 'stop' in a voice chat did nothing — the transcript was just
submitted to the agent as a normal turn, so the conversation never ended.
The only way out was the mouse/hotkey. That's not how a hands-free voice
assistant should work.
useVoiceConversation now checks each finished utterance against a spoken
stop-command matcher BEFORE submitting: 'stop', 'stop listening', 'never
mind', 'goodbye', 'cancel', 'that's all', etc., optionally addressed
('hey hermes, stop'). A match ends the conversation (flips enabled=false,
which drives the existing end() teardown — mic close, playback stop, wake
re-arm) instead of sending a turn.
Deliberately conservative: only a WHOLE-utterance stop phrase matches, so
substantive requests that merely contain 'stop' ('stop the docker
container', 'how do I stop a process') still go through.
- apps/desktop/src/lib/voice-stop-word.ts: isVoiceStopCommand() matcher
- use-voice-conversation.ts: onStopWord option + intercept before submit
- use-composer-voice.ts: wire onStopWord -> end the conversation
- 6 matcher tests (bare/multi-word/addressed stop; substantive requests
with 'stop' pass through; bare address words don't match)
- docs: note the spoken-stop behavior
… the repo template
The wake-word ear reverted to disabled after every restart even when
closed enabled. Root cause is general, not wake-specific: save_config_value
followed load_cli_config's precedence, which falls back to the repo's
checked-in cli-config.yaml when HERMES_HOME/config.yaml doesn't exist yet.
On such installs (managed/desktop first launch), the toggle's persist wrote
wake_word.enabled=true into cli-config.yaml and returned success — but
every config reader (load_config -> get_hermes_home()/config.yaml,
including load_wake_word_config) reads only HERMES_HOME/config.yaml, so the
setting was invisible on the next launch. Same silent loss hit any runtime
persist (model switch, /reasoning, /fast, skin) on a config-less install.
save_config_value now always targets get_hermes_home()/config.yaml,
creating it if absent, and never writes the shipped repo template. Also
resolves HERMES_HOME live instead of the import-time _hermes_home constant
(profile-safe).
E2E verified: persist -> fresh module reload -> load_wake_word_config sees
enabled=true and wake_surface_enabled('gui') is True, for both a fresh
(no config.yaml) and an existing-config install.
- cli.py save_config_value: target user config, create if absent
- tests: 2 regression tests (creates user config when absent; never writes
repo cli-config.yaml); fixture now sets HERMES_HOME. 11 file + 206
adjacent config/model-switch tests green
…g_value fix The prior commit made save_config_value resolve its target via get_hermes_home() (live env) instead of the import-time cli._hermes_home constant. Two _persist_model_switch tests patched only cli._hermes_home and wrote/read tmp_path/config.yaml, so the write now landed in the real HERMES_HOME and the readback saw stale values. Set HERMES_HOME=tmp_path (kept the _hermes_home patch for belt-and-suspenders). No production change.
Diagnosed from a Windows desktop's logs: the backend TTS was generating and saving the reply mp3 every turn (provider: openai), so synthesis was fine — the DESKTOP wasn't playing the first reply. Root cause is Chromium's autoplay policy: audio (HTMLAudioElement.play() and AudioContext) is suspended until the frame sees a user gesture. A voice conversation started by the 'Hey Hermes' wake word has no preceding click, so the first reply's play() was rejected with NotAllowedError (silently swallowed) and only turn 2+ spoke. Manual voice-start worked because the button click WAS the gesture — exactly the 'first message in a new voice session is silent, manual start is fine' report. Fix (defense in depth): - chatWindowWebPreferences sets autoplayPolicy: 'no-user-gesture-required' on every chat window (primary + secondary), so a deliberately-launched native app never gates audio on a gesture. Primary fix. - voice-playback.ts resumes a suspended AudioContext on stream 'start' and retries HTMLAudioElement.play() once via an unlock context after a NotAllowedError — covers the dashboard-embedded surface that doesn't get the Electron policy. Tests: 16 electron session-window tests (incl. new autoplay assertion), tsc + eslint clean.
…at mic 'Clicked voice chat but it never starts listening' — a mic-device contention race. Starting a voice conversation fires wake.pause (to free the mic from the wake-word listener) and opens the conversation's own mic in two separate effects, both keyed on voiceConversationActive with no ordering between them. wake.pause was fire-and-forget, so getUserMedia often raced the wake listener's stream teardown (which joins a reader thread + closes the device in a finally). On Windows the capture device is effectively single-owner, so opening it while wake still held it failed and the conversation never started listening. Wake-word-initiated starts don't hit this: the backend's _on_detect calls pause_listening synchronously before emitting wake.detected, so the mic is already free by the time the frontend opens it. Only the button/hotkey path raced — matching the report. Fix: pauseWakeForVoice now returns an awaitable barrier for the in-flight wake.pause round-trip, and useVoiceConversation awaits a new beforeMicOpen hook (wired to that barrier) right before handle.start(), re-checking enabled/muted/busy/idle after the wait. No behavior change when the wake word isn't running (barrier is null → no wait). tsc + eslint clean, 39 desktop voice/wake tests green.
# Conflicts: # tests/test_tui_gateway_server.py # tui_gateway/server.py
… turn-timeout Two follow-ups from the voice PR (#70509). 1. macOS ARM64 onnx migration. Existing users who pinned openwakeword.inference_framework=onnx before the tflite fix landed kept a wake word that arms but never fires (ONNX's embedding model is broken on Apple Silicon, upstream #336). New resolve_inference_framework() honors an explicit framework everywhere ONNX actually works, but coerces the one provably-dead combination (explicit onnx + macOS ARM64) to tflite with a one-time warning. No config mutation; empty still falls back to the platform default. Both read sites (engine init + requirements check) route through the shared resolver. 2. Voice turn-timeout leak. Each listen cycle reassigned turnTimeoutRef without clearing the prior 60s timer, so a stale timer from an earlier cycle could fire handleTurn() mid-way through a later listen — after enough idle re-listens this wedged the loop into a non-re-arming state (the 'voice chat deactivates after ~a minute' report). Clear before re-arm. Tests: 64 wake tests (added onnx-coercion / intel-kept / tflite-kept / empty-default cases; updated the stale 'explicit onnx kept on ARM64' test that encoded the old broken behavior), 39 desktop voice/wake vitest, tsc + eslint clean.
….7.30 ➔ v2026.8.3) (#253)
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [ghcr.io/gabrielcosi/hermes-agent](https://github.com/NousResearch/hermes-agent) | minor | `v2026.7.30` → `v2026.8.3` |
---
### Release Notes
<details>
<summary>NousResearch/hermes-agent (ghcr.io/gabrielcosi/hermes-agent)</summary>
### [`v2026.8.3`](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.8.3): Hermes Agent v0.20.0 (2026.8.3)
[Compare Source](https://github.com/NousResearch/hermes-agent/compare/v2026.7.30...v2026.8.3)
##### Hermes Agent v0.20.0 (v2026.8.3)
**Release Date:** August 3, 2026
**Since v0.19.0:** \~3,650 commits · \~1,400 merged PRs · \~5,200 files changed · \~559,000 insertions · \~405,000 deletions · **\~1,200 issues closed** · 650+ contributors
> **The Herald Release.** Hermes is the herald of the gods, and this release makes him one in earnest: he **speaks** (real-time conversational voice with streaming TTS, barge-in, on-device wake words, and hands-free control across the CLI, desktop, and every audio-capable gateway platform), he **carries word to other agents** (A2A v1.0), he **announces events to your systems** (signed outbound webhooks), and he **cites his sources** (grounded research with verifiable citations and fact-checking). Around that spine: the desktop app became a platform (artifacts with live preview, a plugin SDK, quick-entry from anywhere, multiple windows), the CLI got a wave of power commands (`!` shell mode, `/init`, `/diff`, `/context`, `/focus`), compression got smarter and gentler, and the tools themselves now recover from their own failures instead of making the model guess. This release rolls up everything from the v0.19.1 infrastructure patch tag — that window is fully documented here.
***
##### ✨ Highlights
- **Talk to Hermes — streaming, conversational voice with barge-in** — Voice mode used to mean: speak, wait for the whole reply to generate, then listen to one long audio file. Now Hermes speaks clause-by-clause as the response streams, you can interrupt it mid-sentence by just talking (it stops, listens, and the model is told you cut in), and busy-aware silence detection means it doesn't talk over you. This works in CLI voice mode, on the desktop, and through gateway adapters. Talking to Hermes finally feels like a conversation, not a voicemail exchange. ([#​69511](https://github.com/NousResearch/hermes-agent/pull/69511), [#​73862](https://github.com/NousResearch/hermes-agent/pull/73862), [#​74223](https://github.com/NousResearch/hermes-agent/pull/74223), [#​74000](https://github.com/NousResearch/hermes-agent/pull/74000), [#​69602](https://github.com/NousResearch/hermes-agent/pull/69602) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- **Wake words and hands-free control** — Say your own open-vocabulary wake phrase ("hey Hermes", or anything you pick) and Hermes starts listening — detection runs on-device, so no audio leaves your machine while it waits. Multi-profile voice routing means different wake words can reach different profiles, and saying "stop" ends the voice chat on every surface without touching the keyboard. Your terminal is now something you can talk to from across the room. ([#​70509](https://github.com/NousResearch/hermes-agent/pull/70509), [#​73106](https://github.com/NousResearch/hermes-agent/pull/73106), [#​73933](https://github.com/NousResearch/hermes-agent/pull/73933) — [@​teknium1](https://github.com/teknium1))
- **Voice on every platform** — Send a voice note to Hermes on WhatsApp, Feishu, DingTalk, LINE, QQ, Photon, or Weixin and it's transcribed and answered; auto-TTS replies are delivered platform-aware (opus where platforms want opus, captions attached correctly). STT is now fully configurable — its own `hermes tools` category, GUI toggles, dashboard dropdowns, unified language resolution so transcripts stop coming back in the wrong language, and OpenAI's gpt-transcribe support. One unified spoken-text preprocessor cleans markdown, code, and URLs out of speech across all TTS providers. ([#​73515](https://github.com/NousResearch/hermes-agent/pull/73515), [#​73508](https://github.com/NousResearch/hermes-agent/pull/73508), [#​73910](https://github.com/NousResearch/hermes-agent/pull/73910), [#​73513](https://github.com/NousResearch/hermes-agent/pull/73513), [#​73067](https://github.com/NousResearch/hermes-agent/pull/73067) — [@​teknium1](https://github.com/teknium1))
- **Research you can trust — grounded citations with fact-checking** — The new `grounded-citations` skill makes Hermes produce research where every claim is backed by a verifiable source: quotes are matched against the actual page text (not hallucinated), citations link to the exact evidence, and a fact-checking mode turns the same machinery on any document or claim you hand it — it tells you what checks out, what doesn't, and what couldn't be verified. If you use Hermes for research, this is the difference between "sounds right" and "provably sourced." ([#​71698](https://github.com/NousResearch/hermes-agent/pull/71698), [#​77104](https://github.com/NousResearch/hermes-agent/pull/77104) — [@​teknium1](https://github.com/teknium1))
- **Outbound webhooks — Hermes pushes events to your systems** — Until now, integrating with Hermes meant polling or listening on a platform. Now Hermes pushes **signed lifecycle events** (session activity, turn completions, tool events) to any HTTP endpoint you register — with HMAC signatures so your receiver can verify authenticity. Wire Hermes into your CI, your home automation, your dashboards, or any service that speaks HTTP, with no polling loop. ([#​69406](https://github.com/NousResearch/hermes-agent/pull/69406) — [@​teknium1](https://github.com/teknium1))
- **The desktop app becomes a platform — artifacts, plugin SDK, quick entry** — Hermes desktop now renders **artifacts**: versioned cards with sandboxed live preview in a right-rail viewer, so generated HTML/apps run safely next to the chat. A real **plugin SDK** landed with Kanban as its founding plugin, `ctx.download` for handing users files, floating pane placement, and multiple GUI windows. A global-hotkey **quick-entry window** captures a thought into any session from anywhere in your OS. The desktop stopped being a chat client and started being a workbench. ([#​72345](https://github.com/NousResearch/hermes-agent/pull/72345), [#​61173](https://github.com/NousResearch/hermes-agent/pull/61173), [#​74413](https://github.com/NousResearch/hermes-agent/pull/74413), [#​72315](https://github.com/NousResearch/hermes-agent/pull/72315), [#​68259](https://github.com/NousResearch/hermes-agent/pull/68259), [#​73143](https://github.com/NousResearch/hermes-agent/pull/73143) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- **Hermes speaks Agent-to-Agent — A2A v1.0** — A new bundled plugin implements the Agent-to-Agent protocol, so Hermes can discover, talk to, and be driven by other A2A-compatible agents. This closes issue [#​514](https://github.com/NousResearch/hermes-agent/issues/514) — one of the oldest open feature requests in the repo. If you're building multi-agent systems with heterogeneous stacks, Hermes now has a standard wire protocol for joining them. ([#​77109](https://github.com/NousResearch/hermes-agent/pull/77109) — [@​teknium1](https://github.com/teknium1))
- **CLI power-user wave** — `!command` runs a shell command instantly without spending a model turn. `/init` scans your project and generates (or updates) an `AGENTS.md`. `/diff` shows staged/all/session changes from any surface, `/context` breaks down exactly what's filling your context window, `/focus` gives you a reduced-output view with hidden-line recovery, and Ctrl+S stashes a half-written prompt into a browsable panel. Plus `hermes import-agent` migrates your Claude Code or Codex CLI setup into Hermes in one command. ([#​72257](https://github.com/NousResearch/hermes-agent/pull/72257), [#​72178](https://github.com/NousResearch/hermes-agent/pull/72178), [#​72240](https://github.com/NousResearch/hermes-agent/pull/72240), [#​72242](https://github.com/NousResearch/hermes-agent/pull/72242), [#​72302](https://github.com/NousResearch/hermes-agent/pull/72302), [#​72262](https://github.com/NousResearch/hermes-agent/pull/72262), [#​72190](https://github.com/NousResearch/hermes-agent/pull/72190) — [@​teknium1](https://github.com/teknium1), several salvaging long-standing community PRs)
- **Correct the agent mid-turn — redirects** — If Hermes is heading the wrong way, you no longer have to `/stop` and re-explain. Type a correction while it works and the active turn is redirected: work in flight is preserved, the original prompt is kept, and the agent course-corrects with your new guidance. Paired with double-ESC draft discard and a composer undo stack, steering feels like editing, not restarting. ([#​63104](https://github.com/NousResearch/hermes-agent/pull/63104), [#​72339](https://github.com/NousResearch/hermes-agent/pull/72339), [#​74736](https://github.com/NousResearch/hermes-agent/pull/74736) — [@​OutThisLife](https://github.com/OutThisLife))
- **Tools that fix themselves** — A sweep of self-recovery upgrades means the agent wastes far fewer turns on tool friction: truncated terminal output spills to a file the agent can read back, `patch` detects already-applied edits and diagnoses whitespace mismatches, `write_file` verifies content on disk, searches that match nothing probe for near-misses and recover, and common failure classes come back with actionable hints. The default tool-calling iteration limit also jumped 90 → 500 — long autonomous runs stopped hitting an artificial wall. ([#​77041](https://github.com/NousResearch/hermes-agent/pull/77041), [#​76998](https://github.com/NousResearch/hermes-agent/pull/76998), [#​77024](https://github.com/NousResearch/hermes-agent/pull/77024), [#​77055](https://github.com/NousResearch/hermes-agent/pull/77055), [#​77011](https://github.com/NousResearch/hermes-agent/pull/77011), [#​76992](https://github.com/NousResearch/hermes-agent/pull/76992), [#​72176](https://github.com/NousResearch/hermes-agent/pull/72176) — [@​teknium1](https://github.com/teknium1))
- **Compression that respects your conversation** — Context compression got a deep overhaul: proactive tool-result pruning for large-window models, per-turn micro-compaction that amortizes the cost instead of one giant pause, a guaranteed N-user-message tail so recent conversation always survives, progress-aware timeouts that stop punishing slow summary models, and ghost-skill defense so a pruned skill can never silently haunt a session. Thresholds are now configurable per-model and in absolute tokens. Long sessions stay coherent and stop stalling. ([#​70254](https://github.com/NousResearch/hermes-agent/pull/70254), [#​75345](https://github.com/NousResearch/hermes-agent/pull/75345), [#​70250](https://github.com/NousResearch/hermes-agent/pull/70250), [#​71508](https://github.com/NousResearch/hermes-agent/pull/71508), [#​70275](https://github.com/NousResearch/hermes-agent/pull/70275) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor), salvaging multiple community PRs)
- **Smart approvals grow up** — `hermes approvals suggest` mines your approval history into allowlist proposals, operators can customize the smart-approval policy, a consecutive-denial circuit breaker stops a misbehaving loop cold, and desktop pairing approvals are profile-correct with a proper surface to answer them from. Plus a new approval gate for docker/podman daemon-redirect commands. Less clicking "approve", without giving an inch of control. ([#​72259](https://github.com/NousResearch/hermes-agent/pull/72259), [#​72186](https://github.com/NousResearch/hermes-agent/pull/72186), [#​72203](https://github.com/NousResearch/hermes-agent/pull/72203), [#​74446](https://github.com/NousResearch/hermes-agent/pull/74446), [#​71092](https://github.com/NousResearch/hermes-agent/pull/71092) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- **Faster everywhere, again** — Prompt caching now covers tool schemas on native Anthropic without history loss. `hermes -w` cold start dropped \~14s → \~1.8s, `hermes update` no-ops got 2–6s faster, heavy SDKs lazy-load off the import path, config reads stopped deep-copying (54× faster on the telemetry gate), and the desktop shipped a second 60fps wave — streaming cost independent of transcript length, drag at 60fps with five streaming tabs, idle CPU near zero in the background. ([#​76032](https://github.com/NousResearch/hermes-agent/pull/76032), [#​71637](https://github.com/NousResearch/hermes-agent/pull/71637), [#​74218](https://github.com/NousResearch/hermes-agent/pull/74218), [#​74204](https://github.com/NousResearch/hermes-agent/pull/74204), [#​71835](https://github.com/NousResearch/hermes-agent/pull/71835), [#​72346](https://github.com/NousResearch/hermes-agent/pull/72346), [#​75218](https://github.com/NousResearch/hermes-agent/pull/75218) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- **New places to run and be reached** — Buzz lands as a bundled gateway platform (Block's Nostr-based messenger, with native WebSocket transport and NIP-42 auth), the Vercel AI Gateway provider and Vercel Sandbox terminal backend return modernized, desktop gains an SSH remote-backend connection mode, and the Relay shipped four phases of parity — media, interactive prompts, thread lifecycle, typing indicators — plus HSP personal + org skill sync. ([#​73610](https://github.com/NousResearch/hermes-agent/pull/73610), [#​73761](https://github.com/NousResearch/hermes-agent/pull/73761), [#​74518](https://github.com/NousResearch/hermes-agent/pull/74518), [#​68130](https://github.com/NousResearch/hermes-agent/pull/68130), [#​71300](https://github.com/NousResearch/hermes-agent/pull/71300)–[#​71624](https://github.com/NousResearch/hermes-agent/pull/71624), [#​66730](https://github.com/NousResearch/hermes-agent/pull/66730) — [@​teknium1](https://github.com/teknium1), [@​yoniebans](https://github.com/yoniebans), [@​benbarclay](https://github.com/benbarclay))
***
##### 🎙️ Voice & Speech
##### Conversational voice
- Streaming, conversational TTS with barge-in across all surfaces; clause-by-clause synthesis for CLI voice mode + gateway adapters ([#​69511](https://github.com/NousResearch/hermes-agent/pull/69511), [#​73862](https://github.com/NousResearch/hermes-agent/pull/73862) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- Voice chat UX polish — busy-aware silence, stop hint, thinking sounds, barge-in fix; full-duplex turn listener (interrupt by voice during generation AND playback) ([#​74000](https://github.com/NousResearch/hermes-agent/pull/74000), [#​74223](https://github.com/NousResearch/hermes-agent/pull/74223) — [@​teknium1](https://github.com/teknium1))
- On-device wake words with open-vocabulary phrases + multi-profile voice routing; say "stop" to end voice chat hands-free on every surface ([#​70509](https://github.com/NousResearch/hermes-agent/pull/70509), [#​73106](https://github.com/NousResearch/hermes-agent/pull/73106), [#​73933](https://github.com/NousResearch/hermes-agent/pull/73933) — [@​teknium1](https://github.com/teknium1))
- The model is told when the user interrupts its spoken reply; desktop speaks the whole turn and idle-flushes held narration ([#​69602](https://github.com/NousResearch/hermes-agent/pull/69602), [#​69936](https://github.com/NousResearch/hermes-agent/pull/69936) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- 15-item CLI/TUI voice-mode UX and environment fix wave ([#​73520](https://github.com/NousResearch/hermes-agent/pull/73520) — [@​teknium1](https://github.com/teknium1))
##### TTS / STT infrastructure
- Unified spoken-text preprocessing + speed/instructions/provider tool params; unified STT language resolution (fixes the wrong-language transcription class); global `stt.language` defaults to `en` ([#​73513](https://github.com/NousResearch/hermes-agent/pull/73513), [#​73067](https://github.com/NousResearch/hermes-agent/pull/73067), [#​73100](https://github.com/NousResearch/hermes-agent/pull/73100) — [@​teknium1](https://github.com/teknium1))
- Fully configurable STT — `hermes tools` category, GUI toggle/matrix, dashboard dropdowns, setup status; OpenAI gpt-transcribe support ([#​73910](https://github.com/NousResearch/hermes-agent/pull/73910), [#​73853](https://github.com/NousResearch/hermes-agent/pull/73853) — [@​teknium1](https://github.com/teknium1))
- Platform-aware auto-TTS voice delivery (opus platforms, streamed/global gap, captions); inbound voice classification/routing for Feishu, DingTalk, LINE, QQ, Photon, WhatsApp, Weixin ([#​73508](https://github.com/NousResearch/hermes-agent/pull/73508), [#​73515](https://github.com/NousResearch/hermes-agent/pull/73515) — [@​teknium1](https://github.com/teknium1))
- Command TTS/STT provider hardening — idle timeouts, env scrubbing, no-shell, path guards ([#​73514](https://github.com/NousResearch/hermes-agent/pull/73514) — [@​teknium1](https://github.com/teknium1))
- Sync per-sentence TTS synthesis pipelined with playback — the next sentence renders while the current one speaks ([#​77355](https://github.com/NousResearch/hermes-agent/pull/77355) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Discord voice PCM streams to ffmpeg stdin instead of a temp file ([#​76970](https://github.com/NousResearch/hermes-agent/pull/76970) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🏗️ Core Agent & Architecture
##### Compression & context
- Proactive tool-result pruning for large-window models; per-turn micro-compaction; N-user tail guarantee (`compression.min_tail_user_messages`); bounded summarizer input with head+tail retention ([#​70254](https://github.com/NousResearch/hermes-agent/pull/70254), [#​75345](https://github.com/NousResearch/hermes-agent/pull/75345), [#​70250](https://github.com/NousResearch/hermes-agent/pull/70250), [#​70249](https://github.com/NousResearch/hermes-agent/pull/70249) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Ghost-skill defense — `[SKILL_PRUNED]` markers, protected prune, deterministic survival; progress-aware timeouts; lock-contended compression soft-defers instead of exhausting ([#​70275](https://github.com/NousResearch/hermes-agent/pull/70275), [#​71508](https://github.com/NousResearch/hermes-agent/pull/71508), [#​70285](https://github.com/NousResearch/hermes-agent/pull/70285) — [@​teknium1](https://github.com/teknium1))
- Per-model threshold overrides; absolute token threshold (`compression.threshold_tokens`); opt-in idle-triggered compaction; opt-in progress notices; structured local logging for compression attempts ([#​69339](https://github.com/NousResearch/hermes-agent/pull/69339), [#​69335](https://github.com/NousResearch/hermes-agent/pull/69335), [#​69360](https://github.com/NousResearch/hermes-agent/pull/69360), [#​70457](https://github.com/NousResearch/hermes-agent/pull/70457), [#​69338](https://github.com/NousResearch/hermes-agent/pull/69338) — [@​teknium1](https://github.com/teknium1))
- Context-engine ABC grows `select_context()` + `on_turn_complete()` verbs (salvage of [@​chaos-xxl](https://github.com/chaos-xxl)'s RFC work); engines can suppress or customize compaction status ([#​70458](https://github.com/NousResearch/hermes-agent/pull/70458), [#​69859](https://github.com/NousResearch/hermes-agent/pull/69859) — [@​teknium1](https://github.com/teknium1))
- Strict redaction applied at every compaction text boundary ([#​69294](https://github.com/NousResearch/hermes-agent/pull/69294) — [@​teknium1](https://github.com/teknium1))
##### Prompt caching & hot-path performance
- Tool schemas cached on native Anthropic without history loss + consolidated cache-plan internals ([#​76032](https://github.com/NousResearch/hermes-agent/pull/76032), [#​76067](https://github.com/NousResearch/hermes-agent/pull/76067) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- DeepSeek prompt caching on OpenCode gateways; per-API-call token accounting off the turn thread; OpenAI wire client reused across sequential LLM calls; send-path tool-call canonicalization memoized ([#​75886](https://github.com/NousResearch/hermes-agent/pull/75886), [#​73359](https://github.com/NousResearch/hermes-agent/pull/73359), [#​73375](https://github.com/NousResearch/hermes-agent/pull/73375), [#​76880](https://github.com/NousResearch/hermes-agent/pull/76880) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Readonly config loader at 29 call sites (28× cheaper reads); per-turn config deepcopies killed (telemetry gate 54×); one raw config.yaml parse per process; inter-tool delay removed ([#​74322](https://github.com/NousResearch/hermes-agent/pull/74322), [#​74211](https://github.com/NousResearch/hermes-agent/pull/74211), [#​74228](https://github.com/NousResearch/hermes-agent/pull/74228), [#​64172](https://github.com/NousResearch/hermes-agent/pull/64172) — [@​teknium1](https://github.com/teknium1), [@​Soju06](https://github.com/Soju06))
- Lazy heavy-SDK imports (−8-10% import cost on top of the mcp/tool-discovery diet); streaming hot loop drops per-chunk repr() (\~3× cheaper accounting); cursor/memo optimizations for per-iteration history walks ([#​74204](https://github.com/NousResearch/hermes-agent/pull/74204), [#​74194](https://github.com/NousResearch/hermes-agent/pull/74194), [#​74221](https://github.com/NousResearch/hermes-agent/pull/74221), [#​74231](https://github.com/NousResearch/hermes-agent/pull/74231) — [@​teknium1](https://github.com/teknium1))
- Cold-start \~14s GIL stall during backend init mitigated; turn flush batched into one SQLite transaction; provider-capability-gated prompt cache keys (implied for api.openai.com) ([#​77814](https://github.com/NousResearch/hermes-agent/pull/77814), [#​77619](https://github.com/NousResearch/hermes-agent/pull/77619), [#​77609](https://github.com/NousResearch/hermes-agent/pull/77609) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- AIAgent hot-path salvage — prompt-cache copy, reasoning-timeout precompute, lazy compressor init ([#​57229](https://github.com/NousResearch/hermes-agent/pull/57229) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### Approvals & the agent loop
- `hermes approvals suggest` mines approval history into allowlist proposals; operator-customizable `approvals.smart_policy`; consecutive-denial circuit breaker; cross-surface approvals mode command ([#​72259](https://github.com/NousResearch/hermes-agent/pull/72259), [#​72186](https://github.com/NousResearch/hermes-agent/pull/72186), [#​72203](https://github.com/NousResearch/hermes-agent/pull/72203), [#​63517](https://github.com/NousResearch/hermes-agent/pull/63517) — [@​teknium1](https://github.com/teknium1))
- Docker/podman daemon-redirect commands require approval; session-wide runaway-loop caps for web\_search + delegate\_task (Claude Code-inspired) ([#​71092](https://github.com/NousResearch/hermes-agent/pull/71092), [#​66600](https://github.com/NousResearch/hermes-agent/pull/66600) — [@​teknium1](https://github.com/teknium1))
- Mid-turn redirects — user corrections steer the active turn, preserving in-flight work and the original prompt ([#​63104](https://github.com/NousResearch/hermes-agent/pull/63104), [#​72339](https://github.com/NousResearch/hermes-agent/pull/72339) — [@​OutThisLife](https://github.com/OutThisLife))
- Delegation: structured timeout/stall metadata + live per-child status in `/agents`; subagents can use `execute_code`; redacted child tool history exposed in `subagent_stop`; public subagent lifecycle API for plugins ([#​72300](https://github.com/NousResearch/hermes-agent/pull/72300), [#​69325](https://github.com/NousResearch/hermes-agent/pull/69325), [#​72403](https://github.com/NousResearch/hermes-agent/pull/72403), [#​72501](https://github.com/NousResearch/hermes-agent/pull/72501) — [@​teknium1](https://github.com/teknium1))
- Single-owner refactors for backend identity + failure-scoped skips, empty-content wire repair, call\_id/reasoning sanitization, model-switch parsing ([#​72505](https://github.com/NousResearch/hermes-agent/pull/72505), [#​73071](https://github.com/NousResearch/hermes-agent/pull/73071), [#​74319](https://github.com/NousResearch/hermes-agent/pull/74319), [#​74229](https://github.com/NousResearch/hermes-agent/pull/74229) — [@​teknium1](https://github.com/teknium1))
- Labeled reasoning excerpt surfaced at the empty-response terminal; tool\_search probe-validates blind tool\_call args ([#​65144](https://github.com/NousResearch/hermes-agent/pull/65144), [#​59267](https://github.com/NousResearch/hermes-agent/pull/59267) — [@​teknium1](https://github.com/teknium1))
##### Tool self-recovery wave
- Terminal: recoverable truncation (full output spilled + pre-truncation size), cwd echoed when a command changes directory, output-pattern failure hints ([#​77041](https://github.com/NousResearch/hermes-agent/pull/77041), [#​77004](https://github.com/NousResearch/hermes-agent/pull/77004), [#​76992](https://github.com/NousResearch/hermes-agent/pull/76992) — [@​teknium1](https://github.com/teknium1))
- Patch: already-applied edits return success no-op, whitespace-visualized no-match diagnosis, ambiguous-match locations listed ([#​76998](https://github.com/NousResearch/hermes-agent/pull/76998), [#​77024](https://github.com/NousResearch/hermes-agent/pull/77024), [#​77001](https://github.com/NousResearch/hermes-agent/pull/77001) — [@​teknium1](https://github.com/teknium1))
- Search: zero-match probes + multi-path recovery, auto-multiline for newline patterns; read\_file default limit 500 → 2000 lines; negative-result cache for read/search misses; write\_file verifies on-disk content ([#​77011](https://github.com/NousResearch/hermes-agent/pull/77011), [#​77102](https://github.com/NousResearch/hermes-agent/pull/77102), [#​76996](https://github.com/NousResearch/hermes-agent/pull/76996), [#​76945](https://github.com/NousResearch/hermes-agent/pull/76945), [#​77055](https://github.com/NousResearch/hermes-agent/pull/77055) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- execute\_code recovery hints; skill\_view dedup stub for unchanged re-reads; terminal/execute\_code schema prose trimmed \~40%; tiered tool disclosure scales with catalog size; default iteration limit 90 → 500 ([#​77106](https://github.com/NousResearch/hermes-agent/pull/77106), [#​77095](https://github.com/NousResearch/hermes-agent/pull/77095), [#​77023](https://github.com/NousResearch/hermes-agent/pull/77023), [#​67034](https://github.com/NousResearch/hermes-agent/pull/67034), [#​72176](https://github.com/NousResearch/hermes-agent/pull/72176) — [@​teknium1](https://github.com/teknium1))
##### Providers & models
- Vercel AI Gateway provider + Vercel Sandbox terminal backend return, modernized (SDK 0.7.2, telemetry off) ([#​74518](https://github.com/NousResearch/hermes-agent/pull/74518) — [@​teknium1](https://github.com/teknium1))
- Gemini 3.1 Pro + 3.6 Flash in catalogs; Gemini salvage cluster (3.6-flash aux default, Vertex catalog, direct cost tracking); claude-opus-5 in OpenRouter + Nous Portal; deepseek-v4-flash-0731 ([#​73479](https://github.com/NousResearch/hermes-agent/pull/73479), [#​73516](https://github.com/NousResearch/hermes-agent/pull/73516), [#​70946](https://github.com/NousResearch/hermes-agent/pull/70946), [#​75501](https://github.com/NousResearch/hermes-agent/pull/75501) — [@​teknium1](https://github.com/teknium1))
- Bedrock Converse API prompt caching (cachePoint) ([#​70231](https://github.com/NousResearch/hermes-agent/pull/70231) — [@​JoaoMarcos44](https://github.com/JoaoMarcos44))
- OpenAI data-residency endpoints get declared transport + correct catalog; provider-aware API-server request routing; backend-acknowledged session model lock; Nous sticky routing via top-level session\_id ([#​74958](https://github.com/NousResearch/hermes-agent/pull/74958), [#​70853](https://github.com/NousResearch/hermes-agent/pull/70853), [#​70950](https://github.com/NousResearch/hermes-agent/pull/70950), [#​69253](https://github.com/NousResearch/hermes-agent/pull/69253) — [@​victor-kyriazakos](https://github.com/victor-kyriazakos), [@​teknium1](https://github.com/teknium1))
- Model picker: curated defaults + collapsible providers + select-all; stale caches served instantly with background refresh; custom-endpoint probe capped at 1.5s; honcho OAuth device-code login ([#​73172](https://github.com/NousResearch/hermes-agent/pull/73172), [#​76430](https://github.com/NousResearch/hermes-agent/pull/76430), [#​76922](https://github.com/NousResearch/hermes-agent/pull/76922), [#​61608](https://github.com/NousResearch/hermes-agent/pull/61608) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​akattelu](https://github.com/akattelu))
- ACP: named custom providers in the model selector; authenticated cross-provider model choices; non-blocking startup via background MCP discovery ([#​70082](https://github.com/NousResearch/hermes-agent/pull/70082), [#​70404](https://github.com/NousResearch/hermes-agent/pull/70404), [#​75985](https://github.com/NousResearch/hermes-agent/pull/75985) — [@​israellot](https://github.com/israellot), [@​amanning3390](https://github.com/amanning3390), [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### Secrets & config
- Command-helper secret source (composes with all vaults); one-command token rotation + actionable startup errors; opt-in encrypted break-glass cache for Bitwarden; vault-injected keys scoped per profile home; orchestrator preserve\_existing + profile aliasing ([#​69266](https://github.com/NousResearch/hermes-agent/pull/69266), [#​68605](https://github.com/NousResearch/hermes-agent/pull/68605), [#​69251](https://github.com/NousResearch/hermes-agent/pull/69251), [#​69250](https://github.com/NousResearch/hermes-agent/pull/69250), [#​69058](https://github.com/NousResearch/hermes-agent/pull/69058) — [@​teknium1](https://github.com/teknium1))
- `${env:VAR}` SecretRef parity between config.yaml and MCP config; secret-source env vars reach stdio MCP servers ([#​69267](https://github.com/NousResearch/hermes-agent/pull/69267), [#​69053](https://github.com/NousResearch/hermes-agent/pull/69053) — [@​teknium1](https://github.com/teknium1))
- Canonical config loaders for behavioral reads; table-driven config migration registry; DEFAULT\_CONFIG extracted to config\_defaults.py; auto-migration support floor at v12 ([#​74237](https://github.com/NousResearch/hermes-agent/pull/74237), [#​74200](https://github.com/NousResearch/hermes-agent/pull/74200), [#​74182](https://github.com/NousResearch/hermes-agent/pull/74182), [#​74433](https://github.com/NousResearch/hermes-agent/pull/74433) — [@​teknium1](https://github.com/teknium1))
##### 🌐 Gateway, Relay & Fleet
- Session activity heartbeats, stall watchdog, and bounded compression waits — re-landed hardened after an in-window revert cycle (originally [#​72424](https://github.com/NousResearch/hermes-agent/issues/72424) by [@​fangliquanflq](https://github.com/fangliquanflq)) ([#​76354](https://github.com/NousResearch/hermes-agent/pull/76354) — [@​teknium1](https://github.com/teknium1))
- SessionState consolidation (19 session-keyed dicts → one turn/conversation/persistent-scoped object); TurnContext/TurnRunner seam extraction; declarative busy\_policy on CommandDef ([#​74289](https://github.com/NousResearch/hermes-agent/pull/74289), [#​74353](https://github.com/NousResearch/hermes-agent/pull/74353), [#​74197](https://github.com/NousResearch/hermes-agent/pull/74197) — [@​teknium1](https://github.com/teknium1))
- Relay parity waves: Phase 1 (supported\_ops discovery, identity fields, /handoff aliasing), Phase 2 media, Phase 3 interactive prompts, Phase 4 thread lifecycle; egress typing indicators ([#​71300](https://github.com/NousResearch/hermes-agent/pull/71300), [#​71363](https://github.com/NousResearch/hermes-agent/pull/71363), [#​71404](https://github.com/NousResearch/hermes-agent/pull/71404), [#​71624](https://github.com/NousResearch/hermes-agent/pull/71624), [#​69721](https://github.com/NousResearch/hermes-agent/pull/69721) — [@​benbarclay](https://github.com/benbarclay))
- HSP skill sync: personal client (M1) + org-skills client (M2) + org-skill namespace with token-gated discovery ([#​66730](https://github.com/NousResearch/hermes-agent/pull/66730), [#​70024](https://github.com/NousResearch/hermes-agent/pull/70024), [#​70459](https://github.com/NousResearch/hermes-agent/pull/70459) — [@​benbarclay](https://github.com/benbarclay))
- Buzz (Block/Nostr) platform adapter with native WebSocket inbound transport + NIP-42 auth ([#​73610](https://github.com/NousResearch/hermes-agent/pull/73610), [#​73761](https://github.com/NousResearch/hermes-agent/pull/73761) — [@​teknium1](https://github.com/teknium1))
- Photon: native polls, effects, clarify-as-poll, rich links (4-PR salvage) ([#​73614](https://github.com/NousResearch/hermes-agent/pull/73614) — [@​teknium1](https://github.com/teknium1))
- Slack: native Block Kit clarify buttons; opt-in reaction triggers; outbound payload sanitization; thread-context lifecycle fixes ([#​69318](https://github.com/NousResearch/hermes-agent/pull/69318), [#​70195](https://github.com/NousResearch/hermes-agent/pull/70195), [#​69317](https://github.com/NousResearch/hermes-agent/pull/69317), [#​69320](https://github.com/NousResearch/hermes-agent/pull/69320) — [@​teknium1](https://github.com/teknium1))
- Discord auto-thread sessions keyed on prospective\_thread\_id; reply references built from ids (no fetch\_message); WhatsApp configurable inbound read receipts ([#​76513](https://github.com/NousResearch/hermes-agent/pull/76513), [#​76875](https://github.com/NousResearch/hermes-agent/pull/76875), [#​73322](https://github.com/NousResearch/hermes-agent/pull/73322) — [@​benbarclay](https://github.com/benbarclay), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Kanban wakes resume the creator's DM/thread session; kanban/delegate wake-ups reach api\_server sessions; per-task model + thinking-depth from the board ([#​72191](https://github.com/NousResearch/hermes-agent/pull/72191), [#​70171](https://github.com/NousResearch/hermes-agent/pull/70171), [#​69876](https://github.com/NousResearch/hermes-agent/pull/69876), [#​76417](https://github.com/NousResearch/hermes-agent/pull/76417) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- Relay: Discord tool-progress routed into the auto-thread instead of the parent channel ([#​77830](https://github.com/NousResearch/hermes-agent/pull/77830) — [@​benbarclay](https://github.com/benbarclay))
- Outbound webhooks — push signed lifecycle events to external endpoints; simplex channel enumeration in `hermes send --list` ([#​69406](https://github.com/NousResearch/hermes-agent/pull/69406), [#​77110](https://github.com/NousResearch/hermes-agent/pull/77110) — [@​teknium1](https://github.com/teknium1))
##### 🖥️ Hermes Desktop App
##### The platform wave
- **Artifacts** — versioned cards, sandboxed live preview, right-rail viewer ([#​72345](https://github.com/NousResearch/hermes-agent/pull/72345) — [@​teknium1](https://github.com/teknium1))
- **Plugin SDK** — Kanban as the founding desktop plugin; `ctx.download` hands the user a file; widget-app SDK (apps as state+reducer+render) with three reference apps; widget-grid layout engine + background-aware theme engine ([#​61173](https://github.com/NousResearch/hermes-agent/pull/61173), [#​74413](https://github.com/NousResearch/hermes-agent/pull/74413), [#​68306](https://github.com/NousResearch/hermes-agent/pull/68306), [#​20379](https://github.com/NousResearch/hermes-agent/pull/20379) — [@​OutThisLife](https://github.com/OutThisLife))
- Quick-entry window (global hotkey → any session); multiple GUI windows; floating pane placement; pane toggles anywhere + hidden header; ⌘O open-folder-as-project ([#​72315](https://github.com/NousResearch/hermes-agent/pull/72315), [#​68259](https://github.com/NousResearch/hermes-agent/pull/68259), [#​73143](https://github.com/NousResearch/hermes-agent/pull/73143), [#​75848](https://github.com/NousResearch/hermes-agent/pull/75848), [#​74623](https://github.com/NousResearch/hermes-agent/pull/74623) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- SSH remote-backend connection mode; event-driven live sync replaces always-on polls; remote profile routing/sessions/pool lifecycle repaired ([#​68130](https://github.com/NousResearch/hermes-agent/pull/68130), [#​73673](https://github.com/NousResearch/hermes-agent/pull/73673), [#​72835](https://github.com/NousResearch/hermes-agent/pull/72835) — [@​yoniebans](https://github.com/yoniebans), [@​OutThisLife](https://github.com/OutThisLife))
- Let the agent drive the shell (preview pane + pane focus) AND inspect the desktop app it's developing; find-in-page (Ctrl+F); GUI terminal copy/paste + font picker ([#​69519](https://github.com/NousResearch/hermes-agent/pull/69519), [#​73121](https://github.com/NousResearch/hermes-agent/pull/73121), [#​72235](https://github.com/NousResearch/hermes-agent/pull/72235), [#​73705](https://github.com/NousResearch/hermes-agent/pull/73705), [#​76395](https://github.com/NousResearch/hermes-agent/pull/76395) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
##### Composer & UX
- Attach files/folders/links via picker; composer chips for @​ paths and pasted links; composer undo stack; double-ESC discards draft; double-Enter sends the queued turn; type-to-focus ([#​74668](https://github.com/NousResearch/hermes-agent/pull/74668), [#​73110](https://github.com/NousResearch/hermes-agent/pull/73110), [#​72201](https://github.com/NousResearch/hermes-agent/pull/72201), [#​72288](https://github.com/NousResearch/hermes-agent/pull/72288), [#​74736](https://github.com/NousResearch/hermes-agent/pull/74736), [#​73101](https://github.com/NousResearch/hermes-agent/pull/73101), [#​68918](https://github.com/NousResearch/hermes-agent/pull/68918) — [@​OutThisLife](https://github.com/OutThisLife))
- 2-keypress model switching (⌘⇧M); YOLO in ⌘K with live toggle state; keyboard-first pickers; keyboard navigation for clarify choices; server-owned pins that follow you between apps ([#​74545](https://github.com/NousResearch/hermes-agent/pull/74545), [#​74674](https://github.com/NousResearch/hermes-agent/pull/74674), [#​74602](https://github.com/NousResearch/hermes-agent/pull/74602), [#​69799](https://github.com/NousResearch/hermes-agent/pull/69799), [#​74234](https://github.com/NousResearch/hermes-agent/pull/74234) — [@​OutThisLife](https://github.com/OutThisLife))
- Grouped, live-ticking tool-activity line; improved tool call detail views; [@​session](https://github.com/session) links resolve to clickable titles; brand icons on known-domain links; iMessage-style emoji reactions (opt-in, two-way); double-click to heart ([#​72893](https://github.com/NousResearch/hermes-agent/pull/72893), [#​69868](https://github.com/NousResearch/hermes-agent/pull/69868), [#​71162](https://github.com/NousResearch/hermes-agent/pull/71162), [#​73047](https://github.com/NousResearch/hermes-agent/pull/73047), [#​74533](https://github.com/NousResearch/hermes-agent/pull/74533), [#​74644](https://github.com/NousResearch/hermes-agent/pull/74644) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
- Sidebar date dividers + pinned section + opt-in stale-session auto-archive; sessions stop lying about running state; credit-usage toasts; configurable attachment size limit; Cron Blueprints + Webhooks pages; searchable timezone picker ([#​70822](https://github.com/NousResearch/hermes-agent/pull/70822), [#​72303](https://github.com/NousResearch/hermes-agent/pull/72303), [#​69828](https://github.com/NousResearch/hermes-agent/pull/69828), [#​73221](https://github.com/NousResearch/hermes-agent/pull/73221), [#​70066](https://github.com/NousResearch/hermes-agent/pull/70066), [#​69687](https://github.com/NousResearch/hermes-agent/pull/69687), [#​73505](https://github.com/NousResearch/hermes-agent/pull/73505) — [@​OutThisLife](https://github.com/OutThisLife), [@​austinpickett](https://github.com/austinpickett), [@​Adolanium](https://github.com/Adolanium), [@​teknium1](https://github.com/teknium1))
- RFC 8252 native desktop sign-in (system browser + PKCE, no webview cookies); "Connect to existing Hermes" in first-run onboarding; profile-correct pairing approvals with a desktop surface ([#​67920](https://github.com/NousResearch/hermes-agent/pull/67920), [#​70907](https://github.com/NousResearch/hermes-agent/pull/70907), [#​74446](https://github.com/NousResearch/hermes-agent/pull/74446) — [@​benbarclay](https://github.com/benbarclay), [@​OutThisLife](https://github.com/OutThisLife))
- Keep-computer-awake toggle + notch wake indicator; /battery status-bar toggle; UI zoom 90% default preset; status bar hideable ([#​68140](https://github.com/NousResearch/hermes-agent/pull/68140), [#​76396](https://github.com/NousResearch/hermes-agent/pull/76396), [#​68860](https://github.com/NousResearch/hermes-agent/pull/68860), [#​73161](https://github.com/NousResearch/hermes-agent/pull/73161), [#​72960](https://github.com/NousResearch/hermes-agent/pull/72960) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1))
##### Desktop performance (60fps wave 2)
- Streaming cost independent of transcript length; 60fps on real sessions (reflow-gated pins, adaptive flush); drag at 60fps with five streaming tabs; multitab streaming made fast ([#​71835](https://github.com/NousResearch/hermes-agent/pull/71835), [#​72504](https://github.com/NousResearch/hermes-agent/pull/72504), [#​72346](https://github.com/NousResearch/hermes-agent/pull/72346), [#​71780](https://github.com/NousResearch/hermes-agent/pull/71780) — [@​OutThisLife](https://github.com/OutThisLife))
- Hidden-pane timers paused (agents view, cron sidebar, floating pet), scroll/status loops stopped in busy sessions ([#​77651](https://github.com/NousResearch/hermes-agent/pull/77651) — [@​kshitijk4poor](https://github.com/kshitijk4poor)); idle CPU near zero in the background; sidebar/overlay render churn killed; statusbar + transcript stop re-rendering per token/sash-drag/session-switch; ⌘K opens instantly; renderer cold start keeps shiki/mermaid off the boot path ([#​75218](https://github.com/NousResearch/hermes-agent/pull/75218), [#​73698](https://github.com/NousResearch/hermes-agent/pull/73698), [#​72163](https://github.com/NousResearch/hermes-agent/pull/72163), [#​72245](https://github.com/NousResearch/hermes-agent/pull/72245), [#​72524](https://github.com/NousResearch/hermes-agent/pull/72524), [#​74665](https://github.com/NousResearch/hermes-agent/pull/74665), [#​73024](https://github.com/NousResearch/hermes-agent/pull/73024) — [@​OutThisLife](https://github.com/OutThisLife))
- State diagnostics (render + store churn counters) + a lint rule banning atom-mirrored refs so the stale-read bug class cannot return; Playwright E2E suite with visual regression diffs ([#​71925](https://github.com/NousResearch/hermes-agent/pull/71925), [#​71560](https://github.com/NousResearch/hermes-agent/pull/71560), [#​65805](https://github.com/NousResearch/hermes-agent/pull/65805) — [@​OutThisLife](https://github.com/OutThisLife), [@​teknium1](https://github.com/teknium1), [@​ethernet8023](https://github.com/ethernet8023))
##### 🖥️ CLI, TUI & Dashboard
- `!` shell mode; `/init` AGENTS.md generation; `/diff` (staged/all/session, cross-surface); `/context` breakdown; `/focus` reduced-output view; Ctrl+S prompt stash; persistent `/goal` indicator; multi-select clarify (checkboxes) across CLI/gateway/TUI ([#​72257](https://github.com/NousResearch/hermes-agent/pull/72257), [#​72178](https://github.com/NousResearch/hermes-agent/pull/72178), [#​72240](https://github.com/NousResearch/hermes-agent/pull/72240), [#​72242](https://github.com/NousResearch/hermes-agent/pull/72242), [#​72302](https://github.com/NousResearch/hermes-agent/pull/72302), [#​72262](https://github.com/NousResearch/hermes-agent/pull/72262), [#​72244](https://github.com/NousResearch/hermes-agent/pull/72244), [#​72188](https://github.com/NousResearch/hermes-agent/pull/72188) — [@​teknium1](https://github.com/teknium1), salvaging [@​SHL0MS](https://github.com/SHL0MS), [@​iRonin](https://github.com/iRonin), [@​gigi206](https://github.com/gigi206) + more)
- `hermes import-agent` — one-command migration from Claude Code / Codex CLI setups ([#​72190](https://github.com/NousResearch/hermes-agent/pull/72190) — [@​teknium1](https://github.com/teknium1))
- Per-turn summary line + live token flow in the spinner; cross-surface theme SDK (one skin themes CLI, TUI, and desktop, live) ([#​72246](https://github.com/NousResearch/hermes-agent/pull/72246), [#​68857](https://github.com/NousResearch/hermes-agent/pull/68857) — [@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife))
- TUI: reach the model picker without wrecking your draft + mid-turn switching; slash menu leads with your most-used skills; attachments live in the composer; Arabic (ar) locale with RTL across desktop/dashboard/agent ([#​74756](https://github.com/NousResearch/hermes-agent/pull/74756), [#​75931](https://github.com/NousResearch/hermes-agent/pull/75931), [#​75210](https://github.com/NousResearch/hermes-agent/pull/75210), [#​70870](https://github.com/NousResearch/hermes-agent/pull/70870) — [@​OutThisLife](https://github.com/OutThisLife))
- `hermes -w` startup \~14s → \~1.8s; global `--version` fast path; banner update-check 6× faster; dashboard lazy-loads routes + GROUP BY session stats; session filtering tabs (Chats/Automation/All) ([#​71637](https://github.com/NousResearch/hermes-agent/pull/71637), [#​62096](https://github.com/NousResearch/hermes-agent/pull/62096), [#​74188](https://github.com/NousResearch/hermes-agent/pull/74188), [#​72294](https://github.com/NousResearch/hermes-agent/pull/72294), [#​73362](https://github.com/NousResearch/hermes-agent/pull/73362), [#​73865](https://github.com/NousResearch/hermes-agent/pull/73865) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Runtime: Node 26 required across installers/heal/upgrade, managed Node/uv resolve before bare PATH, outdated managed trees heal to target major; brew + pip/PyPI wheel channels retired (shell installer / Docker / Nix are the supported channels) ([#​76459](https://github.com/NousResearch/hermes-agent/pull/76459), [#​68217](https://github.com/NousResearch/hermes-agent/pull/68217) — [@​ethernet8023](https://github.com/ethernet8023))
##### 🧩 Skills, Plugins & MCP
- **A2A v1.0** — Agent-to-Agent protocol plugin (closes [#​514](https://github.com/NousResearch/hermes-agent/issues/514)) ([#​77109](https://github.com/NousResearch/hermes-agent/pull/77109) — [@​teknium1](https://github.com/teknium1))
- Curator: surface unmanaged skills + `curator adopt`; skill-description truncation surfaced to authors; grounded-citations skill (+ fact-checking mode); simplify-code v1.1; tldraw-offline scripting skill ([#​71648](https://github.com/NousResearch/hermes-agent/pull/71648), [#​70519](https://github.com/NousResearch/hermes-agent/pull/70519), [#​71698](https://github.com/NousResearch/hermes-agent/pull/71698), [#​77104](https://github.com/NousResearch/hermes-agent/pull/77104), [#​70440](https://github.com/NousResearch/hermes-agent/pull/70440), [#​66896](https://github.com/NousResearch/hermes-agent/pull/66896) — [@​teknium1](https://github.com/teknium1))
- Office skills bundled: docx, xlsx, pdf + refreshed powerpoint; skills-tree debloat continues (yuanbao, segment-anything, jupyter, heartmula, audiocraft → optional-skills; claude-marketplace source removed; hub restructure absorbing themes/desktop-plugins/tui-widgets) ([#​68595](https://github.com/NousResearch/hermes-agent/pull/68595), [#​70452](https://github.com/NousResearch/hermes-agent/pull/70452)–[#​70456](https://github.com/NousResearch/hermes-agent/pull/70456), [#​73903](https://github.com/NousResearch/hermes-agent/pull/73903) — [@​teknium1](https://github.com/teknium1))
- MCP: Comfy Cloud catalog entry with curated 20-tool default; hidden-whitespace warnings in MCP config; pinecone-research optional skill ([#​66112](https://github.com/NousResearch/hermes-agent/pull/66112), [#​75736](https://github.com/NousResearch/hermes-agent/pull/75736), [#​70512](https://github.com/NousResearch/hermes-agent/pull/70512) — [@​teknium1](https://github.com/teknium1))
- MCP lazy server startup from a fingerprint-keyed on-disk tool-schema cache — configured servers no longer all boot at session start (design from [#​56832](https://github.com/NousResearch/hermes-agent/issues/56832)) ([#​77511](https://github.com/NousResearch/hermes-agent/pull/77511) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- NeMo Relay observability integration — re-landed after an in-window revert, on stable NeMo Relay 0.6 ([#​67607](https://github.com/NousResearch/hermes-agent/pull/67607) — [@​afourniernv](https://github.com/afourniernv))
- Gateway health & diagnostics OTLP export ([#​64536](https://github.com/NousResearch/hermes-agent/pull/64536) — [@​victor-kyriazakos](https://github.com/victor-kyriazakos))
##### 🔒 Security & Reliability
- Iron-proxy credential-injection egress firewall re-landed ([#​70848](https://github.com/NousResearch/hermes-agent/pull/70848) — [@​teknium1](https://github.com/teknium1))
- DNS-pinned SSRF-safe fetches + Slack CDN allowlist; strict redaction at compaction boundaries; ReDoS eliminated in config-key redaction patterns; prose words embedding a secret keyword no longer masked ([#​70193](https://github.com/NousResearch/hermes-agent/pull/70193), [#​69294](https://github.com/NousResearch/hermes-agent/pull/69294), [#​76083](https://github.com/NousResearch/hermes-agent/pull/76083), [#​67776](https://github.com/NousResearch/hermes-agent/pull/67776) — [@​teknium1](https://github.com/teknium1))
- Tier-3 credential reads scoped (FAL/XAI/VERCEL/DAYTONA/GITHUB presence checks etc.); CVE dependency pins refreshed (cryptography, starlette, python-multipart); hindsight env file 0600; /model moved off the gateway event loop ([#​75888](https://github.com/NousResearch/hermes-agent/pull/75888), [#​72362](https://github.com/NousResearch/hermes-agent/pull/72362) — [@​teknium1](https://github.com/teknium1))
- Windows hardening wave: text-mode subprocess decode bug class closed repo-wide, console flashes hidden across daemons/env probes/LSP/installer paths, residual encoding gaps (MCP stdio, gateway update I/O, STT/TTS, desktop spawn) ([#​70875](https://github.com/NousResearch/hermes-agent/pull/70875), [#​70205](https://github.com/NousResearch/hermes-agent/pull/70205), [#​70264](https://github.com/NousResearch/hermes-agent/pull/70264), [#​71014](https://github.com/NousResearch/hermes-agent/pull/71014) — [@​teknium1](https://github.com/teknium1), salvaging several community PRs)
- State/session integrity: four session-state fixes (safe close tracking, flush-cursor class fix, row-retry, usage-PK healer); compact v23 FTS layout + `hermes sessions optimize` + CJK-bigram FTS; read-path split with per-thread read-only connections ([#​75883](https://github.com/NousResearch/hermes-agent/pull/75883), [#​65798](https://github.com/NousResearch/hermes-agent/pull/65798), [#​69423](https://github.com/NousResearch/hermes-agent/pull/69423), [#​73344](https://github.com/NousResearch/hermes-agent/pull/73344) — [@​teknium1](https://github.com/teknium1), [@​kshitijk4poor](https://github.com/kshitijk4poor))
- OpenViking memory-provider hardening — fail closed on blocked endpoints, server verification before credentials are sent, config.yaml-first settings ([#​77747](https://github.com/NousResearch/hermes-agent/pull/77747) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Credential pool: reset-aware primary restore (stay on fallback until the rate-limit window resets) + deferred-refresh locking fixes; FTS UPDATE triggers narrowed with fail-closed CJK migration ([#​77631](https://github.com/NousResearch/hermes-agent/pull/77631), [#​77628](https://github.com/NousResearch/hermes-agent/pull/77628) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
- Config-driven memory allocator trim with telemetry; holographic memory vectors stored float32; loop-invariant HRR encodes hoisted ([#​76905](https://github.com/NousResearch/hermes-agent/pull/76905), [#​76917](https://github.com/NousResearch/hermes-agent/pull/76917), [#​76881](https://github.com/NousResearch/hermes-agent/pull/76881) — [@​kshitijk4poor](https://github.com/kshitijk4poor))
##### 🐛 Notable Bug Fixes
- Voice: full-duplex interruption during generation AND playback; whole-turn desktop speech; auto-TTS delivery gaps ([#​74223](https://github.com/NousResearch/hermes-agent/pull/74223), [#​69936](https://github.com/NousResearch/hermes-agent/pull/69936), [#​73508](https://github.com/NousResearch/hermes-agent/pull/73508) — [@​teknium1](https://github.com/teknium1))
- Desktop: Stop parks the queue instead of firing the next queued prompt; branch-in-new-chat restart loss; false remote-gateway reauthentication; cross-session composer leaks ([#​68725](https://github.com/NousResearch/hermes-agent/pull/68725), [#​71960](https://github.com/NousResearch/hermes-agent/pull/71960), [#​68250](https://github.com/NousResearch/hermes-agent/pull/68250), [#​70986](https://github.com/NousResearch/hermes-agent/pull/70986) — [@​SHL0MS](https://github.com/SHL0MS), [@​alelpoan](https://github.com/alelpoan), [@​helix4u](https://github.com/helix4u), [@​OutThisLife](https://github.com/OutThisLife))
- Gateway: session lists scoped before limiting; relay-backed home delivery after restart; timeline display events persisted ([#​65509](https://github.com/NousResearch/hermes-agent/pull/65509), [#​70102](https://github.com/NousResearch/hermes-agent/pull/70102), [#​69771](https://github.com/NousResearch/hermes-agent/pull/69771) — [@​GodsBoy](https://github.com/GodsBoy), [@​victor-kyriazakos](https://github.com/victor-kyriazakos), [@​ethernet8023](https://github.com/ethernet8023))
- Agent: context-length fallback logging + batch trajectory durability; Codex OAuth context windows revalidated against the live catalog ([#​76027](https://github.com/NousResearch/hermes-agent/pull/76027), [#​68554](https://github.com/NousResearch/hermes-agent/pull/68554) — [@​kshitijk4poor](https://github.com/kshitijk4poor), [@​teknium1](https://github.com/teknium1))
- ...plus roughly 770 more `fix:` PRs across every subsystem this window.
##### 👥 Contributors
**647 contributors** shipped this release (commit authors, co-authors, and salvaged-PR credits).
##### Core
[@​teknium1](https://github.com/teknium1), [@​OutThisLife](https://github.com/OutThisLife) (desktop, voice, perf), [@​kshitijk4poor](https://github.com/kshitijk4poor) (perf, caching, salvage), [@​ethernet8023](https://github.com/ethernet8023) (runtime, E2E, desktop), [@​benbarclay](https://github.com/benbarclay) (relay, HSP, auth)
##### All Contributors (alphabetical)
[@​02356abc](https://github.com/02356abc), [@​0301chris](https://github.com/0301chris), [@​0xAlcibiades](https://github.com/0xAlcibiades), [@​0xDevNinja](https://github.com/0xDevNinja), [@​0xLeathery](https://github.com/0xLeathery), [@​0xprincess](https://github.com/0xprincess), [@​0xr00tf3rr3t](https://github.com/0xr00tf3rr3t), [@​100yenadmin](https://github.com/100yenadmin),
[@​2001Y](https://github.com/2001Y), [@​3ssiri](https://github.com/3ssiri), [@​55nx954gn6-debug](https://github.com/55nx954gn6-debug), [@​686f6c61](https://github.com/686f6c61), [@​87degrees](https://github.com/87degrees), [@​aaronlab](https://github.com/aaronlab), [@​abundantbeing](https://github.com/abundantbeing), [@​Adolanium](https://github.com/Adolanium),
[@​adriansotomora](https://github.com/adriansotomora), [@​adurham](https://github.com/adurham), [@​afourniernv](https://github.com/afourniernv), [@​afurm](https://github.com/afurm), [@​AgenticSpark](https://github.com/AgenticSpark), [@​ahmadashfq](https://github.com/ahmadashfq), [@​AhmetArif0](https://github.com/AhmetArif0), [@​ai-ag2026](https://github.com/ai-ag2026),
[@​AIalliAI](https://github.com/AIalliAI), [@​aider4ryder](https://github.com/aider4ryder), [@​airclear](https://github.com/airclear), [@​ajzrva-sys](https://github.com/ajzrva-sys), [@​akattelu](https://github.com/akattelu), [@​AKAZIK-py](https://github.com/AKAZIK-py), [@​akb4q](https://github.com/akb4q), [@​akshan-main](https://github.com/akshan-main), [@​AlanBurningsuit](https://github.com/AlanBurningsuit),
[@​alelpoan](https://github.com/alelpoan), [@​AlexFucuson9](https://github.com/AlexFucuson9), [@​AlexxRussell](https://github.com/AlexxRussell), [@​AllardQuek](https://github.com/AllardQuek), [@​alt-glitch](https://github.com/alt-glitch), [@​aman-merchant](https://github.com/aman-merchant), [@​amanning3390](https://github.com/amanning3390), [@​amathxbt](https://github.com/amathxbt),
[@​aml1973](https://github.com/aml1973), [@​amoreno16003](https://github.com/amoreno16003), [@​AndrewMoryakov](https://github.com/AndrewMoryakov), [@​andrexibiza](https://github.com/andrexibiza), [@​andynguyendk](https://github.com/andynguyendk), [@​andyylin](https://github.com/andyylin), [@​aneym](https://github.com/aneym), [@​angelos](https://github.com/angelos),
[@​aniruddhaadak80](https://github.com/aniruddhaadak80), [@​AnnasMazhar](https://github.com/AnnasMazhar), [@​annguyenNous](https://github.com/annguyenNous), [@​anoopmehendale-cue](https://github.com/anoopmehendale-cue), [@​AnthonyFrancis](https://github.com/AnthonyFrancis), [@​arcabotai](https://github.com/arcabotai), [@​ArcherQAQ](https://github.com/ArcherQAQ),
[@​Ares4Tech](https://github.com/Ares4Tech), [@​arimu1](https://github.com/arimu1), [@​arnoldfrancisca](https://github.com/arnoldfrancisca), [@​asimons81](https://github.com/asimons81), [@​asorry75](https://github.com/asorry75), [@​AtakanGs](https://github.com/AtakanGs), [@​ATran28](https://github.com/ATran28), [@​austinpickett](https://github.com/austinpickett),
[@​Automata-intelligentsia](https://github.com/Automata-intelligentsia), [@​awain7](https://github.com/awain7), [@​aweiker](https://github.com/aweiker), [@​aydnOktay](https://github.com/aydnOktay), [@​ayushere](https://github.com/ayushere), [@​b](https://github.com/b), [@​baau](https://github.com/baau), [@​baauzi](https://github.com/baauzi), [@​baenregod](https://github.com/baenregod),
[@​bakhtiersizhaev](https://github.com/bakhtiersizhaev), [@​Baophan00](https://github.com/Baophan00), [@​baoyu0](https://github.com/baoyu0), [@​Bartok9](https://github.com/Bartok9), [@​basilalshukaili](https://github.com/basilalshukaili), [@​BB-light](https://github.com/BB-light), [@​bbopen](https://github.com/bbopen), [@​Beandon13](https://github.com/Beandon13),
[@​beardedeagle](https://github.com/beardedeagle), [@​bedirhancode](https://github.com/bedirhancode), [@​benbarclay](https://github.com/benbarclay), [@​benegessarit](https://github.com/benegessarit), [@​benjamin2026-dot](https://github.com/benjamin2026-dot), [@​bennybuoy](https://github.com/bennybuoy), [@​BenSheridanEdwards](https://github.com/BenSheridanEdwards),
[@​BKStock](https://github.com/BKStock), [@​BlackishGreen33](https://github.com/BlackishGreen33), [@​bnikanjam](https://github.com/bnikanjam), [@​bounce12340](https://github.com/bounce12340), [@​Bounty13](https://github.com/Bounty13), [@​bpross](https://github.com/bpross), [@​briandevans](https://github.com/briandevans), [@​bricelb](https://github.com/bricelb), [@​brunopirz](https://github.com/brunopirz),
[@​bryanneva](https://github.com/bryanneva), [@​byshubham](https://github.com/byshubham), [@​camaleonidas](https://github.com/camaleonidas), [@​canorionen](https://github.com/canorionen), [@​carbongotfound](https://github.com/carbongotfound), [@​carljborg](https://github.com/carljborg), [@​carlotestor](https://github.com/carlotestor), [@​carrion256](https://github.com/carrion256),
[@​caseyanthony](https://github.com/caseyanthony), [@​cat-thats-fat](https://github.com/cat-thats-fat), [@​Cdddo](https://github.com/Cdddo), [@​ceverson70](https://github.com/ceverson70), [@​chancelu](https://github.com/chancelu), [@​chaos-xxl](https://github.com/chaos-xxl), [@​CharlesMcquade](https://github.com/CharlesMcquade), [@​chazmaniandinkle](https://github.com/chazmaniandinkle),
[@​chefboyrdave21](https://github.com/chefboyrdave21), [@​chelsealong](https://github.com/chelsealong), [@​Christopher-Schulze](https://github.com/Chr…
…-29661bf6 feat(voice): on-device wake words with open-vocabulary phrases and multi-profile voice routing
… turn-timeout Two follow-ups from the voice PR (NousResearch#70509). 1. macOS ARM64 onnx migration. Existing users who pinned openwakeword.inference_framework=onnx before the tflite fix landed kept a wake word that arms but never fires (ONNX's embedding model is broken on Apple Silicon, upstream NousResearch#336). New resolve_inference_framework() honors an explicit framework everywhere ONNX actually works, but coerces the one provably-dead combination (explicit onnx + macOS ARM64) to tflite with a one-time warning. No config mutation; empty still falls back to the platform default. Both read sites (engine init + requirements check) route through the shared resolver. 2. Voice turn-timeout leak. Each listen cycle reassigned turnTimeoutRef without clearing the prior 60s timer, so a stale timer from an earlier cycle could fire handleTurn() mid-way through a later listen — after enough idle re-listens this wedged the loop into a non-re-arming state (the 'voice chat deactivates after ~a minute' report). Clear before re-arm. Tests: 64 wake tests (added onnx-coercion / intel-kept / tflite-kept / empty-default cases; updated the stale 'explicit onnx kept on ARM64' test that encoded the old broken behavior), 39 desktop voice/wake vitest, tsc + eslint clean.
… turn-timeout Two follow-ups from the voice PR (NousResearch#70509). 1. macOS ARM64 onnx migration. Existing users who pinned openwakeword.inference_framework=onnx before the tflite fix landed kept a wake word that arms but never fires (ONNX's embedding model is broken on Apple Silicon, upstream NousResearch#336). New resolve_inference_framework() honors an explicit framework everywhere ONNX actually works, but coerces the one provably-dead combination (explicit onnx + macOS ARM64) to tflite with a one-time warning. No config mutation; empty still falls back to the platform default. Both read sites (engine init + requirements check) route through the shared resolver. 2. Voice turn-timeout leak. Each listen cycle reassigned turnTimeoutRef without clearing the prior 60s timer, so a stale timer from an earlier cycle could fire handleTurn() mid-way through a later listen — after enough idle re-listens this wedged the loop into a non-re-arming state (the 'voice chat deactivates after ~a minute' report). Clear before re-arm. Tests: 64 wake tests (added onnx-coercion / intel-kept / tflite-kept / empty-default cases; updated the stale 'explicit onnx kept on ARM64' test that encoded the old broken behavior), 39 desktop voice/wake vitest, tsc + eslint clean.
Summary
Adds an opt-in, fully on-device wake-word system across all three local surfaces (CLI, TUI, desktop GUI) — say a wake phrase, Hermes opens a fresh session and starts hands-free voice. Goes beyond the "Hey Siri" pattern with open-vocabulary phrases (any typed phrase, zero training) and multi-profile routing: say "hey coder" and the desktop live-switches to the
coderprofile, opens a session there, and starts voice — every wake-enabled profile is addressable by name from one listener.Consolidated salvage of #53378 (@OutThisLife) + #58539 (@omid3098) with authorship preserved, extended with open-vocab, routing, UX toggles, and live TTS calibration.
Features
tools/wakewords/); default engine, no downloads, no keys. Off by default.provider: sherpa) — sherpa-onnx keyword spotting tokenizes the configuredphraseat runtime against a small streaming zipformer (~13 MB, one-time fetch). Any phrase works with zero training.wake_word.enabled: true(phrase defaults tohey <profile>);wake.detectedcarries the matched profile; the desktop live-swaps to it (profile-rail path) → fresh session → voice. CLI/TUI (single-profile processes) print thehermes -p <profile>switch hint. Opt out viawake_word.profile_routing: false./wake on|off|status; TUI off is reconnect-sticky for the session.surface: autorace); non-owners can't steal callbacks, pause, stop, or hijack event delivery.PORCUPINE_ACCESS_KEY) remains as the third engine.Live validation (TTS matrix)
96 synthesized utterances — 6 enrolled profile phrases × 4 edge-tts voices (US m/f, UK, AU) + 4 negative phrases × 4 voices — fed as 16 kHz PCM through the real multi-profile engine:
Sensitivity mapping calibrated from this matrix (default now lands on sherpa's recommended 0.25 threshold).
Footprint & invariants
/wake+ gateway RPCs (wake.start/stop/pause/resume/status,wake.detected)..envonly for the secret — everything behavioral inconfig.yaml; onlyPORCUPINE_ACCESS_KEY(a credential) is an env var.Test plan
tests/tools/test_wake_word.py— 38 tests: config gates, engine dispatch, bundled-model resolution, sherpa runtime tokenization, multi-profile enrollment/routing/opt-out, detector lifecycle, singleton lease (hermetic; numpy/sherpa stubbed).tests/test_tui_gateway_server.py— wake RPC ownership + event payload (incl.profilefield).tsc --noEmitclean ×2 · ruff clean.Closes #58539. #53378 closed with credit.
Infographic