Skip to content

HTTP streaming and SSE dual channels

Streaming does not depend on “pushing tokens in the same POST response body.” Agent Forge uses dual channels: the write channel triggers session scheduling and may wait for this turn to finish; the read channel uses a long-lived SSE connection to subscribe to bus events for text deltas, tool state, and session busy/idle.

flowchart LR
C[Client] -->|POST /session/.../message| W[Write channel SessionPrompt]
C -->|GET /event| R[Read channel SSE]
W --> Loop[runLoop / Processor]
Loop --> Bus[Bus]
Bus --> R
ChannelTypical APIRole
WritePOST .../message or .../prompt_asyncIssue work; sync waits for this turn’s result, async accepts first
ReadGET /eventtext/event-stream, continuous events
sequenceDiagram
participant C as Client
participant E as SSE
participant H as HTTP
participant L as runLoop
participant B as Bus
C->>E: Subscribe /event
E-->>C: connected / heartbeat
C->>H: POST message
H->>L: prompt
L->>B: PartDelta / status
B-->>E: Push
E-->>C: Incremental render
L-->>H: This-turn assistant
H-->>C: Sync response (if sync API)

Clients should open SSE before or alongside sending a prompt. POST only, no event subscription, usually yields a full blob after the request ends.

  • Subscribe to the bus and write events into the SSE stream
  • Periodic heartbeat to reduce idle disconnects through proxies
  • Clean up subscriptions when the connection aborts
  • There are also global-level event and sync-event channels for control plane and multi-instance sync; the session UI main path remains instance-level /event
SourceTypical content
Processortext / reasoning Part deltas
Session projectionMessage / Part update sync events
Session statebusy / idle / retry
PermissionPending user authorization

updatePartDelta may emit to the bus only; the full Part lands at moments such as text-end. So “SSE already has a delta, storage catches up later” is expected. See Message model and cross-turn memory.

APIHTTP semanticsProgress
Sync message/promptWait until this turn ends, then return the assistant messageStill use SSE for deltas
prompt_asyncAccept first (for example 204), run the same SessionPrompt in backgroundOnly SSE or read messages afterward

SDK wrapping: SDK-to-server flow.

SymptomCheck first
UI does not streamSubscribed to /event? Proxy buffering SSE?
Occasional disconnectHeartbeat arriving? Reverse-proxy timeout?
Async task “silent”Mistakenly reading the sync response body? Bus session error events?

Related reading: