#14811: feat(gateway): route chat/agent events per-connection instead of glob…
app: web-ui
gateway
stale
size: M
…al broadcast
The existing toolEventRecipientRegistry already maps runId → connId for tool events via broadcastToConnIds(). This change extends that pattern to chat and agent events so that in multi-user deployments each WebSocket client only receives events for its own session.
Changes:
- Always register connId in toolEventRecipientRegistry (remove wantsToolEvents gate)
- Add optional runId parameter to emitChatDelta/emitChatFinal to look up registered recipients and route via broadcastToConnIds
- Extend ChatAbortOps with optional broadcastToConnIds/getRunRecipients so aborted events are also routed per-connection
- Expose getRunRecipients on GatewayRequestContext and thread it through server-maintenance → chat-abort
- All new fields are optional; every call site falls back to global broadcast() when no connId is registered, preserving full backward compatibility for single-user deployments
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This change extends the existing `ToolEventRecipientRegistry` (runId → connIds) routing pattern beyond tool events to cover `chat` deltas/finals, `agent` events, and `chat aborted` events. The gateway now registers a websocket connection id for each agent run and, when recipients exist for a run, emits `chat`/`agent` via `broadcastToConnIds` instead of global `broadcast`, while still forwarding to the node session bus.
Notable wiring includes threading `getRunRecipients` through `GatewayRequestContext` into maintenance timeouts and chat abort operations, plus new tests validating that `chat` and non-tool `agent` events are targeted when a connId is registered and fall back to global broadcast otherwise.
<h3>Confidence Score: 3/5</h3>
- This PR is directionally correct but has at least one concrete routing regression for `/stop` aborts and a likely runtime-footgun around unconditional `broadcastToConnIds` usage in chat methods.
- Core routing logic in `createAgentEventHandler` and maintenance abort wiring appears consistent and is covered by added tests, but the stop-command abort path still omits the new per-connection ops and will continue globally broadcasting aborted events. Also, `broadcastChatFinal`/`broadcastChatError` call `broadcastToConnIds` without guarding its presence, which can break callers that construct a minimal `GatewayRequestContext`.
- src/gateway/server-methods/chat.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#3517: fix: trigger agent response for webchat sessions after restart
by dovewars · 2026-01-28
80.3%
#9218: Fix Control UI chat resync on gaps and terminal events
by figitaki · 2026-02-05
78.1%
#8352: fix(gateway): include clientRunId in agent event payloads
by MarvinDontPanic · 2026-02-03
77.5%
#12240: fix: suppress heartbeat agent events from webchat broadcast
by Yida-Dev · 2026-02-09
75.6%
#5334: fix(gateway): support abort as alias for chat.abort to restore /sto...
by njbyte · 2026-01-31
75.2%
#16949: fix(gateway): deliver chat:final even when sessionKey is unresolved (…
by ekleziast · 2026-02-15
74.9%
#15564: fix: webchat messages disappear during concurrent session activity
by Automatedcapitalist · 2026-02-13
74.8%
#14309: fix(ui): resolve chat event session key mismatch
by justonlyforyou · 2026-02-11
74.6%
#5681: fix(gateway): flush text buffer before tool events in webchat
by MaudeBot · 2026-01-31
74.3%
#16125: feat(gateway): add stuck session detection
by CyberSinister · 2026-02-14
74.0%