Skip to content

fix(bot-relay): Windows path SyntaxError in waiter + PATH-less delivery ENOENT - #93601

Closed
liuhao1024 wants to merge 2 commits into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-93590
Closed

liuhao1024 wants to merge 2 commits into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-93590

Conversation

@liuhao1024

Copy link
Copy Markdown

What does this PR do?

Fixes both message_agent relay failures from #93590 on a Windows desktop install talking to a remote gateway:

  1. Reply-waiter SyntaxError. waiter_command embeds the reply path into generated python -c source with !r. repr escapes each backslash (C:\\Users\\...), but the Windows execution layer the waiter runs under folds \\ back to \, so \U parses as an invalid unicode escape and SyntaxErrors the whole script. The two string literals in the generated source now carry a raw-string prefix: the folded single backslash parses as a literal.
  2. Delivery ENOENT. local_delivery_command hardcoded "hermes", relying on PATH — which service contexts (systemd units, desktop launchers, non-login SSH shells) do not provide, so the delivery subprocess died with No such file or directory: 'hermes'. It now resolves the CLI next to this gateway's own interpreter (the venv bin/Scripts sibling — hermes.exe on Windows), falling back to the bare name when no sibling exists.

Related Issue

Fixes #93590

Type of Change

  • 🐛 Bug fix

Changes Made

  • tools/bot_relay.py:
    • waiter_command: p = r{reply_path!r} and label = r{label!r} (both string injection points of the same mechanism; comment documents the fold, the POSIX no-op, and that \' inside a raw literal still cannot terminate the string — the Bot Mode reliability program: typed failure reasons, envelope TTL, attention badges, leader-routed group rooms, retry session policy #93091 injection defense is unchanged).
    • new _hermes_cli(): resolves the hermes entrypoint beside sys.executable (the target gateway's own venv interpreter — the deliver RPC runs on THIS gateway per the existing docstring), hermes.exe suffix on Windows, bare-name fallback preserving PATH lookup for source-tree runs.
    • local_delivery_command: first element is _hermes_cli(); the rest of the argv is untouched.
  • tools/bot_mode_dm.py: _delivery_lock now matches the CLI element by basename (split on both / and \), so resolved absolute paths — and hermes.exe — still take the Bot Mode reliability program: typed failure reasons, envelope TTL, attention badges, leader-routed group rooms, retry session policy #93091 per-profile turn lock instead of silently bypassing it. Without this, fix 2 would have re-opened the concurrent-turn race the lock exists to prevent.
  • tests/tools/test_bot_relay_windows_paths.py — new: Windows path compiles after simulated backslash folding (the exact \U crash); POSIX path/label values round-trip unchanged with the raw prefix pinned; injection defense holds under the raw prefix (hostile connection id stays data); sibling resolution + bare-name fallback; turn-lock recognition for resolved paths, bare names, and hermes.exe, with unrelated argvs still bypassing.
  • tests/tools/test_bot_turn_lock.py: the argv-shape pin goes by basename now (argv[1:3] == ["-p", <profile>] + CLI basename), same intent, robust to the resolved-path form.

How to Test

  • New: .venv/bin/python -m pytest tests/tools/test_bot_relay_windows_paths.py -q — should pass (6 tests). On unpatched main, 4 of the 6 fail (no raw literals → folding SyntaxError; no sibling resolution; lock rejects resolved paths), pinning both regressions.
  • Regression: .venv/bin/python -m pytest tests/tools/test_bot_relay.py tests/tools/test_bot_turn_lock.py tests/tools/test_bot_mode_dm.py tests/tui_gateway/test_bot_relay_methods.py -q — should pass (85 tests), including the existing hostile-connection-id injection test against the new raw-prefixed literals.

Observed result: locally, all 6 new tests pass and the 85 existing bot-relay/turn-lock/bot-mode tests stay green — the raw prefix is a no-op for POSIX paths, and the turn lock keeps matching every hermes-CLI delivery shape.

Checklist

  • Code follows project style (no unrelated changes)
  • Self-reviewed the diff
  • Added/updated tests covering the fix
  • All new and existing tests pass locally (85 existing + 6 new, zero regressions)
  • PR targets upstream/main from a fork branch

…ry ENOENT

Two failures on a Windows desktop install relaying to a remote gateway
(NousResearch#93590):

1. waiter_command embeds the reply path in generated python -c source
   with !r. repr escapes each backslash, but the Windows execution layer
   folds \\ back to \, so \U in C:\Users\... parses as a unicode escape
   and SyntaxErrors the whole waiter script. Raw-string literals keep
   the folded single backslash a literal; POSIX paths have no
   backslashes so the prefix is a no-op there, and \' inside a raw
   literal still cannot terminate the string, keeping the NousResearch#93091
   injection defense intact.

2. local_delivery_command hardcoded "hermes", relying on PATH — absent
   in service contexts (systemd units, desktop launchers, non-login SSH
   shells), so delivery died with ENOENT. It now resolves the CLI next
   to this gateway's own interpreter (venv bin/Scripts sibling,
   hermes.exe on Windows) with a bare-name fallback. The NousResearch#93091
   per-profile turn-lock recognition in bot_mode_dm now matches the CLI
   element by basename (split on both separators) so resolved absolute
   paths still take the lock instead of silently bypassing it.

Fixes NousResearch#93590
CI runners have a real hermes sibling next to the venv python, so
local_delivery_command now resolves an absolute path there — the exact
argv filters in the retry-policy fakes and the relay-methods pins must
match by basename instead of the literal "hermes", mirroring the
_delivery_lock matcher.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets platform/windows Native Windows-specific behavior or breakage sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 24, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Related: #93597 addresses the same Windows #93590 path/entrypoint failure with a narrower diff. This PR also covers the generated label literal, hermes.exe/basename lock behavior, fallback handling, and regression coverage; maintainers should choose or consolidate the approach.

teknium1 pushed a commit that referenced this pull request Aug 24, 2026
… decoding on delivery subprocess

Salvage hardening on top of #93601 (with #93597 covering the same core
mechanisms) for #93590:

- _hermes_cli(): after the venv-sibling check (hermes.exe on win32),
  try shutil.which('hermes') before the bare-name fallback, so
  environments with a PATH but no venv sibling resolve exactly what an
  interactive shell would. Platform test switched os.name -> sys.platform
  ('win32') per repo convention.
- tui_gateway/methods_bot_relay.py deliver: pin encoding='utf-8',
  errors='replace' on both subprocess.run sites — without them the
  child's UTF-8 output is decoded with the locale codec (cp1252/GBK on
  Windows), mangling non-ASCII replies or raising on undecodable bytes.
- Regression tests: shutil.which resolution step, bare-name fallback
  with which=None, and encoding-pin assertions in the deliver transport
  test.

Refs #93590, #93597, #93601
@teknium1

Copy link
Copy Markdown
Collaborator

Merged via #93658 with both your commits cherry-picked (authorship preserved); we added a shutil.which step and utf-8 pinning on the delivery subprocesses. @jdtimothy's #93597 credited as earliest diagnosis. Thanks @liuhao1024!

@teknium1 teknium1 closed this Aug 24, 2026
@liuhao1024

Copy link
Copy Markdown
Author

Thanks for the merge and for the note! Good additions — the shutil.which step is a sturdier resolution than the bare sibling lookup (it keeps the PATH-valid case working even when the sibling isn't where we guessed), and the utf-8 piece closes out an edge I hadn't covered. Glad this one landed.

and7777 pushed a commit to and7777/hermes-agent that referenced this pull request Aug 27, 2026
… decoding on delivery subprocess

Salvage hardening on top of NousResearch#93601 (with NousResearch#93597 covering the same core
mechanisms) for NousResearch#93590:

- _hermes_cli(): after the venv-sibling check (hermes.exe on win32),
  try shutil.which('hermes') before the bare-name fallback, so
  environments with a PATH but no venv sibling resolve exactly what an
  interactive shell would. Platform test switched os.name -> sys.platform
  ('win32') per repo convention.
- tui_gateway/methods_bot_relay.py deliver: pin encoding='utf-8',
  errors='replace' on both subprocess.run sites — without them the
  child's UTF-8 output is decoded with the locale codec (cp1252/GBK on
  Windows), mangling non-ASCII replies or raising on undecodable bytes.
- Regression tests: shutil.which resolution step, bare-name fallback
  with which=None, and encoding-pin assertions in the deliver transport
  test.

Refs NousResearch#93590, NousResearch#93597, NousResearch#93601
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
… decoding on delivery subprocess

Salvage hardening on top of NousResearch#93601 (with NousResearch#93597 covering the same core
mechanisms) for NousResearch#93590:

- _hermes_cli(): after the venv-sibling check (hermes.exe on win32),
  try shutil.which('hermes') before the bare-name fallback, so
  environments with a PATH but no venv sibling resolve exactly what an
  interactive shell would. Platform test switched os.name -> sys.platform
  ('win32') per repo convention.
- tui_gateway/methods_bot_relay.py deliver: pin encoding='utf-8',
  errors='replace' on both subprocess.run sites — without them the
  child's UTF-8 output is decoded with the locale codec (cp1252/GBK on
  Windows), mangling non-ASCII replies or raising on undecodable bytes.
- Regression tests: shutil.which resolution step, bare-name fallback
  with which=None, and encoding-pin assertions in the deliver transport
  test.

Refs NousResearch#93590, NousResearch#93597, NousResearch#93601
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows bot_relay delivery fails: Unicode path crash and 'hermes' not found

3 participants