fix(tui): defer buffered gateway events to stop dashboard chat #301 (#36658) - #54116
Merged
kshitijk4poor merged 1 commit intoJun 28, 2026
Conversation
…search#301 (NousResearch#36658) Dashboard /chat spawns the TUI attached to the dashboard's in-memory gateway via HERMES_TUI_GATEWAY_URL. In that attach mode the already-running gateway replays `gateway.ready` (and `session.info`) the instant the socket connects, so those events land in GatewayClient.bufferedEvents *before* the consumer's mount-time subscribe effect (useMainApp.ts) calls drain(). drain() then emitted the buffered events synchronously, so the `gateway.ready` handler's patchUiState / setHistoryItems cascade ran while React was still inside the first commit — tripping "Too many re-renders" (Minified React error NousResearch#301) and breaking Dashboard chat after `hermes update`. Spawn / inline / sidecar modes never hit this: their `gateway.ready` only arrives after the Python child boots, on a later async tick. Fix: drain() defers the replay to the next microtask AND keeps `subscribed` false until that microtask runs. Keeping `subscribed` false in the gap means any live event arriving before the flush keeps buffering (publish() pushes when !subscribed) instead of emitting synchronously and jumping ahead of the chronologically-earlier replayed events — the flush re-drains the buffer right after flipping `subscribed`, preserving FIFO order. A drainGeneration token (bumped in resetStartupState) makes a queued flush a no-op if the transport was reset/killed in the meantime, avoiding use-after-teardown and duplicate/reordered exits. Regression tests: (1) drain() does not dispatch buffered events synchronously; (2) a live event arriving in the post-drain / pre-microtask window still delivers BEHIND the earlier-buffered event (FIFO). Both are red against the old synchronous behavior, green with this fix. Same class of fix as NousResearch#44528. Closes NousResearch#36658
kshitijk4poor
enabled auto-merge
June 28, 2026 09:16
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…teway-drain-microtask fix(tui): defer buffered gateway events to stop dashboard chat NousResearch#301 (NousResearch#36658)
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
…teway-drain-microtask fix(tui): defer buffered gateway events to stop dashboard chat NousResearch#301 (NousResearch#36658)
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…teway-drain-microtask fix(tui): defer buffered gateway events to stop dashboard chat NousResearch#301 (NousResearch#36658)
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…teway-drain-microtask fix(tui): defer buffered gateway events to stop dashboard chat NousResearch#301 (NousResearch#36658)
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…teway-drain-microtask fix(tui): defer buffered gateway events to stop dashboard chat NousResearch#301 (NousResearch#36658)
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
Dashboard
/chatworks again afterhermes update— it was throwingMinified React error #301("Too many re-renders") and rendering nothing.Root cause: Dashboard
/chatspawns the TUI attached to the dashboard's in-memory gateway (HERMES_TUI_GATEWAY_URL). In attach mode the gateway is already running, so it replaysgateway.ready/session.infothe instant the socket connects — those land inGatewayClient.bufferedEventsbefore the consumer's mount-time subscribe effect (useMainApp.ts) callsdrain().drain()emitted the buffered events synchronously, so thegateway.readyhandler'spatchUiState/setHistoryItemscascade ran while React was still inside its first commit → #301. Spawn / inline / sidecar modes never hit it: theirgateway.readyarrives only after the Python child boots, on a later async tick.Changes
ui-tui/src/gatewayClient.ts—drain()defers the buffered-event replay to the next microtask and keepssubscribedfalse until that microtask runs, so a LIVE event arriving in the gap keeps buffering (publish()pushes when!subscribed) instead of jumping ahead of the chronologically-earlier replayed events; the flush re-drains right after flippingsubscribed, preserving FIFO. AdrainGenerationtoken (bumped inresetStartupState) makes a queued flush a no-op if the transport was reset/killed meanwhile.ui-tui/src/__tests__/gatewayClient.test.ts— 2 regression tests: (1)drain()does not dispatch buffered events synchronously; (2) a live event arriving in the post-drain / pre-microtask window still delivers BEHIND the earlier-buffered event (FIFO).Same class of fix as #44528 (deferred a config RPC for an embedded-dashboard #301) — the defect is event-delivery timing at the transport seam, not hook-dep identity in the component.
Validation
drain()(before)drain()(after)gateway.readyreplaynpx vitest run src/__tests__/gatewayClient.test.ts→ 13 passed. Proven RED→GREEN: revertingdrain()to the synchronous loop fails the 2 new tests, restoring passes.npx tsc --noEmit -p tsconfig.json→ clean.npx eslinton changed files → clean.Closes #36658