fix(tui): route background-process notifications to their owning session (#35652) - #65419
Merged
Merged
Conversation
…aned events Two complementary fixes for cross-session background-process notification leakage in the TUI/Desktop multi-session path (#42674, #35652). 1. Poller orphan guard: after _notification_event_belongs_elsewhere returns False, check whether the event has a non-empty session_key that differs from the current session. If so the owner session is gone — drop the event instead of hijacking it into an unrelated session transcript. 2. Post-turn drain filter: the existing drain_notifications() pops every event from the global queue regardless of ownership. Added _drain_owned_notifications() which applies the same ownership routing used by the poller (consume own, requeue foreign-live, drop orphan), and wired it into the post-turn safety drain. Complementary to PR #42731 which addresses a separate code path in the same bug class. Together they close #42674.
Apply positive-proof routing to every addressed notification in the registry and TUI poller while preserving ownerless legacy behavior and TUI delivery for poll-observed completions. Remove the unused exact-key drain helper and cover ordinary success and failure, origin, compression-lineage, orphan, and poll-observed paths. Complements #54785.
Adapt the strongest #63317 live-loop handoff regression and cover lineage lookup failure plus addressed live-loop orphans. Co-authored-by: Abhinav Bansal <abhibansal-sg@users.noreply.github.com>
This was referenced Jul 16, 2026
This was referenced Jul 25, 2026
kevinmanase
added a commit
to kevinmanase/hermes-agent
that referenced
this pull request
Aug 15, 2026
Background process output and completion notifications were routed by durable session_key alone. Several live UI tabs can share one key, so the gateway delivered them to whichever tab came first in iteration order -- a user could see output from a command they never ran, and the tab that actually started it saw nothing. Track the commissioning UI tab on the process itself: - ProcessSession gains origin_ui_session_id, set at spawn from HERMES_UI_SESSION_ID for every background process (not just notify/watch ones -- live streaming needs the same return address). - Completion and watch_match notifications carry it, and it survives a gateway restart through the checkpoint. - _owner_sid_for_process prefers the recorded origin when that tab is still live, and only then falls back to session_key matching. The fallback now also skips finalized tabs, which would drop the output. Processes with no recorded origin (older checkpoints, CLI sessions) keep the previous key-matching behavior. The ownership-routing and delegation-context portions of the original branch landed upstream in NousResearch#65419, so this is the remaining delta rebuilt on current main.
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
Background-process completion notifications in the TUI/Desktop multi-session path now deliver only to the session that owns them — a poller or post-turn drain in session B can no longer consume or inject a completion belonging to session A, and addressed events whose owner is gone are dropped instead of adopted (#35652).
Root cause: the completion queue is process-global, but only
async_delegationevents were ownership-filtered (#58684/#55578 scope). Plaincompletion/watch_match/watch_disabledevents were consumed by whichever session's poller woke first, anddrain_notifications()popped everything regardless of owner.Changes
tools/process_registry.py:drain_notifications()extends positive-proof / key-equality routing to every addressed notification type (events carryingsession_keyororigin_ui_session_id); ownerless legacy events keep global delivery; restored-event fail-closed guard preserved;skip_poll_observedflag so gateway/TUI read-only polling doesn't suppress autonomous delivery.tui_gateway/server.py:_notification_event_requires_owner()gate in the live poller loop and shutdown drain — foreign-live events requeue for their owner's poller, addressed orphans drop (delegation orphans still defer for a later lineage resume).Validation
Salvages #54785 by @yingliang-zhang + @2751738943 (authorship preserved; carries forward the direction of @abhibansal-sg's #63317, closed in favor of this). Fixes #35652.
Infographic