feat(server): SSE replay buffer with Last-Event-ID support on /global/event#25658
Open
pasta-paul wants to merge 9 commits into
Open
feat(server): SSE replay buffer with Last-Event-ID support on /global/event#25658pasta-paul wants to merge 9 commits into
pasta-paul wants to merge 9 commits into
Conversation
Previously child sessions (subagents) were only visible in the sidebar when the user navigated into them via childSessionOnPath. This meant subagent sessions appeared invisible until clicked in the main chat. Replace childSessionOnPath (single child on nav path) with childSessions (all direct children sorted newest-first). Sidebar now renders all children immediately under their parent session.
Subagent sessions now render collapsed by default under their parent.
A chevron toggle appears on hover (Notion-style) to expand/collapse.
Only the 3 most recent children are shown initially, with a "Load more"
button for the rest. Uses the existing Collapsible component and Icon
("chevron-right" / "chevron-down") already in the UI library.
The chevron slot is always sized size-4 in the flex layout. Without children it's invisible (opacity-0), on hover it reveals (group-hover/session:opacity-100). The title never moves.
…upy same slot via absolute positioning
…/event Long-running tasks (e.g. an agent emitting hundreds of events) silently lose UI updates when the SSE stream drops — proxy idle, browser tab backgrounded, network blip. The agents keep working server-side but the client never catches up because reconnects subscribe fresh with no replay. Add a module-level ring buffer (1024 events) that: * assigns a monotonic id to every event published on GlobalBus * stores the last N events for replay * fans events out to all active SSE connections On reconnect, the client's `Last-Event-ID` header (sent automatically by EventSource, or settable via fetch) is honored — everything since that id is replayed before live events resume. Events queued during the replay window are deduped against `lastSentId` to prevent doubles. Heartbeats and the synthetic `server.connected` greeting are intentionally sent without ids: they're per-connection signals, not part of the recoverable event stream. The same pattern can be applied to /event (instance) in a follow-up; that endpoint subscribes to the per-instance Bus and would need a per-instance ring buffer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
This was referenced May 4, 2026
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.
Issue for this PR
Closes #25657
Type of change
What does this PR do?
/global/eventignoredLast-Event-IDon reconnect, so events emitted while a client was briefly disconnected (proxy idle / tab backgrounded / network blip) were lost — the server kept working but the UI went stale.This adds a 1024-entry ring buffer that assigns a monotonic id to every event published on
GlobalBus. The route readsLast-Event-IDfrom the request and replays everything after that id before live events resume.server.connectedandserver.heartbeatstay unsigned — they're per-connection signals, not part of the recoverable stream. Events that land in the live queue during the replay window are deduped vialastSentId.How did you verify your code works?
curl -H "Last-Event-ID: 2"against a populated ring returns events 3..N asid:lines, then continues with the live streamLast-Event-ID: 0→ no replay, just liveScreenshots / recordings
n/a — server-side change
Checklist