Files
hermes-agent/tests/tools/test_terminal_cwd_echo.py
T
Teknium a18a2f170c feat(terminal): echo cwd in result when a command changes the working directory
Production mining (state.db, 400k-msg window): 60.2% of 104k terminal
calls carry a defensive 'cd X && ' prefix (~925k tokens of pure prefix)
and 2,462 failed calls led with cd — the model cannot see cwd state, so
it re-asserts it on every call and runs pwd/ls diagnostics after
directory changes.

The result dict now includes a 'cwd' field whenever the session cwd
after the command differs from the cwd it started in (cd, pushd,
chained cd). Stable-cwd commands are unchanged (no field, no noise).
Per-command workdir overrides stay transient by contract and never
echo. Schema note added so models learn to trust session cwd instead
of prefixing. Pattern borrowed from crush's <cwd> injection.

realpath comparison avoids false echoes through symlinks; the echo is
wrapped defensively so a backend without .cwd can never break the
result path.
2026-08-02 15:10:13 -07:00

69 lines
2.6 KiB
Python

"""Tests for the terminal result cwd echo (feat/terminal-cwd-echo).
When a command changes the session working directory, the tool result
gains a "cwd" field so the model stops prefixing every command with
'cd X && ' (60% of production terminal calls) and stops running pwd
diagnostics after directory changes.
"""
import json
import os
import tempfile
import pytest
from tools.terminal_tool import terminal_tool
@pytest.fixture
def isolated_home(tmp_path, monkeypatch):
monkeypatch.setenv("HERMES_HOME", str(tmp_path / ".hermes"))
return tmp_path
class TestCwdEcho:
def test_cd_reports_new_cwd(self, isolated_home, tmp_path):
target = tmp_path / "projdir"
target.mkdir()
r = json.loads(terminal_tool(f"cd {target}", task_id="t-cwd-1"))
assert r["exit_code"] == 0
assert "cwd" in r
assert os.path.realpath(r["cwd"]) == os.path.realpath(str(target))
def test_non_cd_command_has_no_cwd_field(self, isolated_home):
r = json.loads(terminal_tool("echo hello", task_id="t-cwd-2"))
assert r["exit_code"] == 0
assert "cwd" not in r
def test_cwd_persists_and_stops_reporting_when_stable(self, isolated_home, tmp_path):
target = tmp_path / "stable"
target.mkdir()
r1 = json.loads(terminal_tool(f"cd {target}", task_id="t-cwd-3"))
assert "cwd" in r1
# Next command runs IN the new cwd without changing it: no echo.
r2 = json.loads(terminal_tool("pwd", task_id="t-cwd-3"))
assert "cwd" not in r2
assert os.path.realpath(r2["output"].strip()) == os.path.realpath(str(target))
def test_cd_within_chain_reports_final_dir(self, isolated_home, tmp_path):
a = tmp_path / "a"
b = tmp_path / "b"
a.mkdir(); b.mkdir()
r = json.loads(terminal_tool(f"cd {a} && cd {b} && echo done", task_id="t-cwd-4"))
assert r["exit_code"] == 0
assert os.path.realpath(r.get("cwd", "")) == os.path.realpath(str(b))
def test_workdir_override_does_not_echo(self, isolated_home, tmp_path):
# Per-command workdir is transient by contract; it must not surface
# a cwd field (the session cwd did not change).
sub = tmp_path / "transient"
sub.mkdir()
r = json.loads(terminal_tool("echo hi", workdir=str(sub), task_id="t-cwd-5"))
assert r["exit_code"] == 0
assert "cwd" not in r
def test_failed_cd_no_echo(self, isolated_home):
r = json.loads(terminal_tool("cd /nonexistent_dir_zzz_42", task_id="t-cwd-6"))
assert r["exit_code"] != 0
assert "cwd" not in r