b0f44d3fad
GatewayRunner._run_agent's run_sync() wrote the per-turn session key to the process-global os.environ["HERMES_SESSION_KEY"]. Because os.environ is shared across the whole process, concurrent gateway sessions (e.g. two Discord threads) clobbered each other's value. A tool worker thread whose approval contextvar was unset then fell back to os.environ via get_current_session_key() and read whichever session ran run_sync() last — routing "Command Approval Required" prompts to the wrong thread. Session routing is already concurrency-safe via contextvars: - gateway/session_context.py _SESSION_KEY (set in set_session_vars) - tools/approval.py _approval_session_key (set via set_current_session_key right before the agent runs, inherited by tool worker threads) The only non-test readers of HERMES_SESSION_KEY (tools/approval.py, tools/terminal_tool.py, tools/kanban_tools.py) all prefer the contextvar with os.environ as a mere fallback. CLI/cron/TUI set their own os.environ via separate export paths (e.g. the TUI parent exporting it into the agent subprocess), so removing this in-process write does not affect them. Adds regression tests asserting the resolver prefers the contextvar and does not leak a concurrent session's cleared/clobbered os.environ value. Closes #24100 Co-authored-by: Yosapol Jitrak <yosapol@jitrak.dev>