feat(desktop): auto-speak text responses via voice.auto_tts config - #46672
Open
mustafa-ramax wants to merge 1 commit into
Open
feat(desktop): auto-speak text responses via voice.auto_tts config#46672mustafa-ramax wants to merge 1 commit into
mustafa-ramax wants to merge 1 commit into
Conversation
Adds auto-TTS on response completion. Fixes double-play (WS client gate), session-switch trigger, and long-response timeout (scoped 75s speak timeout).
Collaborator
teknium1
reviewed
Jul 14, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for the accessibility-focused auto-speak work. The session-switch guarantee remains needed: current main subscribes the auto-speak controller to global $messages in apps/desktop/src/app/chat/composer/hooks/use-auto-speak-replies.ts:75, matching the stale-subscription race documented by linked issue #59014.
Problems
- Main has since implemented auto-speak through
useComposerVoice→useAutoSpeakReplies(apps/desktop/src/app/chat/composer/hooks/use-composer-voice.ts:142, commitsfcdc05c89andcf05b3868). The proposedChatBarbusy-edge effect would create a second controller rather than extend the current one. - No regression test accompanies this branch, and current main has no tracked test for
use-auto-speak-replies.
Suggested changes
- Salvage the session-switch guard into
apps/desktop/src/app/chat/composer/hooks/use-auto-speak-replies.tsand test the synchronous$messagesupdate-before-effect-cleanup sequence. - Port the scoped speak timeout independently at
apps/desktop/src/hermes.ts:1007.
This is an automated hermes-sweeper review.
| @@ -1528,6 +1530,38 @@ export function ChatBar({ | |||
| } | |||
Contributor
There was a problem hiding this comment.
Current main has a single auto-speech controller in hooks/use-auto-speak-replies.ts, composed through useComposerVoice. Please port the session-switch guard into that controller rather than adding a second busy-edge effect here; otherwise the current playback/deduplication flow would be duplicated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds auto-TTS on response completion. Fixes double-play (WS client gate), session-switch trigger, and long-response timeout (scoped 75s speak timeout).
What does this PR do?
Adds auto-TTS to the Hermes desktop app — when voice.auto_tts: true is set in config.yaml, Hermes automatically reads its responses aloud once a turn completes, with no extra interaction needed.
This is a genuine quality-of-life feature: you can send a message, switch to another app or task, and hear Hermes respond in voice when it's done — hands-free, eyes-free. It's also a meaningful accessibility improvement for users with visual impairments or motor difficulties who benefit from audio output without having to manually trigger read-aloud each time.
Three bugs were diagnosed and fixed as part of this work:
Double-play — the backend gateway was also speaking responses server-side (CLI parity code), causing every reply to play twice. Fixed by gating server-side speech to non-WebSocket transports only (TUI/CLI), since the desktop client handles its own playback.
Session-switch trigger — switching sessions caused the newly-loaded message to auto-play. Fixed by resetting the busy-gate when sessionId changes (ChatBar is persistent and doesn't remount on switch).
Timeout on long responses — edge-tts synthesizes the entire response in one call; long replies exceeded the app-wide 15s HTTP timeout. Fixed with a scoped 75s timeout on the speak endpoint only, matching the backend's 60s synthesis cap.
Related Issue
Fixes #
Type of Change
Changes Made
tui_gateway/server.py — gate server-side speak_text to non-WS transports only; allow /voice tts toggle without requiring voice mode; fall back to config.voice.auto_tts in _voice_tts_enabled()
apps/desktop/src/types/hermes.ts — add auto_tts?: boolean to voice config type
apps/desktop/src/app/session/hooks/use-hermes-config.ts — expose autoTtsEnabled from config.voice?.auto_tts
apps/desktop/src/app/desktop-controller.tsx — thread autoTtsEnabled down to ChatView
apps/desktop/src/app/chat/index.tsx — thread autoTtsEnabled through to ChatBar
apps/desktop/src/app/chat/composer/types.ts — add autoTtsEnabled?: boolean to ChatBarProps
apps/desktop/src/app/chat/composer/index.tsx — auto-TTS effect with session-switch gate + double-play dedup; move VoicePlaybackActivity outside the scroll-fade div so the stop controller stays visible
apps/desktop/src/hermes.ts — scoped 75s timeout on speakText() (speak endpoint only)
How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AFor New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs
Before: long responses triggered Error: Timed out connecting to Hermes backend after 15000ms and every response played twice through the host speakers.
After: single clean playback through the Electron client with a visible stop controller; long responses complete without timeout; session switching is silent.