Skip to content

fix(desktop): route bot-chat RPCs to the bot's own gateway via tile ownerRoute - #92956

Merged
teknium1 merged 1 commit into
mainfrom
fix/persist-session-owner-hints
Aug 23, 2026
Merged

teknium1 merged 1 commit into
mainfrom
fix/persist-session-owner-hints

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

The real fix

Chatting with any Bot Mode bot except the launch profile failed with "session not found" (4001), then hung endlessly on retry. This dispatches each bot chat's RPCs on that bot's own local gateway — the same per-profile connection pool Sessions mode already uses without issue. No new architecture: the multi-connection machinery exists; the bots path just wasn't using it.

Root cause (traced end-to-end in live logs + source)

  1. A bot chat is a persisted tile that records its exact owner (connectionId + profile) in tile.ownerRoute. requestForSessionProfile already dispatches on any (connectionId, profile) via the per-profile local gateway.
  2. But wiring's requestGateway resolved the owner via rememberedSessionProfile, a $sessions row lookup. Canonical Bot Chats are born hidden (never listed), so the lookup missed and fell back to the active profile → prompt.submit hit the launch backend that never owned the session → 4001. The withSessionNotFoundResume ladder re-resolved through the same blind spot, so it re-registered the wrong backend and hung.
  3. It also keyed off $selectedStoredSessionId, but a bot chat renders in a tile whose id is $focusedStoredSessionId (selected stays the primary pane) — so even the row path was reading the wrong session id.

Log fingerprint (user's machine): the bot backend shows ws accepted … ws closed messages=0 with no tui prompt accepted, while the launch backend logs the bot's session id running a turn that never finishes.

Fix

  • wiring.tsx requestGateway: resolve owner from the focused stored id, preferring the tile's persisted ownerRoute; fall back to the list-derived profile only when no tile route exists. One resolver — submit, resume, attach, interrupt, compress all inherit it.
  • sdk/index.ts openSession: synthesize a local ownerRoute from profile for bot opens with no explicit cross-connection route, so local bot tiles carry their owner too (previously only remote routes did). Strictly routing metadata — the dial path, all-profiles view, and route-registry retry check all still key off the explicit route, so a plain local open behaves exactly as before (no registry-secondary dial, no forced all-profiles view).

Why this fixes the current stuck chats

The tile ownerRoute is persisted and needs no list membership, so it works for chats already open across a rebuild/relaunch — not just freshly opened ones. That was the gap the prior PR (#92928, in-memory owner hints) couldn't cover.

Verification

  • tsc --noEmit: 0 errors.
  • 3 focused tests for sessionTileOwnerRoute (bot tile route returned; no-route tile → undefined; no tile → undefined).
  • CI runs the vitest UI suite (worktree here is production-deps-only per repo convention).
  • Not yet experience-verified — needs a desktop rebuild; acceptance test is messaging an idle bot without 4001/hang.

…wnerRoute

The real fix for Bot Mode 'session not found' / endless hang: dispatch
session-scoped RPCs on the OWNING profile's local gateway, using the route
the chat tile already carries — the same multi-connection machinery Sessions
mode uses, which has never had this problem.

Root cause chain:
- A bot chat is a persisted tile that records its exact owner (connectionId +
  profile) in tile.ownerRoute; requestForSessionProfile already dispatches on
  any (connectionId, profile) via the per-profile local gateway pool.
- But wiring's requestGateway resolved the owner via rememberedSessionProfile,
  a $sessions row lookup. Canonical Bot Chats are born hidden (never listed),
  so the lookup missed and fell back to the ACTIVE profile -> prompt.submit hit
  the launch backend that never owned the session -> 4001, and the resume
  ladder re-resolved through the same blind spot, so it hung.
- It also keyed off $selectedStoredSessionId, but a bot chat renders in a TILE
  whose id is $focusedStoredSessionId (selected stays the primary pane), so
  even the row path was reading the wrong session.

Fix:
- wiring requestGateway: resolve owner from the FOCUSED stored id, preferring
  the tile's persisted ownerRoute; fall back to the list-derived profile only
  when no tile route exists. One resolver, every session RPC (submit, resume,
  attach, interrupt, compress) inherits it.
- sdk openSession: synthesize a local ownerRoute from  for bot opens
  that carry no explicit cross-connection route, so LOCAL bot tiles carry their
  owner too (previously only remote routes did). Strictly routing metadata:
  the dial path, all-profiles view, and the route-registry retry check all
  still key off the EXPLICIT route, so a plain local open behaves exactly as
  before (no registry-secondary dial, no forced all-profiles view).

Fixes already-open chats (tile route is persisted, needs no fresh open) and
survives relaunch. 3 tests for sessionTileOwnerRoute. tsc 0 errors.
@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on f701aab — fix(desktop): route bot-chat RPCs to the bot's own gateway v

⚠️ Warnings

OSV vulnerability scan · View job

7 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 1m41s vs 2m40s (-36.9%). 1 job(s) slower, 5 faster,

  • OSV scan / Emit review status: -5.0s
  • Check no committed infographics / check-no-committed-infographics: -3.0s
  • OSV scan / Scan lockfiles / osv-scan: +1.0s
  • JS & TS checks / JS & TS checks: -1.0s
  • Detect affected areas: -1.0s

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 23, 2026
@teknium1
teknium1 merged commit 9452bca into main Aug 23, 2026
32 checks passed
@teknium1
teknium1 deleted the fix/persist-session-owner-hints branch August 23, 2026 13:32
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…wnerRoute (NousResearch#92956)

The real fix for Bot Mode 'session not found' / endless hang: dispatch
session-scoped RPCs on the OWNING profile's local gateway, using the route
the chat tile already carries — the same multi-connection machinery Sessions
mode uses, which has never had this problem.

Root cause chain:
- A bot chat is a persisted tile that records its exact owner (connectionId +
  profile) in tile.ownerRoute; requestForSessionProfile already dispatches on
  any (connectionId, profile) via the per-profile local gateway pool.
- But wiring's requestGateway resolved the owner via rememberedSessionProfile,
  a $sessions row lookup. Canonical Bot Chats are born hidden (never listed),
  so the lookup missed and fell back to the ACTIVE profile -> prompt.submit hit
  the launch backend that never owned the session -> 4001, and the resume
  ladder re-resolved through the same blind spot, so it hung.
- It also keyed off $selectedStoredSessionId, but a bot chat renders in a TILE
  whose id is $focusedStoredSessionId (selected stays the primary pane), so
  even the row path was reading the wrong session.

Fix:
- wiring requestGateway: resolve owner from the FOCUSED stored id, preferring
  the tile's persisted ownerRoute; fall back to the list-derived profile only
  when no tile route exists. One resolver, every session RPC (submit, resume,
  attach, interrupt, compress) inherits it.
- sdk openSession: synthesize a local ownerRoute from  for bot opens
  that carry no explicit cross-connection route, so LOCAL bot tiles carry their
  owner too (previously only remote routes did). Strictly routing metadata:
  the dial path, all-profiles view, and the route-registry retry check all
  still key off the EXPLICIT route, so a plain local open behaves exactly as
  before (no registry-secondary dial, no forced all-profiles view).

Fixes already-open chats (tile route is persisted, needs no fresh open) and
survives relaunch. 3 tests for sessionTileOwnerRoute. tsc 0 errors.

Co-authored-by: Teknium <teknium1@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists 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.

2 participants