Files
hermes-agent/tests/tools/test_code_execution_hermes_home.py
T
Kevin Rajan 7ef0b98272 fix(tools): honor multiplexed per-turn HERMES_HOME in execute_code child env
Under a multiplexed Desktop/Dashboard connection, one server process serves
multiple profiles, binding a context-local HERMES_HOME override per turn.
_build_child_env scrubbed the server process's os.environ - which carries the
machine-default HERMES_HOME - so skill scripts run via execute_code silently
read/wrote the wrong profile's directory.

Rewrite HERMES_HOME in the child env from the active override when one is
bound (the same per-turn rewrite apply_subprocess_home_env already does for
HOME). With no override (dedicated per-profile process) the inherited value
is left untouched.

Fixes #110303.
2026-09-14 16:13:51 -07:00

76 lines
2.7 KiB
Python

"""execute_code child env honors the multiplexed per-turn HERMES_HOME override (#110303).
Under a multiplexed Desktop/Dashboard connection one server process serves several
profiles, binding a context-local HERMES_HOME override per turn. ``_build_child_env``
scrubs the server process's ``os.environ`` — which carries the machine-default
HERMES_HOME — so without the rewrite below, skill scripts run via ``execute_code``
silently read/write the wrong profile's directory.
"""
import sys
import pytest
from hermes_constants import (
get_hermes_home_override,
reset_hermes_home_override,
set_hermes_home_override,
)
from tools.code_execution_env import _build_child_env
@pytest.fixture
def home_override():
tokens = []
def _set(path):
tokens.append(set_hermes_home_override(path))
return str(path)
yield _set
for token in reversed(tokens):
reset_hermes_home_override(token)
def _child_env():
return _build_child_env(
rpc_endpoint="sock",
rpc_token="tok",
tmpdir="/tmp/hermes-test",
child_python=sys.executable,
)
class TestMultiplexedHermesHome:
def test_override_rewrites_stale_server_default(self, monkeypatch, home_override, tmp_path):
"""The reported bug: child must see the active profile's home, not the server default."""
monkeypatch.setenv("HERMES_HOME", "/machine/default/.hermes")
alpha_home = home_override(tmp_path / "profiles" / "alpha")
assert _child_env()["HERMES_HOME"] == alpha_home
def test_no_override_leaves_inherited_value_untouched(self, monkeypatch):
"""Dedicated per-profile processes (no override): zero behavior change."""
assert get_hermes_home_override() is None
monkeypatch.setenv("HERMES_HOME", "/machine/default/.hermes")
assert _child_env()["HERMES_HOME"] == "/machine/default/.hermes"
def test_override_applies_when_parent_env_unset(self, monkeypatch, home_override, tmp_path):
monkeypatch.delenv("HERMES_HOME", raising=False)
beta_home = home_override(tmp_path / "profiles" / "beta")
assert _child_env()["HERMES_HOME"] == beta_home
def test_override_does_not_leak_between_profiles(self, monkeypatch, home_override, tmp_path):
"""Sequential turns for different profiles each see their own home."""
monkeypatch.setenv("HERMES_HOME", "/machine/default/.hermes")
home_override(tmp_path / "profiles" / "alpha")
first = _child_env()["HERMES_HOME"]
home_override(tmp_path / "profiles" / "beta")
second = _child_env()["HERMES_HOME"]
assert first == str(tmp_path / "profiles" / "alpha")
assert second == str(tmp_path / "profiles" / "beta")