mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix: Hermes child interpreters no longer import packages from the launch directory
python -m / -c put the current directory ahead of PYTHONPATH and site-packages, so a hermes_cli/, tui_gateway/, agent/ or plugins/ package in the directory Hermes runs in (a cloned repo, a task workspace) ran in place of Hermes' own. Spawn these children with -P (safe path, Python >= 3.11) and keep their cwd: TUI slash worker, TUI cli.exec, setup import-agent, profile uninstall, gateway restart watcher, plugin validate probe, Google Meet bot, and the codex hermes-tools MCP server config. The validate probe now names the Hermes tree on PYTHONPATH instead of finding providers through the cwd.
This commit is contained in:
1 parent
4d0d67ba38
commit
b6fa7cd951
8 files changed
+15
-8
No files matched your search
@@ -407,7 +407,8 @@ def _build_hermes_tools_mcp_entry() -> dict:
|
||||
env["HERMES_REDACT_SECRETS"] = env.get("HERMES_REDACT_SECRETS", "true")
|
||||
return {
|
||||
"command": sys.executable,
|
||||
"args": ["-m", "agent.transports.hermes_tools_mcp_server"],
|
||||
# -P: codex starts this in the user's workspace, which must not shadow Hermes' own packages.
|
||||
"args": ["-P", "-m", "agent.transports.hermes_tools_mcp_server"],
|
||||
"env": env,
|
||||
# Generous timeouts — image_generate or delegate_task can take a while.
|
||||
"startup_timeout_sec": 30.0,
|
||||
|
||||
@@ -1166,7 +1166,7 @@ def _spawn_gateway_restart_watcher(old_pid: int, run_argv: list[str], *, host: b
|
||||
watcher_timeout_literal=json.dumps(GATEWAY_RESTART_WATCHER_TIMEOUT_S),
|
||||
project_root_literal=json.dumps(str(PROJECT_ROOT)))
|
||||
|
||||
watcher_argv = [sys.executable, "-c", watcher, str(old_pid), *run_argv]
|
||||
watcher_argv = [sys.executable, "-P", "-c", watcher, str(old_pid), *run_argv]
|
||||
devnull = {"stdout": subprocess.DEVNULL, "stderr": subprocess.DEVNULL}
|
||||
# Host respawn must not inherit a named launcher's dotenv. The watcher copies os.environ
|
||||
# into the gateway child, so the scrub has to be the watcher's own environ.
|
||||
|
||||
@@ -354,10 +354,15 @@ def _run_capability_probe(
|
||||
with tempfile.TemporaryDirectory(prefix="hermes-validate-") as scratch:
|
||||
env = dict(probe[1] if probe else os.environ)
|
||||
env["HERMES_HOME"] = scratch
|
||||
# The probe imports this tree's ``providers``; name the tree instead of relying on the cwd.
|
||||
repo_root = str(Path(__file__).resolve().parents[1])
|
||||
env["PYTHONPATH"] = os.pathsep.join(p for p in (repo_root, env.get("PYTHONPATH", "")) if p)
|
||||
python, *options = probe[0] if probe else [sys.executable]
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
*(probe[0] if probe else [sys.executable]),
|
||||
# -P: ``-c`` would put the user's cwd ahead of Hermes and the plugin's imports.
|
||||
python, "-P", *options,
|
||||
"-c",
|
||||
_PROBE_SCRIPT,
|
||||
str(plugin_dir),
|
||||
|
||||
@@ -136,7 +136,7 @@ def _run_import(source: ImportSource, hermes_home: Path) -> bool:
|
||||
selector = [] if hermes_home.resolve().parent.name == "profiles" else ["--profile", "default"]
|
||||
# --from-setup: the plugin's import speaks as a step of this setup (no "run hermes setup" pointers,
|
||||
# OpenClaw's skipped items folded into one line).
|
||||
argv = [sys.executable, "-m", "hermes_cli.main", *selector, "import-agent", source.name, *source.extra_args,
|
||||
argv = [sys.executable, "-P", "-m", "hermes_cli.main", *selector, "import-agent", source.name, *source.extra_args,
|
||||
"--from-setup"]
|
||||
env = served_profile_child_env(target_home=hermes_home)
|
||||
if os.environ.get("PYTHONPATH"): # this same interpreter running Hermes: keep the source tree
|
||||
|
||||
@@ -616,7 +616,7 @@ def _uninstall_profile(profile) -> None:
|
||||
log_info(f"Uninstalling profile '{name}'...")
|
||||
|
||||
# 1. Gateway service, via `python -m hermes_cli.main` (the `hermes` wrapper may be half-gone).
|
||||
hermes_invocation = [sys.executable, "-m", "hermes_cli.main", "--profile", name]
|
||||
hermes_invocation = [sys.executable, "-P", "-m", "hermes_cli.main", "--profile", name]
|
||||
for subcmd in ("stop", "uninstall"):
|
||||
try:
|
||||
subprocess.run(
|
||||
|
||||
@@ -101,7 +101,7 @@ def start(url: str, *, out_dir: Optional[Path] = None, headed: bool = False,
|
||||
log_path = out / "bot.log"
|
||||
# Detach: stdout/stderr → log file, new session so parent signals don't propagate.
|
||||
with open(log_path, "ab", buffering=0) as log_fh:
|
||||
proc = subprocess.Popen([sys.executable, "-m", "plugins.google_meet.meet_bot"], stdin=subprocess.DEVNULL,
|
||||
proc = subprocess.Popen([sys.executable, "-P", "-m", "plugins.google_meet.meet_bot"], stdin=subprocess.DEVNULL,
|
||||
stdout=log_fh, stderr=subprocess.STDOUT, env=env, start_new_session=True,
|
||||
close_fds=True)
|
||||
record = {"pid": proc.pid, "meeting_id": meeting_id, "out_dir": str(out), "url": url,
|
||||
|
||||
@@ -455,7 +455,7 @@ def _(rid, params: dict) -> dict:
|
||||
# Hermes-owned strip (no-boot-through-venv).
|
||||
_compat = _tools_mod("hermes_cli._subprocess_compat")
|
||||
return _captured_exec(
|
||||
rid, [sys.executable, "-m", "hermes_cli.main", *argv], min(int(params.get("timeout", 240)), 600),
|
||||
rid, [sys.executable, "-P", "-m", "hermes_cli.main", *argv], min(int(params.get("timeout", 240)), 600),
|
||||
on_result=lambda r: _ok(rid, {
|
||||
"blocked": False, "code": r.returncode, "output": (_joined_output(r) or "(no output)")[:48_000]}),
|
||||
timeout_err=(5016, "cli.exec: timeout"), fail_code=5017,
|
||||
|
||||
@@ -263,7 +263,8 @@ class _SlashWorker:
|
||||
# ``--provider`` pins the child to the parent agent's virtual provider: without it the
|
||||
# worker re-resolves provider from config, so a MoA session (provider=moa, model=<preset>)
|
||||
# dispatched its preset NAME to the configured real provider and 402/503'd (#57283).
|
||||
argv = [sys.executable, "-m", "tui_gateway.slash_worker", "--session-key", session_key,
|
||||
# -P: the worker runs in the user's cwd, which must not shadow tui_gateway/hermes_cli.
|
||||
argv = [sys.executable, "-P", "-m", "tui_gateway.slash_worker", "--session-key", session_key,
|
||||
"--parent-pid", str(os.getpid())] \
|
||||
+ (["--model", model] if model else []) \
|
||||
+ (["--provider", provider] if provider else [])
|
||||
|
||||
Reference in new issue
Block a user