fix(delegation): preserve parent session identity across child runs - #75856
Merged
Conversation
…cess env AIAgent.__init__ calls set_current_session_id(self.session_id), which mutated both the task-local ContextVar and the process-global os.environ. Because _build_child_agent wraps construction in delegated_child_context(), the ContextVar write is harmless (task-local), but the os.environ write clobbered the parent's HERMES_SESSION_ID for the rest of the process — leaking the child id into parent tools and subprocesses spawned after the child was built. Root cause of HermesPRDelegationSessionContext: parent 20260729_212118_5d797e dispatched child 20260730_160515_736ea1; later parent terminal inherited HERMES_SESSION_ID=the child. Fix: set_current_session_id() skips the process-global os.environ write when called from within a delegated_child_context(). The child's own tools and subprocesses still resolve their id through the ContextVar (task-local), while the parent's process-wide env keeps the parent's session identity. Root agents (CLI, gateway, cron) retain both paths. Adds 7 regression tests covering single child, concurrent children (8 parallel), parent-tool observation after construction, and root-agent session rotation backward compatibility. All pass; ruff clean.
Contributor
૮ >ﻌ< ა ci reviewran on abf34d0 ℹ️ InfoDesktop E2E visual evidence · View test artifacts · View job2 visual diffs. inline evidence upload failed. Failed to upload diff-1508682a2ae8-boot-ready-diff.png with gh image (exit code 1): Error uploading /home/runner/work/_temp/e2e-evidence/diff-1508682a2ae8-boot-ready-diff.png: step 0 (get upload token): uploadToken not found on repo page — do you have write access to NousResearch/hermes-agent? (or, if NousResearch enforces SAML SSO, authorize at https://github.com/orgs/NousResearch/sso) |
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
Delegated child agents no longer clobber the parent's session identity:
set_current_session_id()now suppresses ContextVar/env writes while a delegated-child scope is active, anddelegated_child_context()saves and restores the parent id around the child run.Root cause: child
AIAgent()construction happens on the parent thread, and agent init unconditionally callsset_current_session_id(child.session_id)— which writes both the ContextVar and process-globalHERMES_SESSION_ID. Main's own comment intools/delegate_tool.pydocuments the clobber but only works around it for the async wake-sid; every parent subprocess spawned after a delegation carried the last child's identity.The guard lives at the helper level so single, batch, and nested delegation are all covered; child subprocesses still receive the correct child id via the existing scoped context bridge.
Salvaged from #74858 by @Xipong with authorship preserved (1 commit cherry-picked onto current main).
Changes
gateway/session_context.py: delegation-scope guard inset_current_session_id(); save/restore in the delegated-child scope.tools/delegate_tool.py: use the scoped context at the child construction site.tests/gateway/test_delegation_session_id_leak.py: single/nested/concurrent regression tests using realbuild_subprocess_env.Validation
Infographic