Skip to content

fix(gateway): scope fallback session lookup to profile in multiplex mode (#74285) - #75043

Open
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/74285-multiplex-session-profile
Open

webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/74285-multiplex-session-profile

Conversation

@webtecnica

Copy link
Copy Markdown

Fixes #74285 — fallback session query now includes profile_name in WHERE clause. Primary query was already scoped (session_key embeds profile), but the fallback returned sibling profile's session.

…query

Root cause: `find_latest_gateway_session_for_peer()` in hermes_state.py
has a fallback SQL query (peer-tuple match) that did not include
profile_name in the WHERE clause. In a multiplexed gateway, this
allowed the fallback to return a session row from a sibling profile,
causing DM routing to the wrong profile.

Fix:
- Add `profile_name` keyword-only parameter to
  find_latest_gateway_session_for_peer()
- Add AND COALESCE(profile_name, '') = COALESCE(?, '') to the
  fallback SQL WHERE clause
- Pass profile_name=source.profile from the sole caller
  SessionStore._find_gateway_session_row() in gateway/session.py

The primary session_key-based query was already correctly scoped
because build_session_key() includes the profile in the key.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for narrowing this to the durable recovery fallback. The premise is confirmed on current main: hermes_state.py:3052-3072 falls back on the peer tuple without profile_name, and gateway/session.py:1547-1555 deliberately permits recovered rows in multiplex mode.

Problems

  • The PR has no regression coverage. Current tests/test_hermes_state.py:2437-2461 covers only a single-profile recovery row, not two same-peer rows differentiated by profile_name. This is important because gateway/session.py:2425 persists that value as the durable isolation key.

Suggested changes

  • Add a real-SessionDB test with identical peer tuples across two profiles and assert fallback recovery returns only the requested profile. Also cover the SessionStore plumbing at gateway/session.py:1689-1696, so a future caller omission cannot reintroduce the issue.

This is an automated hermes-sweeper review.

Comment thread gateway/session.py
chat_id=source.chat_id if allow_peer_fallback else None,
chat_type=source.chat_type if allow_peer_fallback else None,
thread_id=source.thread_id,
profile_name=source.profile,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a regression test for this plumbing and the SQL predicate: create two recoverable rows with the same peer tuple but different profile_name values, then prove recovery for this source selects only its profile's row.

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery area/sessions Session lifecycle, resume, persistence, history area/profiles Multi-profile isolation, HERMES_HOME scoping P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jul 30, 2026
@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 30, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Three PRs address #74285's cross-profile session-recovery defect: #74153 combines profile filtering with a guard and routing cleanup, #74593 scopes candidate selection by exact session-key namespace and rejects ambiguous multiplex ownership, and #75043 provides the minimal profile_name-based SQL fix.

Related pull requests

Duplicates

#74153, #74593, and #75043 target the same peer-fallback isolation defect; #74153 and #75043 are substantially subsumed by #74593.

Suggested consolidation

Keep #74593 open with a salvage path: independently verify its exact-prefix filtering before LIMIT 1, fail-closed handling of ownership-ambiguous multiplex rows, and real-SessionDB regression coverage. Then close #74153 and #75043 as duplicates of #74593; this explicitly departs from their keep_open reviews because #74153 retains the documented default-profile/test defects, while #75043's valid best-fix SQL change is fully represented in #74593 together with the regression coverage its review requires.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I74285(["issue #74285 (open)"])
    subgraph Dup74153 ["PRs duplicating each other"]
        P74153["PR #74153 (open)"]
        P74593["PR #74593 (open)"]
        P75043["PR #75043 (open)"]
    end
    P75043 -->|best fix| I74285
    class I74285 open
    class P74153 open
    class P74593 open
    class P75043 open
    class P74593 best
    class P75043 best
    class P75043 target
    click I74285 "https://github.com/NousResearch/hermes-agent/issues/74285"
    click P74153 "https://github.com/NousResearch/hermes-agent/pull/74153"
    click P74593 "https://github.com/NousResearch/hermes-agent/pull/74593"
    click P75043 "https://github.com/NousResearch/hermes-agent/pull/75043"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 3 pull requests and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 36 kB of PR diffs, 17 kB of issue/PR text, 6 kB of discussion (10 comments), 6 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@teknium1

Copy link
Copy Markdown
Collaborator

The peer-fallback fence this PR proposed is now structural: PR-1/PR-3 (#115665 ad651b8250, #115802 8df0a03793) pin one RoutingIdentity per event before any lookup, PR-5 (#115847 d899d890f8) persists the receiving bot on the row, and hermes sessions repair-profiles (#115689) repairs lanes that already crossed. If a repro of #74285 survives on current main, reopen with it; otherwise this can close.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping area/sessions Session lifecycle, resume, persistence, history comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Multiplexed gateway routes a user's DMs to a sibling profile's session (fallback lookup omits profile_name)

4 participants