From 7cad5da0f06b7efebcc9eae6bbe2d0109c525af7 Mon Sep 17 00:00:00 2001 From: Sagitario JPN <301893261+sagitario-jpn@users.noreply.github.com> Date: Sat, 5 Sep 2026 18:25:05 +0530 Subject: [PATCH] fix(tools): every sandbox creator uses the shared container_config shaper (#76906, #87995, #84027, #100019) The prompt backend-probe and execute_code each kept a private (key, default) table for container_config and drifted from the terminal tool's: the probe omitted docker_network, so under docker_network: false it started a bridge-networked container; execute_code omitted docker_extra_args, docker_forward_env and docker_env, so a sandbox created from that path lost the operator's settings. Both now call terminal_tool_backends._container_config_from_config. Salvaged from #62023 by @sagitario-jpn (the probe docker_network fix), widened to the whole class. --- agent/prompt_builder.py | 14 ++++---------- tools/code_execution_tool.py | 13 ++++--------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/agent/prompt_builder.py b/agent/prompt_builder.py index 9e00dd0dc2..7ee3cde17a 100644 --- a/agent/prompt_builder.py +++ b/agent/prompt_builder.py @@ -845,13 +845,6 @@ def _tenv_read(name: str, default: str = "") -> str: _BACKEND_IMAGE_KEYS = {b: f"{b}_image" for b in ("docker", "singularity", "modal", "daytona")} # (config key, default) pairs forwarded to _create_environment's container_config. -_CONTAINER_CONFIG_DEFAULTS = ( - ("container_cpu", 1), ("container_memory", 5120), ("container_disk", 51200), ("container_persistent", True), - ("modal_mode", "auto"), ("docker_volumes", []), ("docker_mount_cwd_to_workspace", False), - ("docker_forward_env", []), ("docker_env", {}), ("docker_run_as_host_user", False), ("docker_extra_args", []), - ("docker_shm_size", "1g"), ("docker_persist_across_processes", True), ("docker_shared_container_key", ""), - ("docker_orphan_reaper", True), -) # Single-line POSIX probe; `2>/dev/null` keeps a missing binary from polluting output. _BACKEND_PROBE_CMD = ( "printf 'os=%s\\nkernel=%s\\nhome=%s\\ncwd=%s\\nuser=%s\\n' \"$(uname -s 2>/dev/null || echo unknown)\" " @@ -862,16 +855,17 @@ _BACKEND_PROBE_CMD = ( def _run_backend_probe(env_type: str, terminal_tool) -> str: """Execute the probe command inside a freshly built backend; "" when it yields nothing.""" - from tools.terminal_tool_backends import _create_environment, _ssh_config_from_config + from tools.terminal_tool_backends import _container_config_from_config, _create_environment, _ssh_config_from_config from tools.terminal_tool_lifecycle import _cleanup_env config = terminal_tool._get_env_config() - # Mirrors tools/terminal_tool.py's live-command assembly (`_create_environment` is the factory). + # Same container_config shaper as the live terminal path: a private copy of the key table here + # drifted (no docker_network) and gave the probe a bridge-networked container under lockdown. env = _create_environment( env_type=env_type, image=config.get(_BACKEND_IMAGE_KEYS[env_type], "") if env_type in _BACKEND_IMAGE_KEYS else "", cwd=config.get("cwd", ""), timeout=config.get("timeout", 180), ssh_config=_ssh_config_from_config(config) if env_type == "ssh" else None, - container_config=({k: config.get(k, d) for k, d in _CONTAINER_CONFIG_DEFAULTS} + container_config=(_container_config_from_config(config) if terminal_tool._is_container_backend(env_type) else None), task_id="prompt-backend-probe", host_cwd=config.get("host_cwd"), ) diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index bf56c3e7c2..a06bc98679 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -394,17 +394,10 @@ def _call(tool_name, args): # ---- Remote execution support (file-based RPC via terminal backend) ---- -# execute_code's container_config keys (a subset of terminal_tool's; the create path fills the rest). -_CONTAINER_CONFIG_DEFAULTS = ( - ("container_cpu", 1), ("container_memory", 5120), ("container_disk", 51200), ("container_persistent", True), - ("vercel_runtime", ""), ("docker_volumes", []), ("docker_run_as_host_user", False), ("docker_network", True), -) - - def _get_or_create_env(task_id: str): """``(env, env_type)`` — the environment the terminal/file tools share for *task_id*, created on first use (same double-checked per-task lock pattern as file_tools._get_file_ops).""" - from tools.terminal_tool_backends import _create_environment, _ssh_config_from_config + from tools.terminal_tool_backends import _container_config_from_config, _create_environment, _ssh_config_from_config from tools.terminal_tool import ( _active_environments, _env_lock, _get_env_config, _last_activity, _start_cleanup_thread, _creation_locks, _creation_locks_lock, _task_env_overrides, @@ -431,7 +424,9 @@ def _get_or_create_env(task_id: str): overrides = _task_env_overrides.get(effective_task_id, {}) container_config = None if _is_container_backend(env_type): - container_config = {key: config.get(key, default) for key, default in _CONTAINER_CONFIG_DEFAULTS} + # Shared shaper: execute_code's own key subset dropped docker_extra_args / docker_forward_env / + # docker_env, so a sandbox created from this path lost the operator's configured settings. + container_config = _container_config_from_config(config) logger.info("Creating new %s environment for execute_code task %s...", env_type, effective_task_id[:8]) env = _create_environment(