Skip to content

fix(tui-gateway): secondary side workers and branch builds honour their own terminal backend; launch env-only SSH/Docker policy survives multiplexing - #110207

Merged
teknium1 merged 3 commits into
mainfrom
fix/mux-triage-terminal-scope
Sep 13, 2026
Merged

teknium1 merged 3 commits into
mainfrom
fix/mux-triage-terminal-scope

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Under gateway.multiplex_profiles, a secondary profile's prompt.background / prompt.btw / preview.restart side agents and its resume/branch agent builds now run on that profile's own terminal backend, and launch-profile turns keep the process's env-only TERMINAL_* policy (systemd / op run / launcher-bridged SSH or Docker) instead of collapsing to local once a second profile is served.

Two P1 findings from the andrexibiza review of #108440, both re-verified RED on origin/main b9271bc with the review's own probes.

Changes

  • tui_gateway/methods_prompt.py::_spawn_side_agent and methods_session.py::_profile_build_scope enter _session_profile_runtime_scope — the same home → secrets → terminal composition a prompt turn binds (mirror of gateway/run.py::_profile_runtime_scope). The terminal scope covers the whole worker/build lifetime, resets on success and on exception, and a malformed secondary config still installs the fail-closed refusal scope. No ambient os.environ write is restored.
  • tui_gateway/launch_terminal_policy.py (new, ~45 LOC): freezes the process TERMINAL_* once, in server._profile_home right before the first secondary home is registered as served — the last moment ambient env is provably the launch profile's own. Never re-read from ambient state afterwards.
  • tools/terminal_scope.py::build_profile_terminal_scope(..., env_overlay=): optional trusted overlay sitting where the process env sits in the standalone bridge (defaults ← .env ← overlay ← explicit config.yaml keys). prompt_turn._prepare_turn_input passes the launch snapshot for launch turns once multiplexing is active.
  • Tests: tests/tui_gateway/test_profile_terminal_scope_entrypoints.py — 2 per finding, red on base (side worker + branch build pick docker, scope reset on success/exception, os.environ untouched; launch turn keeps env-only SSH after activation and ignores ambient TERMINAL_* written afterwards).

Validation

Review probes (contract_probes.py, fresh process each, env -i), secondary terminal.backend: docker, launch TERMINAL_ENV=local / env-only TERMINAL_ENV=ssh TERMINAL_SSH_HOST=example.test with {} config:

probe before (origin/main b9271bc) after (this head)
terminal_side (prompt.background worker) {"scope_bound": false, "backend": "local"} ✗ {"scope_bound": true, "backend": "docker"} ✓
terminal_build (session.branch build) {"scope_bound": false, "backend": "local"} ✗ {"scope_bound": true, "backend": "docker"} ✓
terminal_launch (launch turn, secondary served) {"scope_bound": true, "backend": "local", "ssh_host": ""} ✗ {"scope_bound": true, "backend": "ssh", "ssh_host": "example.test"} ✓
reset/ambient probe — side ok/exc + build ok/exc all docker, scope None after each, os.environ["TERMINAL_ENV"] still local ✓
malformed secondary config.yaml inside _profile_build_scope — refusal scope, _get_env_config() raises TerminalPolicyUnavailable, reset after ✓

scripts/run_tests.sh tests/tui_gateway/ tests/tools/test_terminal_* → 154 files, 1938 passed, 0 failed. ruff, check-windows-footguns --all, check_compat_pointers, git diff --check clean.

Root cause

#108440 correctly stopped terminal_tool from bridging a routed profile's terminal.* into os.environ, which turned every "home-only" secondary entrypoint into an ambient-launch-backend leak, and 606903b's launch-turn scope rebuilt policy from files alone, so the launch process's env-only policy had nothing to be rebuilt from.

Refs #108440 review (andrexibiza), #107442 (ehz0ah).

Infographic

mux-terminal-scope

…own terminal scope

Under multiplexing tools/terminal_tool.py no longer bridges a profile's
terminal.* into os.environ while a home override is bound (#108440), so any
secondary-profile entrypoint that binds only the home (+ secrets) leaves
terminal_tool on the launch process's ambient TERMINAL_*: a secondary with
`terminal.backend: docker` ran its prompt.background / prompt.btw /
preview.restart side agents, its eager resume and its session.branch build
on the launch `local` backend.

_spawn_side_agent and _profile_build_scope now enter
_session_profile_runtime_scope, the same home -> secrets -> terminal
composition a prompt turn binds (and gateway/run.py::_profile_runtime_scope
mirrors). The terminal scope is installed for the whole worker/build
lifetime and reset on success and on exception; a malformed secondary
config still yields the fail-closed refusal scope. No ambient os.environ
write is restored.

Refs #108440 review (andrexibiza), #107442 (ehz0ah).
…olicy once multiplexing is active

606903b made launch-profile turns bind a file-backed terminal scope as
soon as any secondary home is served, so a poisoned ambient bridge can never
be the launch turn's authority. That scope is rebuilt from defaults +
<home>/.env + config.yaml only, which drops the launch process's legitimate
env-only policy: TERMINAL_ENV=ssh TERMINAL_SSH_HOST=example.test with a `{}`
config.yaml became backend=local, ssh_host='' the moment a second profile
was served.

tui_gateway/launch_terminal_policy.py freezes the process TERMINAL_* once,
in _profile_home right before the first secondary home is registered as
served — the last moment ambient env is provably the launch profile's own.
build_profile_terminal_scope takes that snapshot as a trusted env_overlay
sitting where the process env sits in the standalone bridge (explicit YAML
keys still win). Launch turns overlay the snapshot; ambient os.environ is
never re-read after activation, so a later secondary write is still
rejected, and the scope is reset after the turn as before.

Refs #108440 review (andrexibiza), #107442 (ehz0ah).
…l scope and launch env-only policy

Two per finding, proven red on origin/main b9271bc: secondary side
worker and branch build run their own docker backend (scope reset on success
and exception, os.environ untouched); launch turns keep env-only SSH policy
after activation and ignore ambient TERMINAL_* written afterwards.
@github-actions

github-actions Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

ran on e9ca8c9 — test(tui-gateway): invariants for secondary side-worker/buil

⚠️ Warnings

CI timings · View report · View job

Wall time 90m17s vs 5m15s (+1619.7%). 8 job(s) slower, 5 faster, 1 unchanged.

  • Python tests / Run tests: -12.0s
  • Check contributors / check-attribution: +11.0s
  • Detect affected areas: +7.0s
  • Check no committed infographics / check-no-committed-infographics: +6.0s
  • Python tests / e2e: +5.0s

OSV vulnerability scan · View job

76 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/tools Tool registry, model_tools, toolsets tool/terminal Terminal execution and process management area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Sep 13, 2026
@teknium1
teknium1 merged commit ce52c23 into main Sep 13, 2026
68 of 70 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/tools Tool registry, model_tools, toolsets comp/tui Terminal UI (ui-tui/ + tui_gateway/) P1 High — major feature broken, no workaround sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state 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.

2 participants