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:
Teknium 2026-05-08 05:07:40 -07:00
parent e0c03defd5
commit 26f5af52a8
2 changed files with 297 additions and 4 deletions

View file

@ -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"
)
# =========================================================================