diff --git a/cli.py b/cli.py index 3608f440e2f..cf11c62c067 100644 --- a/cli.py +++ b/cli.py @@ -517,6 +517,7 @@ def load_cli_config() -> Dict[str, Any]: "container_disk": "TERMINAL_CONTAINER_DISK", "container_persistent": "TERMINAL_CONTAINER_PERSISTENT", "docker_volumes": "TERMINAL_DOCKER_VOLUMES", + "docker_env": "TERMINAL_DOCKER_ENV", "docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE", "docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER", "sandbox_dir": "TERMINAL_SANDBOX_DIR", @@ -540,7 +541,7 @@ def load_cli_config() -> Dict[str, Any]: continue if _file_has_terminal_config or env_var not in os.environ: val = terminal_config[config_key] - if isinstance(val, list): + if isinstance(val, (list, dict)): os.environ[env_var] = json.dumps(val) else: os.environ[env_var] = str(val) diff --git a/gateway/run.py b/gateway/run.py index c8b7a34ea42..af144fe94e5 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -460,6 +460,7 @@ if _config_path.exists(): "container_disk": "TERMINAL_CONTAINER_DISK", "container_persistent": "TERMINAL_CONTAINER_PERSISTENT", "docker_volumes": "TERMINAL_DOCKER_VOLUMES", + "docker_env": "TERMINAL_DOCKER_ENV", "docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE", "docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER", "sandbox_dir": "TERMINAL_SANDBOX_DIR", @@ -478,7 +479,7 @@ if _config_path.exists(): # receives a literal "~/" which the kernel rejects. if _cfg_key == "cwd" and isinstance(_val, str): _val = os.path.expanduser(_val) - if isinstance(_val, list): + if isinstance(_val, (list, dict)): os.environ[_env_var] = json.dumps(_val) else: os.environ[_env_var] = str(_val) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 3740fc2223e..2b0262dbac0 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -4843,6 +4843,7 @@ def set_config_value(key: str, value: str): "terminal.vercel_runtime": "TERMINAL_VERCEL_RUNTIME", "terminal.docker_mount_cwd_to_workspace": "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE", "terminal.docker_run_as_host_user": "TERMINAL_DOCKER_RUN_AS_HOST_USER", + "terminal.docker_env": "TERMINAL_DOCKER_ENV", # terminal.cwd intentionally excluded — CLI resolves at runtime, # gateway bridges it in gateway/run.py. Persisting to .env causes # stale values to poison child processes. diff --git a/tests/tools/test_terminal_config_env_sync.py b/tests/tools/test_terminal_config_env_sync.py index 892062fae71..1aecea0cd7c 100644 --- a/tests/tools/test_terminal_config_env_sync.py +++ b/tests/tools/test_terminal_config_env_sync.py @@ -208,3 +208,19 @@ def test_docker_mount_cwd_to_workspace_is_bridged_everywhere(): assert "docker_mount_cwd_to_workspace" in _gateway_env_map_keys() assert "docker_mount_cwd_to_workspace" in _save_config_env_sync_keys() assert "TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE" in _terminal_tool_env_var_names() + + +def test_docker_env_is_bridged_everywhere(): + """Regression pin for docker_env config key being silently ignored. + + ``terminal.docker_env`` in config.yaml specifies extra env vars to inject + into the Docker container at runtime. The key was present in + _create_environment's container_config consumer (line ~1130) but never + bridged from config.yaml to TERMINAL_DOCKER_ENV, so the dict was always + empty regardless of what the user set. Guard all four bridging points so + this cannot regress. + """ + assert "docker_env" in _cli_env_map_keys() + assert "docker_env" in _gateway_env_map_keys() + assert "docker_env" in _save_config_env_sync_keys() + assert "TERMINAL_DOCKER_ENV" in _terminal_tool_env_var_names() diff --git a/tools/terminal_tool.py b/tools/terminal_tool.py index 52de7873e5f..5d6b80c1bc4 100644 --- a/tools/terminal_tool.py +++ b/tools/terminal_tool.py @@ -1085,6 +1085,7 @@ def _get_env_config() -> Dict[str, Any]: "container_disk": _parse_env_var("TERMINAL_CONTAINER_DISK", "51200"), # MB (default 50GB) "container_persistent": os.getenv("TERMINAL_CONTAINER_PERSISTENT", "true").lower() in ("true", "1", "yes"), "docker_volumes": _parse_env_var("TERMINAL_DOCKER_VOLUMES", "[]", json.loads, "valid JSON"), + "docker_env": _parse_env_var("TERMINAL_DOCKER_ENV", "{}", json.loads, "valid JSON"), "docker_run_as_host_user": os.getenv("TERMINAL_DOCKER_RUN_AS_HOST_USER", "false").lower() in ("true", "1", "yes"), }