From 2d131bfb8a9213582ad692e04b73dd5adf8c9e79 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 23:03:44 +0000 Subject: [PATCH] Verify native hermes is actually the right agent before trusting it hermes_cli_args() previously trusted any 'hermes' found on native PATH without checking what it actually was. Windows machines can have an unrelated tool also named 'hermes' (softwarepub/HERMES, an academic software-publication tool with harvest/process/curate/deposit subcommands - confirmed to be what was actually on this machine's PATH), which would silently get used instead of the real NousResearch agent installed in WSL, producing the misleading 'Hermes needs setup' message. Now check that a native 'hermes' actually exposes the agent's 'chat' subcommand before using it directly, falling back to the WSL bridge otherwise. --- server.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/server.py b/server.py index 581a4e3..b106ccd 100644 --- a/server.py +++ b/server.py @@ -142,16 +142,29 @@ def append_audit(entry: dict): # ─── Agent Discovery (instant filesystem checks) ──────────────────── +def _cli_has_subcommand(base_args: list, subcommand: str) -> bool: + try: + r = subprocess.run([*base_args, "--help"], capture_output=True, text=True, timeout=10) + return subcommand in ((r.stdout or "") + (r.stderr or "")) + except Exception: + return False + def hermes_cli_args(*args: str) -> list: - """Build the command to invoke Hermes, bridging through WSL if it's only installed there. + """Build the command to invoke Hermes, bridging through WSL if the real agent only lives there. The dashboard commonly runs as a native Windows process while Hermes (whose official - installer is Bash-only) lives inside WSL - a plain PATH lookup on Windows will never find it. + installer is Bash-only) lives inside WSL - a plain PATH lookup on Windows will never find it + there. Windows machines can also have an unrelated tool also named 'hermes' on PATH (e.g. the + academic softwarepub/HERMES metadata-publishing project, which coincidentally shares the name), + so don't just trust that a native 'hermes' is the right one - confirm it exposes the + NousResearch agent's `chat` subcommand before using it, falling back to WSL otherwise. """ - if shutil.which("hermes") is not None or shutil.which("wsl") is None: + if shutil.which("hermes") is not None and _cli_has_subcommand(["hermes"], "chat"): return ["hermes", *args] - quoted = " ".join(shlex.quote(a) for a in args) - return ["wsl", "-e", "bash", "-lc", f"hermes {quoted}"] + if shutil.which("wsl") is not None: + quoted = " ".join(shlex.quote(a) for a in args) + return ["wsl", "-e", "bash", "-lc", f"hermes {quoted}"] + return ["hermes", *args] def hermes_available() -> bool: try: