hermes-agent/tools
kshitij a38cc69bcc
fix(terminal): complete sane PATH entries on POSIX (salvage of #35614) (#42653)
* fix(terminal): complete sane PATH entries on POSIX

Fixes macOS gateway/launchd terminal sessions whose PATH already
includes /usr/bin while omitting Apple Silicon Homebrew paths.
LocalEnvironment._make_run_env() now appends each missing _SANE_PATH
entry individually on POSIX, preserving caller precedence and avoiding
duplicate sane entries.

Root cause: the previous logic used /usr/bin as the sentinel for sane
PATH injection. macOS launchd commonly provides /usr/bin while leaving
out /opt/homebrew/bin and /opt/homebrew/sbin, so Homebrew-installed
CLIs stayed unavailable in terminal tool calls.

Salvaged from #35614 by @y0shua1ee. Fixes #35613.

Co-authored-by: y0shua1ee <104712437+y0shua1ee@users.noreply.github.com>

* test(terminal): harden sane PATH completion against dup/empty entries

Follow-up to the #35613 fix. Strengthens _append_missing_sane_path_entries:

- De-duplicate the caller-supplied PATH (first occurrence wins) so a PATH
  that already contains duplicate entries is collapsed rather than carried
  through. Previously only newly-appended sane entries were guarded against
  duplication; pre-existing caller duplicates were preserved verbatim.
- Drop empty PATH entries (leading/trailing/double ':'), which POSIX shells
  interpret as the current working directory — a mild foot-gun in a
  default terminal environment.

Behaviour for well-formed PATHs (no duplicates, no empty entries) is
byte-identical to before; only malformed/duplicated inputs change.

Adds regression tests for: the literal macOS launchd PATH
(/usr/bin:/bin:/usr/sbin:/sbin), caller-duplicate collapsing with
order preservation, and empty-entry stripping.

* docs(terminal): clarify PATH normalisation semantics; drop dead set add

Addresses review findings on the sane-PATH completion follow-up:

- Sharpen the _append_missing_sane_path_entries docstring to state
  explicitly that on POSIX the caller PATH is rewritten (empty entries
  stripped, duplicates collapsed) rather than merely appended to, and
  that well-formed PATHs remain byte-identical bar the appended sane
  entries. This makes the intentional semantic change visible rather
  than buried under "hardening".
- Document why _path_env_key is a deliberate second Windows guard
  distinct from the helper's early return (key-casing selection vs
  standalone safety), so neither is mistaken for redundant and removed.
- Drop the dead `seen.add(entry)` in the sane-entry loop: _SANE_PATH is
  a static duplicate-free constant, so the membership check against the
  caller entries is sufficient and `seen` is never read afterwards.

No behaviour change: verified byte-identical output across the launchd,
minimal, empty, duplicate, empty-entry and already-full cases, and
re-confirmed gh/brew resolve through the real LocalEnvironment.execute()
path under a launchd-style PATH. 133 targeted tests pass.

Intentionally NOT consolidating with tools/browser_tool._merge_browser_path:
it prepends (vs append), filters on os.path.isdir, uses os.pathsep, and
draws from a dynamic candidate set — a shared helper is a separate
refactor, out of scope for this bugfix.

---------

Co-authored-by: y0shua1ee <104712437+y0shua1ee@users.noreply.github.com>
2026-06-09 02:21:12 -07:00
..
computer_use fix(computer_use): honor custom vision routing 2026-06-07 02:09:20 -07:00
environments fix(terminal): complete sane PATH entries on POSIX (salvage of #35614) (#42653) 2026-06-09 02:21:12 -07:00
neutts_samples
__init__.py
ansi_strip.py
approval.py fix(approval): resolve Hermes home at detection time, not import time 2026-06-08 11:55:40 -07:00
binary_extensions.py
browser_camofox.py fix(browser): rewrite Camofox Docker loopback URLs (#25541) 2026-05-29 15:43:55 +10:00
browser_camofox_state.py
browser_cdp_tool.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
browser_dialog_tool.py
browser_supervisor.py fix(browser): recover from CDP DOM-node serialization crash in browser_console (#35385) 2026-05-30 07:31:25 -07:00
browser_tool.py fix(tools): percent-encode non-ascii URL components 2026-06-07 11:42:26 -06:00
budget_config.py
checkpoint_manager.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
clarify_gateway.py
clarify_tool.py
code_execution_tool.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
computer_use_tool.py
credential_files.py refactor(image_gen): delegate cache-path mapping to shared helper 2026-06-06 13:19:07 -07:00
cronjob_tools.py fix(cron): sanitize invisible unicode in vetted skill content instead of hard-blocking (#37245) 2026-06-02 00:29:44 -07:00
debug_helpers.py
delegate_tool.py fix(delegate): resolve custom-endpoint subagent pools by endpoint identity (#41730) 2026-06-07 22:05:14 -07:00
discord_tool.py
env_passthrough.py harden(env_passthrough): apply GHSA-rhgp-j443-p4rf filter to config.yaml path (#27794) 2026-05-25 03:35:23 -07:00
env_probe.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
fal_common.py refactor(image_gen): port FAL backend to plugins/image_gen/fal 2026-05-22 04:10:45 -07:00
feishu_doc_tool.py
feishu_drive_tool.py
file_operations.py fix(file-ops): make rg/grep search error guard reachable and preserve partial matches (#39858) 2026-06-05 17:44:52 -07:00
file_state.py
file_tools.py fix(file-tools): reject sentinel TERMINAL_CWD; anchor worktree edits before live cwd exists (#41861) 2026-06-07 23:58:47 -07:00
fuzzy_match.py fix(patch): widen new_string \t/\r unescape to all match strategies (#33733) 2026-05-28 03:27:20 -07:00
homeassistant_tool.py
image_generation_tool.py refactor(image_gen): delegate cache-path mapping to shared helper 2026-06-06 13:19:07 -07:00
interrupt.py
kanban_tools.py fix(kanban): kanban_create inherits the spawning worker's task workspace (#37182) 2026-06-01 21:26:29 -07:00
lazy_deps.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
managed_tool_gateway.py fix(managed-gateway): keep tool availability scans off the Nous token-refresh path 2026-05-30 07:58:08 -07:00
mcp_oauth.py feat(mcp-oauth): accept 'skip' at paste prompt to bypass auth without disabling server (#32069) 2026-05-25 05:37:30 -07:00
mcp_oauth_manager.py
mcp_tool.py fix: skip MCP preflight content-type probe on reconnect when already ready (#40604) 2026-06-07 09:51:11 -07:00
memory_tool.py fix(memory): instruct in-turn consolidation + retry on overflow (#41755) 2026-06-07 22:16:28 -07:00
microsoft_graph_auth.py
microsoft_graph_client.py
mixture_of_agents_tool.py
neutts_synth.py
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
path_security.py
process_registry.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
registry.py
schema_sanitizer.py
send_message_tool.py fix(photon): support E.164 and DM GUID targets for home channel 2026-06-08 21:03:58 -07:00
session_search_tool.py feat(desktop): drag sessions into chat as @session links + spawn loader 2026-06-04 19:41:51 -05:00
skill_manager_tool.py fix(skill_manager): allow SKILL.md in _validate_file_path without weakening traversal guard (#40568) 2026-06-06 18:32:37 -07:00
skill_provenance.py
skill_usage.py fix(curator): protect load-bearing built-in skills from archival/consolidation (#41817) 2026-06-07 22:23:29 -07:00
skills_ast_audit.py refactor(skills): slim AST diagnostic to single entry point 2026-05-23 17:47:26 -07:00
skills_guard.py fix(skills-guard): stop flagging benign skill content + honor skill ignore files (#36231) 2026-06-01 01:58:48 -07:00
skills_hub.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
skills_sync.py feat(skills): blank-slate skills — install --no-skills + opt-out/opt-in (#36228) 2026-06-01 02:57:57 -07:00
skills_tool.py fix(skills): block path traversal via skill_view name argument (#40566) 2026-06-06 18:29:52 -07:00
slash_confirm.py
terminal_tool.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
thread_context.py fix(code-exec): propagate agent-turn context into tool worker threads 2026-05-29 03:44:49 -07:00
threat_patterns.py feat(security): promptware defense — shared threat patterns + memory load-time scan + tool-result delimiters (#32269) 2026-05-25 14:52:24 -07:00
tirith_security.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
todo_tool.py hardening(todo): bound TodoStore item content length and count 2026-06-07 18:06:27 -07:00
tool_backend_helpers.py feat(tools): surface the free tool pool in entitlement + setup (#36153) 2026-06-01 06:32:48 +05:30
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
tool_search.py fix(tool-search): scope bridge catalog + dispatch to the session's toolsets 2026-05-29 02:04:12 -07:00
transcription_tools.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
tts_tool.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
url_safety.py fix(tools): percent-encode non-ascii URL components 2026-06-07 11:42:26 -06:00
video_generation_tool.py fix(xai): route video models by modality 2026-06-01 19:00:30 -07:00
vision_tools.py fix(deps): force prompt=False on the two mid-session lazy-install tool paths 2026-06-06 18:44:15 -07:00
voice_mode.py fix: prevent TUI gateway stdin EOF crash across all TUI-context subprocess calls 2026-06-08 22:46:57 -07:00
web_tools.py fix(web): make _has_env config-aware so SEARXNG_URL auto-detect honors Hermes config 2026-06-08 01:12:32 +05:30
website_policy.py chore(web): remove web_crawl tool + provider crawl plumbing (#33824) 2026-05-28 04:52:42 -07:00
x_search_tool.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
xai_http.py
yuanbao_tools.py Fix unsafe gateway media path delivery 2026-05-23 01:40:35 -07:00