mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-11 03:31:55 +00:00
feat: enrich system-prompt environment hints with host + terminal-backend info
build_environment_hints() now emits a factual block describing the execution environment on every prompt build: * Local backend: host OS, $HOME, and cwd — so the agent stops guessing paths from the hostname. Windows also gets two specific callouts: - hostname != username (prevents C:\Users\<hostname>\... bugs) - `terminal` shells out to bash (git-bash/MSYS), not PowerShell * Remote backend (docker/singularity/modal/daytona/ssh/vercel_sandbox): host info is SUPPRESSED — the agent's tools can't touch the host, so showing it is misleading. Instead we probe the backend once per process with `uname/whoami/pwd` and cache the result. On probe failure, fall back to a per-backend description that states only what we know from the backend choice itself (container type + likely OS family) without inventing user/cwd/$HOME. Linux/Mac local users now get a small helpful 3-line host block instead of an empty string. Zero change to the existing WSL hint paragraph. Tests: 8 new/updated in TestEnvironmentHints, including a regression guard that fails if a new remote backend is added without listing it in _REMOTE_TERMINAL_BACKENDS.
This commit is contained in:
parent
e0c03defd5
commit
26f5af52a8
2 changed files with 297 additions and 4 deletions
|
|
@ -839,15 +839,106 @@ class TestEnvironmentHints:
|
|||
def test_build_environment_hints_on_wsl(self, monkeypatch):
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: True)
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "/mnt/" in result
|
||||
assert "WSL" in result
|
||||
# WSL block still carries the always-on host info ahead of it.
|
||||
assert "User home directory:" in result
|
||||
|
||||
def test_build_environment_hints_not_wsl(self, monkeypatch):
|
||||
def test_build_environment_hints_on_linux_local(self, monkeypatch):
|
||||
import agent.prompt_builder as _pb
|
||||
import sys, platform
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.setattr(sys, "platform", "linux")
|
||||
monkeypatch.setattr(platform, "system", lambda: "Linux")
|
||||
monkeypatch.setattr(platform, "release", lambda: "6.8.0-generic")
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert result != ""
|
||||
assert "Host: Linux" in result
|
||||
assert "6.8.0-generic" in result
|
||||
assert "User home directory:" in result
|
||||
assert "Current working directory:" in result
|
||||
# Linux must NOT get the Windows-specific callouts.
|
||||
assert "PowerShell" not in result
|
||||
assert "hostname" not in result
|
||||
assert "WSL" not in result
|
||||
|
||||
def test_build_environment_hints_on_windows_local(self, monkeypatch):
|
||||
import agent.prompt_builder as _pb
|
||||
import sys
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.setattr(sys, "platform", "win32")
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "Host: Windows" in result
|
||||
assert "User home directory:" in result
|
||||
# Two Windows-specific callouts that must ALWAYS appear together:
|
||||
# hostname warning + bash-not-PowerShell warning.
|
||||
assert "hostname" in result
|
||||
assert "NOT the username" in result
|
||||
assert "bash" in result
|
||||
assert "PowerShell" in result
|
||||
|
||||
def test_build_environment_hints_on_macos_local(self, monkeypatch):
|
||||
import agent.prompt_builder as _pb
|
||||
import sys
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.setattr(sys, "platform", "darwin")
|
||||
monkeypatch.delenv("TERMINAL_ENV", raising=False)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert "Host: macOS" in result
|
||||
assert "User home directory:" in result
|
||||
# macOS must NOT get the Windows-specific callouts.
|
||||
assert "PowerShell" not in result
|
||||
assert "hostname" not in result
|
||||
|
||||
def test_build_environment_hints_suppresses_host_on_docker_backend(self, monkeypatch):
|
||||
"""Docker/remote backends must hide host info — the agent can only touch the backend."""
|
||||
import agent.prompt_builder as _pb
|
||||
import sys
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.setattr(sys, "platform", "win32")
|
||||
monkeypatch.setenv("TERMINAL_ENV", "docker")
|
||||
# Force the probe to fail so we exercise the static fallback path
|
||||
# deterministically (the live probe would try to spin up docker).
|
||||
monkeypatch.setattr(_pb, "_probe_remote_backend", lambda _t: None)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
# Host suppression: none of the local-backend lines should appear.
|
||||
assert "Host: Windows" not in result
|
||||
assert "User home directory:" not in result
|
||||
assert "PowerShell" not in result
|
||||
# Backend info must appear instead.
|
||||
assert "Terminal backend: docker" in result
|
||||
assert "inside" in result.lower()
|
||||
|
||||
def test_build_environment_hints_uses_live_probe_when_available(self, monkeypatch):
|
||||
"""When the probe succeeds, its output must appear in the hint block."""
|
||||
import agent.prompt_builder as _pb
|
||||
monkeypatch.setattr(_pb, "is_wsl", lambda: False)
|
||||
monkeypatch.setenv("TERMINAL_ENV", "modal")
|
||||
fake_probe_output = " OS: Linux 6.8.0\n User: root\n Home: /root\n Working directory: /workspace"
|
||||
monkeypatch.setattr(_pb, "_probe_remote_backend", lambda _t: fake_probe_output)
|
||||
_pb._clear_backend_probe_cache()
|
||||
result = _pb.build_environment_hints()
|
||||
assert result == ""
|
||||
assert "Terminal backend: modal" in result
|
||||
assert "Linux 6.8.0" in result
|
||||
assert "/workspace" in result
|
||||
|
||||
def test_remote_backend_list_covers_known_sandboxes(self):
|
||||
"""Regression guard: if someone adds a remote backend, they must list it here."""
|
||||
import agent.prompt_builder as _pb
|
||||
for backend in ("docker", "singularity", "modal", "daytona", "ssh", "vercel_sandbox"):
|
||||
assert backend in _pb._REMOTE_TERMINAL_BACKENDS, (
|
||||
f"{backend!r} must be in _REMOTE_TERMINAL_BACKENDS so its host "
|
||||
f"info is suppressed in the system prompt"
|
||||
)
|
||||
|
||||
|
||||
# =========================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue