mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(desktop): deliver wake.detected over the websocket, not stdio
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.
This commit is contained in:
parent
a6aada24f5
commit
813d9ffad0
1 changed files with 22 additions and 3 deletions
|
|
@ -17602,6 +17602,12 @@ def _voice_record_key() -> str:
|
|||
_wake_lock = threading.Lock()
|
||||
_wake_active = False
|
||||
_wake_event_sid = ""
|
||||
# Transport captured at wake.start time. The detector callback fires on a
|
||||
# background thread where the request-scoped transport ContextVar is unset, so
|
||||
# write_json would fall back to stdio and the event would never cross the
|
||||
# desktop's websocket (#wake-detected-not-delivered). We pin the arming
|
||||
# request's transport here and bind it for the emit.
|
||||
_wake_transport: "Optional[Transport]" = None
|
||||
|
||||
|
||||
def _wake_is_active() -> bool:
|
||||
|
|
@ -17623,13 +17629,23 @@ def _wake_on_detect() -> None:
|
|||
"""Detector-thread callback: tell the client to open a fresh voice session."""
|
||||
with _wake_lock:
|
||||
sid = _wake_event_sid
|
||||
transport = _wake_transport
|
||||
try:
|
||||
from tools.wake_word import wake_phrase
|
||||
phrase = wake_phrase()
|
||||
except Exception:
|
||||
phrase = ""
|
||||
logger.info("wake.detected: emitting to sid=%r", sid)
|
||||
_emit("wake.detected", sid, {"phrase": phrase})
|
||||
logger.info("wake.detected: emitting to sid=%r (transport=%s)",
|
||||
sid, type(transport).__name__ if transport else None)
|
||||
# Bind the arming request's transport so write_json reaches the right peer
|
||||
# (WS for desktop/dashboard) instead of falling back to stdio on this
|
||||
# background thread.
|
||||
token = bind_transport(transport) if transport is not None else None
|
||||
try:
|
||||
_emit("wake.detected", sid, {"phrase": phrase})
|
||||
finally:
|
||||
if token is not None:
|
||||
reset_transport(token)
|
||||
|
||||
|
||||
@method("wake.start")
|
||||
|
|
@ -17639,7 +17655,7 @@ def _(rid, params: dict) -> dict:
|
|||
Idempotent and gated: returns ``{started: False, reason}`` when the wake
|
||||
word is disabled, scoped to another surface, or its deps/mic aren't ready.
|
||||
"""
|
||||
global _wake_active, _wake_event_sid
|
||||
global _wake_active, _wake_event_sid, _wake_transport
|
||||
surface = str(params.get("surface") or "auto").strip().lower()
|
||||
try:
|
||||
from tools.wake_word import (
|
||||
|
|
@ -17663,6 +17679,9 @@ def _(rid, params: dict) -> dict:
|
|||
|
||||
with _wake_lock:
|
||||
_wake_event_sid = params.get("session_id") or _wake_event_sid
|
||||
# Capture the live transport (WS for desktop) so the background detector
|
||||
# thread can route wake.detected back to this client, not stdio.
|
||||
_wake_transport = current_transport() or _wake_transport
|
||||
try:
|
||||
start_listening(_wake_on_detect, config=cfg)
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue