Skip to content

fix(constants): display_hermes_home returns absolute path when HERMES_HOME == HOME#21929

Open
li0near wants to merge 1 commit into
NousResearch:mainfrom
li0near:fix/display-hermes-home-confusing-shorthand
Open

fix(constants): display_hermes_home returns absolute path when HERMES_HOME == HOME#21929
li0near wants to merge 1 commit into
NousResearch:mainfrom
li0near:fix/display-hermes-home-confusing-shorthand

Conversation

@li0near

@li0near li0near commented May 8, 2026

Copy link
Copy Markdown
Contributor

Symptom

Tool descriptions, log messages, and prompts that embed display_hermes_home() rendered strings like:

Relative paths resolve under ~/./scripts/.

in some Docker setups. The ~ is the problem. ~ universally means the current reader's $HOME — that's its only meaning across shells, libraries, and the broader ecosystem. Whoever expands the shorthand — a human running a shell command, an LLM agent placing a file — looks up $HOME in their own environment and resolves the path against that. They have no way to know it was meant to point at HERMES_HOME instead.

In LLM-driven workflows that mismatch causes silent miscompilation: the agent, seeing ~/./scripts/ in a tool description, reads it as $HOME/scripts/ (or worse, pattern-matches it against the conventional ~/.hermes/scripts/ path) and writes script files there. The cron scheduler, which resolves script paths against HERMES_HOME/scripts/, then can't find them at execution time.

Root cause

The official Docker image sets the unix user's home directory equal to HERMES_HOME:

  • Dockerfile:21RUN useradd -u 10000 -m -d /opt/data hermes
  • Dockerfile:86ENV HERMES_HOME=/opt/data

So inside the agent process, Path.home() and get_hermes_home() both resolve to /opt/data. The old implementation:

home = get_hermes_home()
try:
    return "~/" + str(home.relative_to(Path.home()))
except ValueError:
    return str(home)

evaluates Path('/opt/data').relative_to(Path('/opt/data'))Path('.'), producing the shorthand ~/.. That gets concatenated into the cronjob tool description as ~/./scripts/.

The deeper issue: the ~/ shorthand is only safe when the reader can resolve it back to the same absolute path. That requires Path.home() (the reader's $HOME) to be the prefix of get_hermes_home(). When they're equal, there's nothing left to shorten, and the resulting string is meaningless to anyone whose $HOME doesn't happen to match the writer's.

Fix

When home == Path.home() the shorthand cannot produce a useful string, so fall back to the absolute path:

if home == Path.home():
    return str(home)

In the broken Docker layout this returns /opt/data instead of ~/.. The cronjob tool description renders as:

Relative paths resolve under /opt/data/scripts/.

Unambiguous to any reader.

What's unchanged

  • Standard installs (HERMES_HOME=~/.hermes) still render ~/.hermes.
  • Profile-mode installs (HERMES_HOME=~/.hermes/profiles/coder) still render ~/.hermes/profiles/coder.
  • Custom paths outside the user's home (e.g. HERMES_HOME=/opt/hermes-custom) were already taking the ValueError branch and returning the absolute form — still do.

Tests

Two tests in tests/test_hermes_constants.py:

  1. test_default_install_uses_shorthand — the happy path; ~/.hermes rendering still works.
  2. test_hermes_home_equals_user_home_returns_absolute — the regression. Fails on unpatched code with the literal ~/. output, passes on patched code.

The profile-install and custom-path-outside-home cases I had earlier exercised the same relative_to / ValueError branches as #1 — I dropped them as redundant.

Test results

Regression test fails on unpatched upstream/main:

$ git checkout upstream/main -- hermes_constants.py
$ pytest tests/test_hermes_constants.py::TestDisplayHermesHome -v

FAILED test_hermes_home_equals_user_home_returns_absolute
  AssertionError:
  - /tmp/pytest-of-hermes/.../test_hermes_home_equals_user_h0
  + ~/.

Proves the test catches the bug.

With the patch — full tests/test_hermes_constants.py passes:

$ pytest tests/test_hermes_constants.py -q
36 passed in 5.54s

Existing callers continue to pass:

$ pytest tests/cli/test_cli_status_command.py tests/cli/test_personality_none.py \
         tests/skills/test_google_oauth_setup.py tests/gateway/test_session.py \
         tests/hermes_cli/test_memory_reset.py -q
126 passed

End-to-end: verified inside the official Docker container that the cronjob tool description previously rendered as Relative paths resolve under ~/./scripts/. After this fix it renders as Relative paths resolve under /opt/data/scripts/.

Risk

Low. The new branch only fires when home == Path.home() exactly — a condition that already produced broken output. The existing relative_to and ValueError branches are untouched.

@li0near li0near force-pushed the fix/display-hermes-home-confusing-shorthand branch from 1e71a92 to 88803d4 Compare May 8, 2026 15:27
…_HOME == HOME

The ~/ shorthand is only meaningful if the reader can resolve it.
`~` universally means "$HOME for the current user". Whoever sees a
description like `Relative paths resolve under ~/./scripts/.` —
human or LLM — will expand `~` against their own $HOME, not against
HERMES_HOME, and look for files in the wrong place.

That mismatch fires in the official Docker image, where:

  Dockerfile:21  RUN useradd -u 10000 -m -d /opt/data hermes
  Dockerfile:86  ENV HERMES_HOME=/opt/data

set the unix user's home directory equal to HERMES_HOME. Inside the
agent process, `Path.home()` and `get_hermes_home()` then resolve
to the same path. The old implementation:

    home.relative_to(Path.home())        # = Path('.')
    return "~/" + str(...)              # = "~/."

produced `~/.`, which concatenated into the cronjob tool description
as `~/./scripts/`. Agents reading that string place script files
under $HOME (`/opt/data/home`) instead of HERMES_HOME (`/opt/data`),
and the cron scheduler — which resolves script paths against
`HERMES_HOME/scripts/` — fails to find them.

Fix: when `home == Path.home()` the shorthand has nothing to shorten,
so return the absolute path. Standard installs (HERMES_HOME under
HOME) and custom paths outside HOME are unchanged.

Tests:
- test_default_install_uses_shorthand — happy path, ~/.hermes form.
- test_hermes_home_equals_user_home_returns_absolute — regression
  guard. Fails on unpatched code with the literal '~/.' output.
@li0near li0near force-pushed the fix/display-hermes-home-confusing-shorthand branch from 88803d4 to 0f816e8 Compare May 11, 2026 03:58
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder area/docker Docker image, Compose, packaging labels May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging comp/agent Core agent loop, run_agent.py, prompt builder P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants