fix(multiplex): children spawned for a served profile start from that profile's env

Under gateway.multiplex_profiles (and the Desktop/dashboard backend serving named
profiles) os.environ holds the LAUNCH profile's .env. Five spawn sites built a
child's env from it while acting for another profile, so the child saw the
launch profile's HERMES_HOME (bot_relay, key_cmd), its credentials, HERMES_MODEL
and TERMINAL_* policy, and none of the served profile's own .env:

- tui_gateway/server.py _SlashWorker: pinned HERMES_HOME but kept the launch
  base with tier-2 credentials + settings.
- tools/bot_relay.py delivery_env (relay RPC + --run-delivery): dict(os.environ).
- tools/browser_tool.py _build_browser_env: re-added BROWSERBASE/FIRECRAWL/
  BROWSER_USE keys from os.environ after the scrub.
- plugins/platforms/a2a/adapter.py _forward_to_profile: {**os.environ}.
- agent/command_token_source.py _mint: key_cmd helper inherited os.environ.

tools.environments.local.served_profile_child_env is the one builder: pin the
target home, drop the launch profile's .env residue and bridged TERMINAL_*
(strip_launch_profile_env), and for children that legitimately run with the
profile's credentials (agent worker, token helper) overlay the target profile's
own secrets - what a standalone `hermes -p X` loads itself, never a sibling's.
The browser keeps the provider scrub and re-adds only its passthrough keys via
get_secret. Outside multiplex the env is unchanged.

Live proof from inside the child (launch A, served B, multiplex on): all five
children print HERMES_HOME == B, see B_MARKER=b from B's .env and do not see
A_MARKER; the browser child gets B's FIRECRAWL_API_KEY. On base every one leaked
A_MARKER and lacked B_MARKER; bot_relay and key_cmd also had A's HERMES_HOME.
This commit is contained in:
teknium1
2026-09-14 23:31:07 -07:00
committed by Teknium
parent b66e7f2149
commit d1794d5539
9 changed files with 172 additions and 24 deletions
+5 -3
View File
@@ -585,9 +585,11 @@ class A2AAdapter(BasePlatformAdapter):
profile, "SELECT id FROM sessions WHERE title = ? ORDER BY started_at DESC LIMIT 1",
(session_title,), "A2A: could not lookup forwarded session")
cmd = ["hermes", "chat", "-q", framed_text, "-Q", "--source", "a2a"] + (["--resume", session_id] if session_id else [])
env = {**os.environ, "HERMES_A2A_PEER": peer}
if home := _profile_home(profile):
env["HERMES_HOME"] = home
# The child IS the target profile's turn: build its env for that home (launch .env /
# TERMINAL_* residue dropped, the target's own secrets overlaid), not the gateway's raw environ.
from tools.environments.local import served_profile_child_env
env = served_profile_child_env(target_home=_profile_home(profile), inherit_credentials=True)
env["HERMES_A2A_PEER"] = peer
start = time.time()
try:
proc = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace",