mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
feat(agent): add runtime_cwd resolver (single source of truth for working dir)
This commit is contained in:
parent
f1237aa95b
commit
4bc7296042
2 changed files with 71 additions and 0 deletions
31
agent/runtime_cwd.py
Normal file
31
agent/runtime_cwd.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"""Single source of truth for the agent working directory.
|
||||
|
||||
`TERMINAL_CWD` is the runtime carrier for the configured working directory
|
||||
(design #19214/#19242: `terminal.cwd` is bridged once to `TERMINAL_CWD` at
|
||||
gateway/cron startup). The local-CLI backend deliberately leaves it unset and
|
||||
relies on the launch dir. Reading it in one place keeps the system prompt, the
|
||||
tool surfaces, and context-file discovery agreeing on where the agent lives.
|
||||
|
||||
The #29531 per-session extension point is this function: a future PR adds a
|
||||
contextvar arm inside `resolve_agent_cwd` and `.set()`s it at the
|
||||
`set_session_vars` seam — by design, not a reopening hazard.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def resolve_agent_cwd() -> Path:
|
||||
raw = os.environ.get("TERMINAL_CWD", "").strip()
|
||||
if raw:
|
||||
p = Path(raw).expanduser()
|
||||
if p.is_dir():
|
||||
return p
|
||||
return Path(os.getcwd())
|
||||
|
||||
|
||||
def resolve_context_cwd() -> Path | None:
|
||||
# None is load-bearing: it signals "skip context-file discovery" so the
|
||||
# gateway install dir's AGENTS.md isn't slurped (see system_prompt.py).
|
||||
raw = os.environ.get("TERMINAL_CWD", "").strip()
|
||||
return Path(raw).expanduser() if raw else None
|
||||
40
tests/agent/test_runtime_cwd.py
Normal file
40
tests/agent/test_runtime_cwd.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
"""Tests for agent/runtime_cwd.py — the single source of truth for the agent working directory."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from agent.runtime_cwd import resolve_agent_cwd, resolve_context_cwd
|
||||
|
||||
|
||||
class TestResolveAgentCwd:
|
||||
def test_prefers_terminal_cwd_over_getcwd(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("TERMINAL_CWD", str(tmp_path))
|
||||
monkeypatch.chdir(os.path.expanduser("~"))
|
||||
assert resolve_agent_cwd() == tmp_path
|
||||
|
||||
def test_falls_back_to_getcwd_when_unset(self, monkeypatch, tmp_path):
|
||||
# The #19242 local-CLI contract: TERMINAL_CWD is unset, so the launch dir wins.
|
||||
monkeypatch.delenv("TERMINAL_CWD", raising=False)
|
||||
monkeypatch.chdir(tmp_path)
|
||||
assert resolve_agent_cwd() == tmp_path
|
||||
|
||||
def test_skips_nonexistent_terminal_cwd(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("TERMINAL_CWD", str(tmp_path / "gone"))
|
||||
monkeypatch.chdir(tmp_path)
|
||||
assert resolve_agent_cwd() == tmp_path
|
||||
|
||||
def test_expands_leading_tilde(self, monkeypatch):
|
||||
monkeypatch.setenv("TERMINAL_CWD", "~")
|
||||
assert resolve_agent_cwd() == Path(os.path.expanduser("~"))
|
||||
|
||||
|
||||
class TestResolveContextCwd:
|
||||
def test_returns_dir_when_set(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("TERMINAL_CWD", str(tmp_path))
|
||||
assert resolve_context_cwd() == tmp_path
|
||||
|
||||
def test_returns_none_when_unset(self, monkeypatch):
|
||||
# None is load-bearing: it tells the caller to skip context-file discovery
|
||||
# (don't slurp the gateway install dir's AGENTS.md).
|
||||
monkeypatch.delenv("TERMINAL_CWD", raising=False)
|
||||
assert resolve_context_cwd() is None
|
||||
Loading…
Add table
Add a link
Reference in a new issue