diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 17a1226061b9..f879b05a82ab 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -220,6 +220,11 @@ def ensure_hermes_home(): # (terminal and execute_code). Skill-declared required_environment_variables # are passed through automatically; this list is for non-skill use cases. "env_passthrough": [], + # Env var names containing proxy-brokered placeholders (not real secrets) + # that should bypass the provider-secret blocklist. For runtimes like + # NVIDIA OpenShell where a network proxy rewrites placeholder values + # with real credentials on egress (e.g. SLACK_BOT_TOKEN, SLACK_APP_TOKEN). + "proxy_credentials": [], "docker_image": "nikolaik/python-nodejs:python3.11-nodejs20", "docker_forward_env": [], "singularity_image": "docker://nikolaik/python-nodejs:python3.11-nodejs20", diff --git a/tools/environments/local.py b/tools/environments/local.py index 27282b6ef67a..b73f7d5c79e6 100644 --- a/tools/environments/local.py +++ b/tools/environments/local.py @@ -130,6 +130,29 @@ def _build_provider_env_blocklist() -> frozenset: _HERMES_PROVIDER_ENV_BLOCKLIST = _build_provider_env_blocklist() +# Guard so proxy_credentials registration runs only once, not on every subprocess call. +_proxy_credentials_registered = False + + +def _ensure_proxy_credentials_registered() -> None: + """Register proxy_credentials from config into env_passthrough (once). + + Called from both _sanitize_subprocess_env and _make_run_env so that + whichever path runs first triggers registration. + """ + global _proxy_credentials_registered + if _proxy_credentials_registered: + return + try: + from cli import CLI_CONFIG + proxy_creds = CLI_CONFIG.get("terminal", {}).get("proxy_credentials", []) + if proxy_creds: + from tools.env_passthrough import register_env_passthrough + register_env_passthrough(proxy_creds) + except ImportError: + pass + _proxy_credentials_registered = True + def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = None) -> dict: """Filter Hermes-managed secrets from a subprocess environment. @@ -139,6 +162,8 @@ def _sanitize_subprocess_env(base_env: dict | None, extra_env: dict | None = Non :mod:`tools.env_passthrough` (skill-declared or user-configured) also bypass the blocklist. """ + _ensure_proxy_credentials_registered() + try: from tools.env_passthrough import is_env_passthrough as _is_passthrough except Exception: @@ -271,6 +296,7 @@ def _is_noise(line: str) -> bool: def _make_run_env(env: dict) -> dict: """Build a run environment with a sane PATH and provider-var stripping.""" + _ensure_proxy_credentials_registered() try: from tools.env_passthrough import is_env_passthrough as _is_passthrough except Exception: