hermes-agent/tools
teknium1 ed33ebca1d refactor: canonical config loaders for behavioral reads + guarded raw-read primitive (kills the managed-scope/env-expansion drift class)
The disease: ~15 scattered raw yaml.safe_load(config.yaml) reads that
silently miss managed-scope overlay, ${ENV_VAR} expansion, profile-aware
pathing, and root-model normalization. Every new config feature needed an
N-site sweep (incident chain 9cbcc0c9c8732293cf87b0e47a98f91928aa0443). This commit assigns every raw read to an owner and adds a
lint-guard test so the class cannot regrow.

New primitive (additive-only change to hermes_cli/config.py):
  read_user_config_raw(path=None) — reads the user file EXACTLY as
  written; docstring states it is ONLY legal for write-back round-trips
  and raw-file diagnostics. Behavioral reads must use
  load_config()/load_config_readonly().

BEHAVIOR FIXES (class-a sites migrated to a canonical loader — these
previously read values that could DIFFER from the effective config):

  gateway/run.py _try_resolve_fallback_provider → _load_gateway_runtime_config
    keys: fallback_providers/fallback_model (provider, model, base_url,
    api_key). Drift fixed: a managed-pinned fallback chain was ignored;
    an api_key of "${OPENROUTER_API_KEY}" reached the resolver unexpanded.
  gateway/run.py GatewayRunner._load_provider_routing → same loader
    key: provider_routing. Drift fixed: managed-pinned routing prefs and
    ${VAR} templates were ignored.
  gateway/run.py GatewayRunner._load_fallback_model → same loader
    keys: fallback chain. Same drift as above.
  gateway/run.py GatewayRunner._refresh_fallback_model
    keeps the raw primitive (its last-known-good-on-parse-failure contract
    forbids the fail-open loader, which returns {} on a torn write) but now
    applies managed overlay + env expansion inline. Drift fixed: chain
    edits under managed scope / env templates were previously frozen out.
  tui_gateway/server.py _load_cfg (72 behavioral call sites)
    now = raw read + managed overlay (pre-existing) + NEW ${VAR} expansion,
    split from a new _load_cfg_raw() write-back primitive. Drift fixed:
    e.g. custom_prompt: "hello ${VAR}", agent.system_prompt, model,
    api_key/base_url templates reached sessions unexpanded. DEFAULT_CONFIG
    is deliberately NOT merged (callers treat missing keys as unset;
    `_load_cfg() == {}` sentinels and _save_cfg round-trips depend on it).
  tui_gateway/server.py _profile_configured_cwd
    keys: terminal.cwd of a NON-launch profile. Drift fixed: managed
    overlay + ${VAR} expansion now apply (load_config() would resolve the
    wrong profile's home, so the raw primitive + inline pipeline is used).
  plugins/platforms/telegram/adapter.py _reload_dm_topics_from_config
    → load_config_readonly(). keys: platforms.telegram.extra.dm_topics.
    Drift fixed: managed overlay + profile-aware pathing + expansion.
  plugins/memory/holographic _load_plugin_config → load_config_readonly().
    keys: plugins.hermes-memory-store.*. Same drift class.

WRITE-BACK ROUND-TRIPS (class-b: stay raw BY DESIGN via read_user_config_raw;
merging defaults/overlay would pollute the saved user file):
  gateway/slash_commands.py: model persist x2, _save_gateway_config_key,
    memory/skills write_approval toggles
  gateway/platforms/yuanbao.py auto-sethome
  tui_gateway/server.py _write_config_key + all cfg→_save_cfg blocks
    (reasoning show/hide/full/clamp, details_mode[.section], prompt)
    → new _load_cfg_raw()
  plugins/memory/holographic save_config

RAW-FILE DIAGNOSTICS + presence-sensitive bridges (class-c: stay raw,
now via the shared primitive with an explanatory comment):
  hermes_cli/doctor.py x5 (model validation, stale-root-keys, .env drift,
    deprecation sweep, memory-provider probe — the latter two keep their
    inline managed overlay where they had one)
  gateway/run.py _bridge_max_turns_from_config and the module-level
    TERMINAL_*/HERMES_* env bridge (bridging merged defaults would export
    all of DEFAULT_CONFIG into the environment; both keep their inline
    overlay + expansion)
  hermes_cli/send_cmd.py env bridge (same presence-sensitivity)
  hermes_cli/gateway.py multiplex-conflict probe (reads the DEFAULT root's
    config, not the active profile's — load_config is the wrong owner)
  hermes_cli/profiles.py / hermes_cli/web_server.py / tools/wake_word.py
    multi-profile reads (load_config targets only the ACTIVE profile home)
  cron/jobs.py _resolve_default_model_snapshot and cron/scheduler.py
    run_job config read keep their existing inline overlay+expansion but
    now share the primitive (their fail-open + last-value semantics and
    the deliberate no-defaults merge are preserved exactly).

Failure-semantics audit: every migrated site preserves its exact previous
behavior on missing file ({} / early return) and parse failure (raise into
the caller's existing except, warn, last-known-good, or fail-open) —
read_user_config_raw intentionally mirrors bare open()+safe_load semantics
(raises on parse errors, {} only on FileNotFoundError/non-dict root).

Guard: tests/hermes_cli/test_config_read_guard.py scans the tree for
yaml.safe_load within 6 lines of a 'config.yaml' reference outside an
explicit ALLOWLIST (hermes_cli/config.py, gateway/config.py, gateway/run.py
fallback path, hermes_cli/managed_scope.py which reads the MANAGED file,
gateway/readiness.py parse-health probe) and fails on new offenders.

E2E: tests/hermes_cli/test_config_loader_e2e.py runs a subprocess with a
temp HERMES_HOME (config.yaml containing ${E2E_PROMPT_SUFFIX}) plus a
HERMES_MANAGED_DIR overlay pinning agent.reasoning_effort, asserting
tui _load_cfg resolves "hello world"/"high" while _load_cfg_raw +
_save_cfg round-trip the template and user value verbatim with no
managed/default leakage.
2026-07-29 10:53:29 -07:00
..
computer_use fix(computer_use): revive ended cua-driver sessions once 2026-07-28 14:24:30 -07:00
environments refactor: single build_subprocess_env() factory for all child-process spawns (profile + secret-scrub single owner) 2026-07-29 10:14:11 -07:00
neutts_samples
wakewords feat(voice): bundle the trained "hey hermes" model as the out-of-the-box default 2026-07-28 07:58:16 -07:00
__init__.py
ansi_strip.py fix(cli): sanitize terminal escapes when replaying stored history (/resume recap, /status recap) 2026-07-17 04:53:38 -07:00
approval.py Reapply "feat(observability): integrate NeMo Relay runtime and shared metrics" 2026-07-27 21:10:51 -07:00
async_delegation.py Merge origin/main into feat/gateway-health-diagnostics 2026-07-28 13:09:17 +00:00
audio_container.py refactor: extract shared audio container sniffer to tools/audio_container.py 2026-07-28 11:52:44 -07:00
binary_extensions.py
blueprints.py fix: widen UTF-8 BOM tolerance to all sibling frontmatter parsers 2026-07-17 04:52:02 -07:00
browser_camofox.py fix(browser): block Camofox input on private pages 2026-07-03 03:27:47 -07:00
browser_camofox_state.py
browser_cdp_tool.py fix(browser): stop stale cdp_url from stalling every startup by 10+ seconds 2026-07-27 14:32:05 -07:00
browser_dialog_tool.py
browser_supervisor.py fix(browser): harden browser tool safety boundaries 2026-07-01 05:04:41 -07:00
browser_tool.py perf: lazy mcp SDK import + tool-discovery mtime cache + browser_tool import diet 2026-07-29 10:02:03 -07:00
budget_config.py fix(agent): scale tool-output budget to the model context window (#23767) 2026-06-21 17:46:38 +05:30
checkpoint_manager.py refactor: single build_subprocess_env() factory for all child-process spawns (profile + secret-scrub single owner) 2026-07-29 10:14:11 -07:00
clarify_gateway.py feat(clarify): extend multi-select to gateway text fallback and TUI bridge 2026-07-26 17:46:55 -07:00
clarify_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
close_terminal_tool.py fix(config): accept 'on' as truthy for env flags via shared env_var_enabled helper 2026-07-02 03:00:59 -07:00
code_execution_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
computer_use_tool.py feat(computer_use): cross-platform cua-driver (macOS/Windows/Linux) 2026-06-22 06:42:30 -07:00
credential_files.py fix(credentials): hoist read-guard import, fail closed loudly (#67665) 2026-07-20 09:21:08 -07:00
cronjob_tools.py feat(cron): user-owned model pins + cron.model fleet default 2026-07-28 11:52:47 -07:00
daemon_pool.py fix(cli): reliable interrupts, bounded exit, and exit feedback (#57000) 2026-07-02 04:20:43 -07:00
debug_helpers.py feat(moa): expose MoA presets as selectable virtual models (#46081) 2026-06-25 13:52:06 -07:00
delegate_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
delegation_live_log.py fix(delegation): redact credentials in live subagent transcripts 2026-07-20 06:50:31 -07:00
desktop_ui.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
discord_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
env_passthrough.py fix(security): strip dynamic Hermes secrets from all subprocess spawn env 2026-07-01 14:37:22 +05:30
env_probe.py fix(windows): re-fit env_probe console suppression to the temp-file _run + add no-window tests (#67690 follow-up) 2026-07-23 18:20:24 -07:00
fal_common.py
feishu_doc_tool.py
feishu_drive_tool.py
file_operations.py fix(file-safety): distinguish safe-root write denial from credential blocks 2026-07-14 17:09:40 +05:30
file_state.py
file_tools.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
focus_pane_tool.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
fuzzy_match.py fix(tools): normalize Unicode space family and minus sign in patch fuzzy matching 2026-07-26 20:58:43 -07:00
homeassistant_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
hook_output_spill.py fix: route stray HERMES_HOME hardcodes through get_hermes_home() (profile + native-Windows safety) 2026-07-29 09:33:48 -07:00
image_generation_tool.py fix(deepinfra): harden multimodal provider routing 2026-07-14 02:59:39 +05:30
image_source.py security(vision): route local-file inputs through the shared credential-read guard 2026-07-05 00:47:54 -07:00
interrupt.py fix(interrupt): run user-approved commands from a clean interrupt slate 2026-07-06 04:58:42 -07:00
kanban_tools.py fix: scope kanban auto-subscriptions to active profile 2026-07-26 16:27:52 -07:00
lazy_deps.py Merge remote-tracking branch 'origin/main' into feat/gateway-health-diagnostics-monitoring 2026-07-29 15:37:14 +00:00
managed_tool_gateway.py fix(windows): sweep remaining bare read_text/write_text sites + linter rule 2026-07-24 17:10:39 -07:00
mcp_dashboard_oauth.py fix(mcp): preserve live OAuth state during reauth 2026-07-17 04:50:47 -07:00
mcp_oauth.py perf: lazy mcp SDK import + tool-discovery mtime cache + browser_tool import diet 2026-07-29 10:02:03 -07:00
mcp_oauth_manager.py fix(mcp): make Figma remote OAuth work via DCR allowlist defaults 2026-07-28 00:53:16 -05:00
mcp_stdio_watchdog.py fix(mcp): use direct parent identity in stdio watchdog 2026-07-18 04:15:13 -07:00
mcp_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
memory_tool.py refactor: extract atomic_write_text to utils.py; fix write-failure error handling 2026-07-29 16:49:07 +05:30
microsoft_graph_auth.py
microsoft_graph_client.py refactor: single shared Retry-After parser 2026-07-29 10:13:50 -07:00
neutts_synth.py fix(voice): reconcile NeuTTS backbone/codec GPU device strings 2026-07-28 14:07:21 -07:00
open_preview_tool.py feat(desktop): agent can focus panes + shared desktop-UI event bridge 2026-07-22 12:13:01 -05:00
openrouter_client.py
osv_check.py fix(osv_check): honor npx --package/-p install target when parsing package arg (#40567) 2026-06-06 18:30:39 -07:00
patch_parser.py fix(patch): ignore inert context-only hunks (#63678) 2026-07-13 02:42:18 -07:00
path_security.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
process_registry.py fix(process_registry): reader loop no longer hangs when an orphaned grandchild holds the stdout pipe 2026-07-24 15:59:02 -07:00
project_tools.py feat(tools): add project workspace tools 2026-06-25 16:40:27 -05:00
read_extract.py feat(read): extract notebook and office documents (#37082) 2026-06-13 14:42:51 -07:00
read_terminal_tool.py fix(config): accept 'on' as truthy for env flags via shared env_var_enabled helper 2026-07-02 03:00:59 -07:00
registry.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
schema_sanitizer.py fix(schema): preserve dependentRequired property names 2026-07-28 14:37:19 -07:00
send_message_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
session_search_tool.py fix(session-search): allow scrolling compacted lineage history 2026-07-26 16:18:31 -07:00
skill_manager_tool.py refactor: use utils atomic writes in cron/skill_manager 2026-07-29 10:13:50 -07:00
skill_provenance.py
skill_usage.py fix(curator): make the autonomous write policy consistent (#67140) 2026-07-25 19:27:17 -07:00
skills_ast_audit.py
skills_guard.py fix(skills): bind bundles to exact files and origins 2026-07-12 02:59:27 -07:00
skills_hub.py refactor: remove the claude-marketplace skill source (redundant Marketplace hub tab) 2026-07-28 23:16:27 -07:00
skills_sync.py fix(skills): avoid redundant bind-mount scans (#72622) 2026-07-28 04:24:50 -05:00
skills_tool.py fix(skills): scope 60-char description enforcement to the create path 2026-07-24 07:54:21 -07:00
slash_confirm.py
terminal_tool.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
thread_context.py
threat_patterns.py fix: bound threat-pattern/FTS5 regex input and cover V4A Move-File edits 2026-07-01 01:05:28 -07:00
tirith_security.py fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428) 2026-07-24 11:45:57 -07:00
todo_tool.py fix(compression): preserve synthetic user provenance 2026-07-22 06:56:53 -07:00
tool_backend_helpers.py fix(voice): single scoped resolver — STT/TTS keys fall back to the credential pool 2026-07-28 11:53:09 -07:00
tool_output_limits.py fix: tool_output_limits re-reads config on every call (no caching) 2026-05-31 00:50:19 -07:00
tool_result_storage.py fix: keep persisted tool results inside their storage directory 2026-06-30 16:39:41 -07:00
tool_search.py refactor: migrate hand-rolled error envelopes to shared tool_error() 2026-07-29 10:14:00 -07:00
transcription_tools.py fix(stt): kill faster-whisper silence hallucinations at the source 2026-07-29 00:01:33 -07:00
tts_streaming.py fix(tts): route streaming-provider secrets through resolve_provider_secret; bound per-sentence stream bodies at 16 MiB 2026-07-28 22:31:40 -07:00
tts_text_normalize.py fix(tts): unify TTS text preprocessing behind one shared cleaner 2026-07-28 11:55:01 -07:00
tts_tool.py feat(voice): calm ambient "thinking" sound while the agent works in voice chat 2026-07-29 08:24:00 -07:00
url_safety.py fix: scope private URL policy per profile 2026-07-28 14:17:45 -07:00
video_generation_tool.py feat(providers): Support DeepInfra as an LLM provider 2026-07-14 02:59:39 +05:30
vision_tools.py fix(security): pin DNS resolutions for SSRF-safe fetches 2026-07-23 11:44:43 -07:00
voice_mode.py fix(voice): full-duplex agent-turn listener — interrupt by voice during generation AND playback 2026-07-29 10:08:53 -07:00
wake_word.py refactor: canonical config loaders for behavioral reads + guarded raw-read primitive (kills the managed-scope/env-expansion drift class) 2026-07-29 10:53:29 -07:00
web_tools.py fix: add encoding="utf-8" to Path.write_text() calls (P1) 2026-07-24 17:10:39 -07:00
website_policy.py fix(website-policy): key blocklist cache on the real default config path 2026-07-04 15:08:49 -07:00
working_diff.py feat(diff): cross-surface /diff with staged/all/session modes 2026-07-26 18:28:20 -07:00
write_approval.py fix(memory,skills): repair write-approval inline prompt, gateway staging, and gateway /skills review (#43452) 2026-06-10 02:57:15 -07:00
x_search_tool.py docs(xai): clarify x_search vs xurl routing without schema cross-refs 2026-07-23 21:06:47 -07:00
xai_http.py fix(voice): single scoped resolver — STT/TTS keys fall back to the credential pool 2026-07-28 11:53:09 -07:00
xai_video_tools.py feat(xai): Imagine public-URL storage, chaining & video edit/extend 2026-06-29 21:11:58 -07:00
yuanbao_tools.py