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:
@@ -116,7 +116,7 @@ def _(rid, params: dict, _root=_relay_root, _run=_run_delivery) -> dict:
|
||||
def _detail(p) -> str:
|
||||
return (p.stderr or p.stdout or "").strip()[-500:]
|
||||
|
||||
turn_env = delivery_env(author)
|
||||
turn_env = delivery_env(author, live_home)
|
||||
|
||||
fd, tmp = tempfile.mkstemp(prefix="hermes-relay-dm-", suffix=".txt", text=True)
|
||||
try:
|
||||
|
||||
@@ -233,16 +233,14 @@ class _SlashWorker:
|
||||
# slash_worker runs the Hermes agent → needs provider credentials. Tier-1 secrets
|
||||
# (gateway/GitHub/infra) are still stripped (#29157). Global-remote / multi-profile sessions: the
|
||||
# worker must resolve config/skills/state against the session's profile home, not the gateway's
|
||||
# launch HERMES_HOME (#40677). The override goes through the build_subprocess_env factory's `extra`
|
||||
# (applied last, always wins) instead of a hand-rolled env["HERMES_HOME"] assignment.
|
||||
from tools.environments.local import build_subprocess_env
|
||||
# launch HERMES_HOME (#40677).
|
||||
from tools.environments.local import served_profile_child_env
|
||||
|
||||
# The worker runs the agent → needs provider credentials; tier-1 secrets (gateway/GitHub/
|
||||
# infra) are still stripped. Multi-profile sessions resolve against the session's profile
|
||||
# home via `extra` (applied last, always wins); the base already carries the HOME contract.
|
||||
env = _prepend_tool_paths(build_subprocess_env(
|
||||
hermes_subprocess_env(inherit_credentials=True), scrub_secrets=False,
|
||||
inherit_profile_home=False, extra={"HERMES_HOME": str(profile_home)} if profile_home else None))
|
||||
# infra) are still stripped. A served profile's worker gets THAT profile's home + secrets and
|
||||
# none of the launch profile's .env / TERMINAL_* residue, exactly what a standalone
|
||||
# `hermes -p X` would load itself.
|
||||
env = _prepend_tool_paths(served_profile_child_env(target_home=profile_home, inherit_credentials=True))
|
||||
# Internal slash workers must import the same checkout as their parent.
|
||||
module_root = str(Path(__file__).resolve().parent.parent)
|
||||
env["PYTHONPATH"] = os.pathsep.join(
|
||||
|
||||
Reference in New Issue
Block a user