Skip to content

fix(tools): centralize container_config across sandbox entry points - #60815

Closed
NaMinhyeok wants to merge 2 commits into
NousResearch:mainfrom
NaMinhyeok:fix/execute-code-container-config
Closed

NaMinhyeok wants to merge 2 commits into
NousResearch:mainfrom
NaMinhyeok:fix/execute-code-container-config

Conversation

@NaMinhyeok

@NaMinhyeok NaMinhyeok commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes container environment settings being silently dropped depending on which production path calls _create_environment().

There are four callers that need the same container_config:

  1. terminal_tool
  2. execute_code
  3. file tools
  4. the system-prompt remote-backend probe

The first three share one collapsed environment slot per task, so whichever path creates or recreates the sandbox determines the configuration reused by all three. The prompt probe uses a separate slot, but it also creates a real container.

Before this PR:

  • execute_code omitted docker_forward_env, docker_env, docker_extra_args, modal_mode, mount, persistence, and orphan-reaper settings.
  • file tools omitted docker_env, docker_extra_args, modal_mode, persistence, and orphan-reaper settings.
  • the prompt backend probe omitted docker_network, so terminal.docker_network: false silently fell back to a network-enabled probe container.

This PR adds one private _build_container_config(config) projection in tools/terminal_tool.py and routes all four callers through it. That fixes the current losses and removes the hand-copied dictionaries that repeatedly drifted.

Why this is not already fixed

Earlier fixes covered only subsets of the call paths. #14235 fixed the terminal dictionary, and #12900 partially fixed file tools; the execute-code path remained incomplete. The prompt-probe docker_network omission was a fourth sibling path not covered by the existing three-module regression guard.

Related work and contributor credit

This patch builds on prior reports and implementations:

elozadaf and Ted Malone are preserved as co-authors on follow-up commit 2a7daf1d4. Related historical reports/attempts include #12534, #17620, #16214, and #5722.

Changes made

  • tools/terminal_tool.py — add the canonical container-config builder and use it in the terminal path.
  • tools/code_execution_tool.py — use the shared builder.
  • tools/file_tools.py — use the shared builder.
  • agent/prompt_builder.py — use the shared builder so the probe honors docker_network and every other container setting.
  • Regression tests now verify:
    • all 14 existing settings preserve non-default sentinel values;
    • terminal, execute-code, and file-tool callers pass the canonical builder output;
    • real TERMINAL_* parsing reaches the real environment factory and final Docker constructor for forward_env, static env, extra args, and network lockdown;
    • the prompt probe carries docker_network: false through the real parser/builder/factory chain.

How to test

python -m pytest \
  tests/tools/test_execute_code_container_config.py \
  tests/tools/test_file_tools_container_config.py \
  tests/tools/test_docker_network_config.py \
  tests/tools/test_docker_environment.py \
  tests/tools/test_parse_env_var.py \
  tests/tools/test_docker_orphan_reaper_integration.py \
  tests/tools/test_modal_sandbox_fixes.py \
  tests/tools/test_terminal_config_env_sync.py \
  tests/tools/test_container_cwd_sanitize.py \
  tests/agent/test_prompt_builder.py -q

Result on the PR branch: 321 passed, 1 skipped.

The complete two-commit PR was also replayed onto current origin/main (e4ea0a0ed) in a temporary worktree: clean rebase, 321 passed, 1 skipped. Ruff, Python compilation, the Windows-footgun check, and git diff --check also pass.

Scope

  • Bug fix only; no new tool, schema, config key, environment variable, or user-facing documentation surface.
  • Local and SSH behavior is unchanged (container_config=None).
  • No system-prompt text or conversation state changes; prompt-cache stability is unaffected.

Checklist

  • Read CONTRIBUTING.md and AGENTS.md.
  • Conventional commit messages.
  • Searched and credited related issues/PRs.
  • Focused change with behavior-level regression coverage.
  • Tested on macOS (Apple Silicon) and replayed on latest main.
  • No documentation/config example update required because no public setting changed.

…l parity

The three sandbox entry points (terminal, execute_code, file tools) share
one environment slot per task but hand-copy the container_config dict they
pass to _create_environment. The copies drifted: execute_code's dict lacks
docker_forward_env, docker_env, docker_extra_args, modal_mode,
docker_mount_cwd_to_workspace, docker_persist_across_processes and
docker_orphan_reaper; file_tools lacks docker_env, docker_extra_args,
modal_mode and the persist/reaper flags.

_create_environment silently defaults missing keys (cc.get(key, default)),
so whenever execute_code is the tool that (re)creates the shared
environment - e.g. after the idle-lifetime cleanup evicts it - the new
environment is built with forward_env=[] and every secret listed in
docker_forward_env vanishes from the session env snapshot for all tools
until a terminal/file call happens to recreate it. Same mechanism for the
other dropped keys.

Bring both dicts to full terminal parity and add regression tests that
pin every key with a non-default sentinel and assert execute_code and
file_tools build identical configs.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists tool/code-exec execute_code sandbox tool/file File tools (read, write, patch, search) area/docker Docker image, Compose, packaging labels Jul 8, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Competing-PR cluster for container_config parity across the sandbox entry points: this PR is effectively a rebase of the stale OPEN #30097 (execute_code subset) and OPEN #35660 (file_tools + code_execution parity) onto current main, extended to full docker/modal key parity with regression tests. Marking related_to those (not a duplicate). Maintainer to pick the canonical PR of the three.

@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for the focused parity fix. The premise holds on current main: tools/code_execution_tool.py:680-688 and tools/file_tools.py:1112-1122 omit configuration keys that tools/terminal_tool.py:2202-2216 forwards. Those values are consumed by _create_environment for Docker creation (tools/terminal_tool.py:1412-1446), and the tool paths share the collapsed default container slot (tools/terminal_tool.py:1123-1155).

The added test captures both entry points' container_config and verifies full parity, including the affected Docker values. Current-main inspection found no substantive issue with the implementation. The PR base has no subsequent changes in the touched production paths, and GitHub reports it mergeable, so salvage should be mechanical.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 10, 2026
@NaMinhyeok NaMinhyeok changed the title fix(tools): bring execute_code/file_tools container_config to terminal parity fix(tools): centralize container_config across sandbox entry points Jul 13, 2026
Route terminal, execute_code, file tools, and the prompt backend probe through one canonical container_config builder. This prevents future caller drift and fixes the probe silently ignoring docker_network=false.

Add behavior-level parity checks plus real env-config-to-Docker-factory regressions for secret forwarding and network lockdown.

Co-authored-by: elozadaf <elozada@lozortfederal.com>
Co-authored-by: Ted Malone <ted.malone@outlook.com>
@NaMinhyeok
NaMinhyeok force-pushed the fix/execute-code-container-config branch from 2fbf92e to 2a7daf1 Compare July 13, 2026 06:49

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 2a7daf1d4.

While re-auditing every production _create_environment() caller, I found a fourth hand-built container_config in agent/prompt_builder.py::_probe_remote_backend. That path omitted docker_network, so terminal.docker_network: false could still create a network-enabled probe container.

Rather than adding another one-off key fix, the follow-up now:

  • adds one canonical _build_container_config(config) projection in tools/terminal_tool.py;
  • routes terminal, execute-code, file tools, and the prompt backend probe through it;
  • preserves local/SSH behavior (container_config=None);
  • replaces the incomplete source-shape guard with behavior-level parity checks;
  • adds real TERMINAL_* → config parser → builder → _create_environment → fake Docker factory regressions for secret forwarding, static env, extra args, and docker_network=false.

Contributor credit from the prior implementations is preserved: elozadaf (#30097) and Ted Malone / temalo (#35660) are co-authors on the follow-up commit, and #8320 is credited in the PR description.

Validation:

  • relevant suite: 321 passed, 1 skipped;
  • complete two-commit PR replayed cleanly onto current main: 321 passed, 1 skipped;
  • Ruff, Python compilation, Windows-footgun scan, and git diff --check: passed.

The PR title and description have also been updated to match the final four-caller implementation and reproducible test command.

@alt-glitch alt-glitch added comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/terminal Terminal execution and process management backend/docker Docker container execution labels Jul 13, 2026
@sda0831

sda0831 commented Jul 23, 2026

Copy link
Copy Markdown

Independent confirmation from production (Hermes v0.19.0, Synology DSM / ContainerManager):

We hit exactly this. file_tools and code_execution_tool built partial container_config dicts missing docker_extra_args (and code_execution also docker_forward_env). Because the tool paths share the collapsed task=default container slot, whenever the agent's first container op was a file-read or code-exec rather than a terminal command, it created the shared container without the configured --network — silently landing on the default bridge, which every later tool and cron then reused. It was slow to diagnose precisely because config, .env, and the env bridge all carried the right value; only the caller-built dict dropped it.

The _build_container_config() centralization here is the right fix and would have prevented all of it, including the fourth prompt_builder probe path. +1 to landing this over the per-key patches.

@kentimsit

Copy link
Copy Markdown
Contributor

@teknium1 the bug still exists. Docker containers may not get the env variables depending on which tool created them. This makes Docker containers unusable unless the code is fixed locally. However, this PR is now stale and out of sync with main.

Should I create replacement PR based on current main that preserves this shared-builder approach and credits the prior work in #30097, #35660, and #60815?

@NaMinhyeok

Copy link
Copy Markdown
Contributor Author

Closing this stale/conflicting branch in favor of the current-main refresh in #90050, which explicitly preserves attribution for the shared container-config projection proposed here. The narrower sandbox egress follow-up is also being worked in #99723.

@NaMinhyeok NaMinhyeok closed this Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging backend/docker Docker container execution comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data tool/code-exec execute_code sandbox tool/file File tools (read, write, patch, search) tool/terminal Terminal execution and process management type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants