feat(go): real-time SSE message streaming + instant "Thinking…" indicator#480
Merged
zomux merged 1 commit intoJun 11, 2026
Merged
Conversation
Bring the OpenAgents Go clients (Swift app + packages/go/web) up to parity with the workspace/frontend reference: consume the /v1/events/stream SSE endpoint for push-based message updates, falling back to adaptive polling when the stream is unavailable. While the stream is healthy there is no steady message polling — updates arrive push-driven. Swift (packages/go/OpenAgents): - WorkspaceAPI.streamEvents(channel:): a URLSession.bytes SSE client that yields a signal per `data:` frame and throws on connect failure. - WorkspaceStore message loop is now SSE-first: each frame triggers an incremental forward poll (reusing the dedup / optimistic-reconcile / notification path in pollNewMessages rather than a second decode path), with an adaptive-polling fallback (1.5s active / 3s idle) on SSE failure. Web (packages/go/web): adds getSSEUrl() and the EventSource-first effect in use-polling.ts — now identical to the workspace/frontend reference. Thinking indicator: show an immediate "Thinking…" row the moment a message is sent to an agent channel, bridging the genuine agent-latency gap before its first status frame. Flag-based (awaitingFirstResponseSessionIds) rather than a placeholder message so it never reorders against the user's real message during reconciliation; cleared when the agent emits any output or by a 45s safety timeout. The send button also swaps to stop immediately on send. SSE delivery requires Redis pub/sub on the backend (the existing stream_events path). Without it, clients transparently fall back to polling.
|
@baryhuang is attempting to deploy a commit to the Raphael's projects Team on Vercel. A member of the Team first needs to authorize it. |
zomux
approved these changes
Jun 11, 2026
zomux
left a comment
Contributor
There was a problem hiding this comment.
Clean, well-designed SSE streaming + thinking indicator. Signal-based approach on Swift (reusing pollNewMessages) is solid, graceful polling fallback on both clients, proper lifecycle/cancellation handling. LGTM.
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
Makes the OpenAgents Go clients feel real-time: messages arrive push-driven over SSE instead of on a polling timer, and there's instant feedback the moment you send (no dead gap before the agent's first frame). Brings
packages/goto parity with theworkspace/frontendSSE reference.Changes
SSE-first message streaming (Swift app +
packages/go/web)Consume
/v1/events/streamfor push updates, fall back to adaptive polling when the stream is unavailable. While the stream is healthy there is no steady message polling.WorkspaceAPI.swift): newstreamEvents(channel:)— aURLSession.bytesSSE client that yields a signal perdata:frame and throws on connect failure.WorkspaceStore.swift): the message loop is now SSE-first — each frame triggers an incremental forward poll, reusingpollNewMessages' dedup / optimistic-reconcile / notification path rather than a second decode path. Adaptive-polling fallback (1.5s active / 3s idle) on SSE failure; reopens on session switch.packages/go/web): addsgetSSEUrl()+ theEventSource-first effect inuse-polling.ts— now byte-identical to theworkspace/frontendreference.Instant "Thinking…" indicator
The ~1–3s gap after sending is genuine agent latency (nothing has been emitted yet). Show a client-side affordance immediately:
awaitingFirstResponseSessionIds) — not a placeholder message, which would reorder against the user's real message during reconciliation.Backend dependency
SSE delivery uses the existing
stream_eventsRedis pub/sub path. Where Redis isn't configured (REDIS_URLunset),subscribe_eventsreturns immediately and clients transparently fall back to adaptive polling — so this change is safe regardless of backend Redis availability.Verification
xcodebuild OpenAgentsGo_macOS→BUILD SUCCEEDED.[sse] stream openheld open (no reconnect churn), idle message polling drops to zero, ~150ms push latency; "Thinking…" appears instantly on send and is replaced by the agent's first frame.Test plan
[sse] stream openstays open and no message polling while idle.