feat(terminal): collapse subagent task_ids to shared container - #16177
Merged
Conversation
Before: delegate_task children each allocated their own terminal
sandbox keyed by child task_id. Starting extra containers (or Modal
sandboxes / Daytona workspaces) is expensive, and the subagent's work
is invisible to the parent — files written by the child in its
container don't exist in the parent's when the subagent returns.
After: a single `_resolve_container_task_id` helper maps any
tool-call task_id to "default" UNLESS an env override is registered
for it. The parent agent and all delegate_task children therefore
share one long-lived sandbox — installed packages, cwd, /workspace
files, and /tmp scratch carry over freely between them.
RL and benchmark environments (TerminalBench2, HermesSweEnv, ...)
opt in to isolation via `register_task_env_overrides(task_id, {...})`;
those task_ids survive the collapse and get their own sandbox,
preserving the per-task Docker image behavior these benchmarks rely on.
file_state / active-subagents registry / TUI events still key off the
original child task_id, so the 'subagent wrote a file the parent read'
warning and UI per-subagent panels keep working.
Tradeoff: parallel delegate_task children (tasks=[...]) now share one
bash/container. Concurrent cd, env-var mutations, and writes to the
same path will collide. If that bites a specific workflow, the
subagent can opt back into isolation via register_task_env_overrides.
Applied at four lookup sites:
- tools/terminal_tool.py terminal_tool() and get_active_env()
- tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd()
- tools/code_execution_tool.py _get_or_create_environment()
Docs: website/docs/user-guide/configuration.md updated to reflect the
shared-container reality and document the RL/benchmark carve-out.
Tests: tests/tools/test_shared_container_task_id.py (9 cases).
3 tasks
donald131
pushed a commit
to donald131/hermes-agent
that referenced
this pull request
May 2, 2026
…esearch#16177) Before: delegate_task children each allocated their own terminal sandbox keyed by child task_id. Starting extra containers (or Modal sandboxes / Daytona workspaces) is expensive, and the subagent's work is invisible to the parent — files written by the child in its container don't exist in the parent's when the subagent returns. After: a single `_resolve_container_task_id` helper maps any tool-call task_id to "default" UNLESS an env override is registered for it. The parent agent and all delegate_task children therefore share one long-lived sandbox — installed packages, cwd, /workspace files, and /tmp scratch carry over freely between them. RL and benchmark environments (TerminalBench2, HermesSweEnv, ...) opt in to isolation via `register_task_env_overrides(task_id, {...})`; those task_ids survive the collapse and get their own sandbox, preserving the per-task Docker image behavior these benchmarks rely on. file_state / active-subagents registry / TUI events still key off the original child task_id, so the 'subagent wrote a file the parent read' warning and UI per-subagent panels keep working. Tradeoff: parallel delegate_task children (tasks=[...]) now share one bash/container. Concurrent cd, env-var mutations, and writes to the same path will collide. If that bites a specific workflow, the subagent can opt back into isolation via register_task_env_overrides. Applied at four lookup sites: - tools/terminal_tool.py terminal_tool() and get_active_env() - tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd() - tools/code_execution_tool.py _get_or_create_environment() Docs: website/docs/user-guide/configuration.md updated to reflect the shared-container reality and document the RL/benchmark carve-out. Tests: tests/tools/test_shared_container_task_id.py (9 cases).
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…esearch#16177) Before: delegate_task children each allocated their own terminal sandbox keyed by child task_id. Starting extra containers (or Modal sandboxes / Daytona workspaces) is expensive, and the subagent's work is invisible to the parent — files written by the child in its container don't exist in the parent's when the subagent returns. After: a single `_resolve_container_task_id` helper maps any tool-call task_id to "default" UNLESS an env override is registered for it. The parent agent and all delegate_task children therefore share one long-lived sandbox — installed packages, cwd, /workspace files, and /tmp scratch carry over freely between them. RL and benchmark environments (TerminalBench2, HermesSweEnv, ...) opt in to isolation via `register_task_env_overrides(task_id, {...})`; those task_ids survive the collapse and get their own sandbox, preserving the per-task Docker image behavior these benchmarks rely on. file_state / active-subagents registry / TUI events still key off the original child task_id, so the 'subagent wrote a file the parent read' warning and UI per-subagent panels keep working. Tradeoff: parallel delegate_task children (tasks=[...]) now share one bash/container. Concurrent cd, env-var mutations, and writes to the same path will collide. If that bites a specific workflow, the subagent can opt back into isolation via register_task_env_overrides. Applied at four lookup sites: - tools/terminal_tool.py terminal_tool() and get_active_env() - tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd() - tools/code_execution_tool.py _get_or_create_environment() Docs: website/docs/user-guide/configuration.md updated to reflect the shared-container reality and document the RL/benchmark carve-out. Tests: tests/tools/test_shared_container_task_id.py (9 cases).
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
…esearch#16177) Before: delegate_task children each allocated their own terminal sandbox keyed by child task_id. Starting extra containers (or Modal sandboxes / Daytona workspaces) is expensive, and the subagent's work is invisible to the parent — files written by the child in its container don't exist in the parent's when the subagent returns. After: a single `_resolve_container_task_id` helper maps any tool-call task_id to "default" UNLESS an env override is registered for it. The parent agent and all delegate_task children therefore share one long-lived sandbox — installed packages, cwd, /workspace files, and /tmp scratch carry over freely between them. RL and benchmark environments (TerminalBench2, HermesSweEnv, ...) opt in to isolation via `register_task_env_overrides(task_id, {...})`; those task_ids survive the collapse and get their own sandbox, preserving the per-task Docker image behavior these benchmarks rely on. file_state / active-subagents registry / TUI events still key off the original child task_id, so the 'subagent wrote a file the parent read' warning and UI per-subagent panels keep working. Tradeoff: parallel delegate_task children (tasks=[...]) now share one bash/container. Concurrent cd, env-var mutations, and writes to the same path will collide. If that bites a specific workflow, the subagent can opt back into isolation via register_task_env_overrides. Applied at four lookup sites: - tools/terminal_tool.py terminal_tool() and get_active_env() - tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd() - tools/code_execution_tool.py _get_or_create_environment() Docs: website/docs/user-guide/configuration.md updated to reflect the shared-container reality and document the RL/benchmark carve-out. Tests: tests/tools/test_shared_container_task_id.py (9 cases).
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…esearch#16177) Before: delegate_task children each allocated their own terminal sandbox keyed by child task_id. Starting extra containers (or Modal sandboxes / Daytona workspaces) is expensive, and the subagent's work is invisible to the parent — files written by the child in its container don't exist in the parent's when the subagent returns. After: a single `_resolve_container_task_id` helper maps any tool-call task_id to "default" UNLESS an env override is registered for it. The parent agent and all delegate_task children therefore share one long-lived sandbox — installed packages, cwd, /workspace files, and /tmp scratch carry over freely between them. RL and benchmark environments (TerminalBench2, HermesSweEnv, ...) opt in to isolation via `register_task_env_overrides(task_id, {...})`; those task_ids survive the collapse and get their own sandbox, preserving the per-task Docker image behavior these benchmarks rely on. file_state / active-subagents registry / TUI events still key off the original child task_id, so the 'subagent wrote a file the parent read' warning and UI per-subagent panels keep working. Tradeoff: parallel delegate_task children (tasks=[...]) now share one bash/container. Concurrent cd, env-var mutations, and writes to the same path will collide. If that bites a specific workflow, the subagent can opt back into isolation via register_task_env_overrides. Applied at four lookup sites: - tools/terminal_tool.py terminal_tool() and get_active_env() - tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd() - tools/code_execution_tool.py _get_or_create_environment() Docs: website/docs/user-guide/configuration.md updated to reflect the shared-container reality and document the RL/benchmark carve-out. Tests: tests/tools/test_shared_container_task_id.py (9 cases).
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…esearch#16177) Before: delegate_task children each allocated their own terminal sandbox keyed by child task_id. Starting extra containers (or Modal sandboxes / Daytona workspaces) is expensive, and the subagent's work is invisible to the parent — files written by the child in its container don't exist in the parent's when the subagent returns. After: a single `_resolve_container_task_id` helper maps any tool-call task_id to "default" UNLESS an env override is registered for it. The parent agent and all delegate_task children therefore share one long-lived sandbox — installed packages, cwd, /workspace files, and /tmp scratch carry over freely between them. RL and benchmark environments (TerminalBench2, HermesSweEnv, ...) opt in to isolation via `register_task_env_overrides(task_id, {...})`; those task_ids survive the collapse and get their own sandbox, preserving the per-task Docker image behavior these benchmarks rely on. file_state / active-subagents registry / TUI events still key off the original child task_id, so the 'subagent wrote a file the parent read' warning and UI per-subagent panels keep working. Tradeoff: parallel delegate_task children (tasks=[...]) now share one bash/container. Concurrent cd, env-var mutations, and writes to the same path will collide. If that bites a specific workflow, the subagent can opt back into isolation via register_task_env_overrides. Applied at four lookup sites: - tools/terminal_tool.py terminal_tool() and get_active_env() - tools/file_tools.py _get_file_ops() and _get_live_tracking_cwd() - tools/code_execution_tool.py _get_or_create_environment() Docs: website/docs/user-guide/configuration.md updated to reflect the shared-container reality and document the RL/benchmark carve-out. Tests: tests/tools/test_shared_container_task_id.py (9 cases).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The top-level agent and all
delegate_tasksubagents now share one long-lived terminal sandbox. RL/benchmark rollouts keep their per-task isolation via the existingregister_task_env_overridesopt-in.Before: each delegate_task child spun up its own Docker container / Modal sandbox / Daytona workspace keyed by the child task_id. Expensive, and files/packages written by the child were invisible to the parent when the subagent returned.
After: a single
_resolve_container_task_idhelper intools/terminal_tool.pycollapses any tool-call task_id to"default"unless an env override is registered for that task_id. All four call sites (terminal_tool.terminal_tool+get_active_env, file_tools._get_file_ops+_get_live_tracking_cwd, code_execution_tool._get_or_create_environment) go through it.Changes
tools/terminal_tool.py— add_resolve_container_task_id(); routeterminal_tool()andget_active_env()through ittools/file_tools.py— route_get_file_ops()and_get_live_tracking_cwd()through ittools/code_execution_tool.py— route_get_or_create_environment()through ittests/tools/test_shared_container_task_id.py— 9 new regression tests covering None/empty/literal-default, subagent IDs, arbitrary session IDs, RL override honoured, cleared override re-collapses,get_active_envreads shared container from subagent ID,get_active_envhonours RL overridewebsite/docs/user-guide/configuration.md— rewrite Container-lifecycle paragraph to matchDesign notes
file_state/ active-subagents registry / TUI events still key off the original child task_id, so "subagent wrote a file the parent read" warnings and per-subagent UI panels keep working — only the container/sandbox lookup is collapsed.delegate_task(tasks=[...])children now share one bash / one/tmp. Concurrentcd, env-var mutations, and same-path writes will collide. Documented explicitly. Escape hatch:register_task_env_overrides(task_id, {...}).Validation
E2E against real Docker with
nikolaik/python-nodejsimage:/tmp/markthen subagent (task_id="subagent-1-abc") reads ithostnamecalls return the same IDregister_task_env_overrides_active_environmentsafter 5 mixed calls[default, subagent-1-abc, subagent-2-xyz, rl-task-42][default, rl-task-42]docker psrunning containersUnit tests: 9/9 new regression tests pass; 3796/3796 in
tests/tools/pass (no regressions).Depends on concept introduced by merged PR #16158 (container lifecycle doc).