Skip to content

feat(server): SSE replay buffer with Last-Event-ID support on /global/event#25658

Open
pasta-paul wants to merge 9 commits into
anomalyco:devfrom
pasta-paul:feat/sse-replay-with-last-event-id
Open

feat(server): SSE replay buffer with Last-Event-ID support on /global/event#25658
pasta-paul wants to merge 9 commits into
anomalyco:devfrom
pasta-paul:feat/sse-replay-with-last-event-id

Conversation

@pasta-paul
Copy link
Copy Markdown

@pasta-paul pasta-paul commented May 4, 2026

Issue for this PR

Closes #25657

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

/global/event ignored Last-Event-ID on 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 reads Last-Event-ID from the request and replays everything after that id before live events resume. server.connected and server.heartbeat stay 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 via lastSentId.

How did you verify your code works?

  • curl -H "Last-Event-ID: 2" against a populated ring returns events 3..N as id: lines, then continues with the live stream
  • No header / Last-Event-ID: 0 → no replay, just live
  • Long-running multi-agent task in a browser tab, throttled network in DevTools, restored — UI catches up via replay instead of going silent

Screenshots / recordings

n/a — server-side change

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

pasta-paul and others added 9 commits May 2, 2026 16:37
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.
…/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>
@pasta-paul pasta-paul requested a review from adamdotdevin as a code owner May 4, 2026 01:45
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels May 4, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: /global/event SSE stream loses events on reconnect (no Last-Event-ID support)

1 participant