fix(terminal): isolate ambient bridge and bind launch scope under multiplexing (#107422) - #107714
Open
JoaoMarcos44 wants to merge 1 commit into
Open
JoaoMarcos44 wants to merge 1 commit into
JoaoMarcos44 wants to merge 1 commit into
Conversation
…tiplexing (NousResearch#107422) Multiplexed dashboard (app-global remote mode) previously allowed _ensure_terminal_env_bridged() to execute while a secondary profile's HERMES_HOME override was active. Because the bridge writes to process-global os.environ and latches _terminal_config_bridge_attempted=True, subsequent turns under the launch/primary profile inherited the secondary profile's docker policy, spawning containers with mismatched docker tags and mounts. Root Cause: - _ensure_terminal_env_bridged() only checked get_terminal_scope() is not None, ignoring get_hermes_home_override(). When an unscoped secondary profile call triggered the bridge, it wrote the secondary profile's terminal configuration into os.environ and latched the one-shot bridge attempt. Key Fixes: 1. tools/terminal_tool.py: - Early-return in _ensure_terminal_env_bridged() if get_hermes_home_override() is not None, ensuring secondary profile environments never mutate process-global os.environ or poison the one-shot bridge flag. 2. tools/terminal_scope.py: - Add optional ambient_env keyword to build_profile_terminal_scope(). - Introduce build_launch_terminal_scope() and install_launch_terminal_scope() to preserve launch-time ambient process environment while overriding explicit keys from launch config.yaml. 3. tui_gateway/prompt_turn.py: - When multiplexing is active (_served_profile_homes is non-empty), bind install_launch_terminal_scope() on launch-profile turns during _prepare_turn_input(). 4. tui_gateway/methods_session.py: - Bind install_profile_terminal_scope() in _profile_build_scope() for eager-resume and branch builds. 5. tui_gateway/methods_prompt.py: - Propagate and reset profile terminal scope across background thread boundaries in _spawn_side_agent() workers. 6. Cleaned duplicate docstring paragraphs in tui_gateway/server.py. 7. Regression Tests: - Added test_secondary_home_override_does_not_latch_ambient_env in tests/tools/test_terminal_env_bridge.py. - Added comprehensive tests for launch scope ambient preservation, multiplexed turn preparation, profile build scope, and side-agent worker propagation in tests/tools/test_terminal_scope_multiplex.py.
Related: competing fix for #107422 alongside earlier open #107442. Both PRs carry the same core change ( |
This branch has not been deployed
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
Fixes #107422.
In multiplexed dashboard mode (app-global remote mode), when a secondary profile's turn ran without an active terminal scope,
tools/terminal_tool._ensure_terminal_env_bridged()latched that secondary profile'sterminal.*configuration into process-globalos.environand set_terminal_config_bridge_attempted = True. Consequently, subsequent tool calls under the primary/launch profile inherited the secondary profile's docker policy (e.g.TERMINAL_DOCKER_IMAGE,TERMINAL_DOCKER_VOLUMES, and backend type), resulting in mislabeled containers and cross-profile configuration leaks (a residual issue following #68559).Root Cause Analysis
Unscoped Bridge Poisoning:
_ensure_terminal_env_bridged()intools/terminal_tool.pychecked only whetherget_terminal_scope() is not None. It did not check whetherget_hermes_home_override()was set. When an unscoped secondary profile call triggered the bridge, it read the secondary profile'sconfig.yamland latched those values intoos.environ, also marking_terminal_config_bridge_attempted = True. This poisoned all subsequent primary-profile calls.Scope Propagation Across Asynchronous Worker Boundaries:
_spawn_side_agent()intui_gateway/methods_prompt.pyspawns background daemon threads to execute side tasks (such asprompt.backgroundandpreview.restart). ContextVars do not automatically propagate across thread boundaries, causing background turns to lose their profile's terminal policy and secret scope unless explicitly bound in the worker.Eager Resume & Branch Build Scope Isolation:
In
tui_gateway/methods_session.py,_profile_build_scope()only boundHERMES_HOMEand secret scope. Eager-resume and branch builds run inside this scope, so without an installed terminal scope, terminal policy probing could leak or latch defaults.Launch Profile Scope & Ambient Environment Preservation:
Under multiplexing, once secondary profiles are served (
_served_profile_homesis non-empty), launch-profile turns need an authoritative terminal scope to prevent falling back to processos.environ. However, if the launch profile'sconfig.yamlomits aterminalsection, naive scope construction would overwrite ambient process environment variables (such asTERMINAL_ENV=sshset at launch). A dedicatedbuild_launch_terminal_scope()was needed to preserve ambient process environment while respecting explicit launch config overrides.Key Changes
tools/terminal_tool.py:_ensure_terminal_env_bridged()ifget_hermes_home_override() is not Nonebefore touchingos.environor setting_terminal_config_bridge_attempted = True. Secondary profile operations never latch into process-global environment.tools/terminal_scope.py:ambient_envparameter tobuild_profile_terminal_scope().build_launch_terminal_scope()andinstall_launch_terminal_scope(), which project ambientos.environunderTERMINAL_*keys and apply explicit launchconfig.yamloverrides.tui_gateway/prompt_turn.py:_prepare_turn_input(), when multiplexing is active (_served_profile_homesis non-empty), bindinstall_launch_terminal_scope()for launch profile turns.tui_gateway/methods_session.py:_profile_build_scope()to bind and resetinstall_profile_terminal_scope()alongside home and secret scopes.tui_gateway/methods_prompt.py:_spawn_side_agent()to bind and resetinstall_profile_terminal_scope(orinstall_launch_terminal_scopeif multiplexed) on daemon worker threads.tui_gateway/server.py:_profile_scoped.Tests:
tests/tools/test_terminal_env_bridge.py: Addedtest_secondary_home_override_does_not_latch_ambient_envvalidating that unscoped calls under secondary home override do not mutateos.environor mark_terminal_config_bridge_attempted.tests/tools/test_terminal_scope_multiplex.py: Added tests for ambient preservation on launch scope, multiplexed turn scope binding,_profile_build_scopeterminal binding, and thread-boundary propagation in_spawn_side_agent.Verification Evidence
tests/tools/test_terminal_env_bridge.py: 10 passedtests/tools/test_terminal_scope_multiplex.py: 14 passed