hermes-agent/tools
kshitij 72c63aa586 fix(stt): close idle-unload races — strong model ref, single long-lived watcher
Review pass on the idle-unload feature found two material concurrency
bugs; both fixed here with a regression guard:

1. Unload-vs-use null deref (HIGH): _transcribe_local re-read the
   module global _local_model at the transcribe call site. An idle
   unload firing between the model load and transcribe() evaluated
   None.transcribe → AttributeError → user-visible 'Local
   transcription failed'. The window was real: the idle timer was only
   touched AFTER a successful transcription, so a voice note arriving
   exactly as the timeout expired raced the watcher directly.
   Fix: bind a strong local reference under the model lock and use it
   for the whole transcription (the watcher can null the global at any
   time; this in-flight call keeps its instance — the generator holds
   self, so no use-after-free). Also touch the idle timer at the START
   of transcription so a long in-flight transcribe can't be counted as
   idle time. The CUDA-fallback retry path gets the same treatment
   (locked global write, local ref use).

2. Watcher replacement race + response-path join (MEDIUM/HIGH): the
   old design stopped and re-started the watcher after EVERY
   transcription with an unlocked set/join(5)/clear/start sequence on
   shared globals. Two concurrent voice messages could interleave to
   leave TWO live watchers (one with a stale, shorter timeout — a
   raised unload_after_idle_seconds could still unload on the old
   value), and the join(timeout=5) sat on the user-visible response
   path (a watcher blocked on _local_model_lock during a concurrent
   multi-second model load stalls the reply up to 5s).
   Fix: single long-lived watcher under a management lock — started
   only when none is alive (per-transcription cost: one lock + one
   is_alive check), re-reads the configured timeout from config every
   cycle (config edits now apply within one 30s interval, without
   waiting for the next voice message — previously undocumented), and
   stands down without unloading when the timeout is set to 0
   mid-idle.

Tests: 17 now — idempotent start (same thread, no churn), config
re-read + stand-down-when-disabled, and the race guard
(unload firing mid-transcription must not fail the in-flight call).
The race guard is mutation-verified: reverting the fix (re-reading
the global at the call site) makes it fail with the exact NoneType
error; the fixed code passes.
2026-08-07 19:26:12 +05:30
..
computer_use
environments
neutts_samples
wakewords
__init__.py
ansi_strip.py
approval.py refactor: fold /simplify-code findings 2026-08-06 17:03:10 +05:30
async_delegation.py
audio_container.py
binary_extensions.py
blueprints.py fix(cron): surface initial scheduler registration failures 2026-08-07 17:45:06 +05:30
browser_camofox.py
browser_camofox_state.py
browser_cdp_tool.py
browser_dialog_tool.py
browser_supervisor.py
browser_tool.py
budget_config.py
checkpoint_manager.py
clarify_gateway.py
clarify_tool.py
close_terminal_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
code_execution_tool.py fix: preserve session history when a turn crashes 2026-08-06 23:08:23 -05:00
computer_use_tool.py
credential_files.py
cronjob_tools.py refactor(cron): polish registration partial-failure surfaces 2026-08-07 17:45:06 +05:30
daemon_pool.py
debug_helpers.py
delegate_tool.py fix(delegation): bind steering to session generation 2026-08-06 09:40:27 -07:00
delegation_live_log.py
desktop_ui.py
discord_tool.py
env_passthrough.py
env_probe.py
fal_common.py
feishu_doc_tool.py
feishu_drive_tool.py
file_operations.py
file_state.py
file_tools.py fix(read_file): stop promising anydoc conversion in the tool schema 2026-08-05 22:03:47 -07:00
flux3_video_tool.py
focus_pane_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
fuzzy_match.py
homeassistant_tool.py
hook_output_spill.py
image_generation_tool.py
image_source.py
interrupt.py
kanban_tools.py
lazy_deps.py refactor: derive teams install hint via feature_install_command(venv_pip=True) 2026-08-07 13:28:43 +05:30
managed_tool_gateway.py
mcp_dashboard_oauth.py
mcp_oauth.py
mcp_oauth_manager.py
mcp_schema_cache.py
mcp_stdio_watchdog.py
mcp_tool.py
memory_tool.py
microsoft_graph_auth.py
microsoft_graph_client.py
neutts_synth.py
open_preview_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
openrouter_client.py
osv_check.py
patch_parser.py
path_security.py
process_registry.py fix(secrets): redact command in process checkpoint file (#77484) 2026-08-07 17:01:23 +05:30
project_tools.py
react_to_message_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
read_extract.py fix(read_extract): cap anydoc input size before conversion 2026-08-05 22:02:42 -07:00
read_preview_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
read_terminal_tool.py fix(agent): the desktop's tools reach it on remote and cloud backends too 2026-08-06 19:35:47 -06:00
registry.py
schema_sanitizer.py
send_message_tool.py
session_search_tool.py
skill_manager_tool.py
skill_provenance.py
skill_usage.py
skills_ast_audit.py
skills_guard.py
skills_hub.py
skills_sync.py
skills_sync_client.py
skills_tool.py
slash_confirm.py
terminal_hints.py
terminal_tool.py fix: close simplify-pass findings — scheduler sibling site + home-unresolvable totality 2026-08-06 17:36:40 +05:30
thread_context.py
threat_patterns.py
tirith_security.py
todo_tool.py
tool_backend_helpers.py
tool_output_limits.py
tool_result_storage.py
tool_search.py
transcription_tools.py fix(stt): close idle-unload races — strong model ref, single long-lived watcher 2026-08-07 19:26:12 +05:30
tts_streaming.py
tts_text_normalize.py
tts_tool.py
url_safety.py
video_generation_tool.py
vision_tools.py fix(vision): stream image and video downloads with chunk-by-chunk size cap 2026-08-07 18:50:39 +05:30
voice_mode.py fix(voice): early-exit sliding window on match, clear barge phase in finally, fix test helper 2026-08-07 14:38:02 +05:30
wake_word.py
web_tools.py
website_policy.py
working_diff.py
write_approval.py
x_search_tool.py
xai_http.py
xai_video_tools.py
yuanbao_tools.py