diff --git a/gateway/run.py b/gateway/run.py index 40be1f3af9658..6b9a3a01fd637 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -27020,6 +27020,14 @@ async def start_gateway(config: Optional[GatewayConfig] = None, replace: bool = def main(): """CLI entry point for the gateway.""" + # Advertise the agent harness to child processes (AI_AGENT is the + # cross-agent standard; HERMES_AGENT the Hermes-specific marker — see + # _advertise_agent_env in hermes_cli/main.py, kept inline here to avoid + # importing that module's startup side effects). setdefault so an outer + # harness is never clobbered. + os.environ.setdefault("AI_AGENT", "hermes") + os.environ.setdefault("HERMES_AGENT", "true") + # Force UTF-8 stdio on Windows — gateway logs and startup banner would # otherwise UnicodeEncodeError on cp1252 consoles. No-op on POSIX. try: diff --git a/hermes_cli/main.py b/hermes_cli/main.py index e6612636da75e..c1b3fa5e9fe5a 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -11180,12 +11180,30 @@ def cmd_claw(args): claw_command(args) +def _advertise_agent_env() -> None: + """Advertise the agent harness to child processes. + + ``AI_AGENT`` is the emerging cross-agent standard (huggingface_hub's agent + detection reads it; pi and other agents set it — earendil-works/pi#7493) + so generic tooling can attribute subprocesses to the harness that spawned + them. ``HERMES_AGENT`` is the Hermes-specific marker. setdefault: never + clobber an outer harness (e.g. Hermes running inside another agent's + terminal). + """ + os.environ.setdefault("AI_AGENT", "hermes") + os.environ.setdefault("HERMES_AGENT", "true") + + def main(): """Main entry point for hermes CLI.""" # Cosmetic: make the process show up as 'hermes' instead of 'python3.11' # in ps/top/htop. Non-fatal — just a nicer UX. _set_process_title() + # Let child processes (and tools like huggingface_hub) detect they run + # under an AI agent harness. + _advertise_agent_env() + # Force UTF-8 stdio on Windows before anything prints. No-op elsewhere. try: from hermes_cli.stdio import configure_windows_stdio diff --git a/tests/hermes_cli/test_agent_env_advertisement.py b/tests/hermes_cli/test_agent_env_advertisement.py new file mode 100644 index 0000000000000..86fb12f036494 --- /dev/null +++ b/tests/hermes_cli/test_agent_env_advertisement.py @@ -0,0 +1,34 @@ +"""Tests for the AI_AGENT / HERMES_AGENT harness-attribution env vars. + +Port of earendil-works/pi#7493: entry points advertise the agent harness to +child processes via the cross-agent ``AI_AGENT`` standard plus a +Hermes-specific marker, without clobbering an outer harness. +""" + +import os + +from hermes_cli.main import _advertise_agent_env + + +class TestAdvertiseAgentEnv: + def test_sets_both_vars_when_unset(self, monkeypatch): + monkeypatch.delenv("AI_AGENT", raising=False) + monkeypatch.delenv("HERMES_AGENT", raising=False) + _advertise_agent_env() + assert os.environ["AI_AGENT"] == "hermes" + assert os.environ["HERMES_AGENT"] == "true" + + def test_does_not_clobber_outer_harness(self, monkeypatch): + monkeypatch.setenv("AI_AGENT", "pi") + monkeypatch.delenv("HERMES_AGENT", raising=False) + _advertise_agent_env() + assert os.environ["AI_AGENT"] == "pi" + assert os.environ["HERMES_AGENT"] == "true" + + def test_idempotent(self, monkeypatch): + monkeypatch.delenv("AI_AGENT", raising=False) + monkeypatch.delenv("HERMES_AGENT", raising=False) + _advertise_agent_env() + _advertise_agent_env() + assert os.environ["AI_AGENT"] == "hermes" + assert os.environ["HERMES_AGENT"] == "true" diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index bae4a6166c20a..b02b7cdd8ce40 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -864,6 +864,8 @@ Unset the variable or remove it from `.env` to restore normal writes (still subj | `SESSION_IDLE_MINUTES` | Reset sessions after N minutes of inactivity (default: 1440) | | `SESSION_RESET_HOUR` | Daily reset hour in 24h format (default: 4 = 4am) | | `HERMES_SESSION_ID` | **Exported automatically into every tool subprocess** Hermes spawns (`terminal`, `execute_code`, persistent shell, Docker/Singularity backends, delegated subagent runs). Set by the agent to the current session ID; user scripts called from tools can read it to correlate their output, telemetry, or side effects with the originating Hermes session. **You should not set this manually** — overriding it from a parent shell only takes effect outside an agent run, and is overwritten the moment the agent starts a session. | +| `AI_AGENT` | **Set to `hermes` by the CLI and gateway entry points** (only when not already set by an outer harness). The emerging cross-agent standard for child-process attribution — generic tooling (e.g. huggingface_hub's agent detection) reads it to know it runs under an AI agent. Don't set manually. | +| `HERMES_AGENT` | **Set to `true` by the CLI and gateway entry points** so child processes can detect they run inside Hermes specifically. Don't set manually. | ## Context Compression (config.yaml only)