@monad/protocol, the transport client in @monad/client, and shared caches in @monad/client-rtk.
Channel map
Third-party agent diagnostics use their own raw and convenience observation streams. They are not chat-generation channels; see mesh-agents.md.
Canonical message lifecycle
Message Ingress is the only runtime writer of chat messages. It commits durable state before publishing:transcriptTargetId and producer. Durable lifecycle events carry the complete message snapshot and messageRevision; a delta carries messageId, channel, monotonic index, and text. The terminal event has the same event.id on both control and generation delivery. Clients therefore deduplicate by event ID and reconcile durable state by message ID plus revision.
Delta delivery is transient. Correctness never depends on replaying every delta because a reconnect can replace local draft state with an authoritative snapshot.
Chat client state machine
- Hold one control subscription for the client lifetime.
- When a transcript opens, load canonical message history and its durable revision.
- Fold control lifecycle events by message ID and revision without assuming fetch/event order.
- When a visible message is
pendingorstreaming, open its message-scoped generation stream. - Apply ordered deltas to that draft. Replace it on a terminal frame, regardless of whether the terminal arrives first on control or SSE.
- Dispose the generation stream when the message settles or leaves the visible set. Disposing a viewer never cancels the server run.
- For off-screen transcripts, consume lifecycle only for unread/session ordering; do not open generation streams.
- On session switch, dispose every generation subscription owned by the previous view.
Last-Event-ID and ?after=. If a cursor is no longer available, the server sends a replacement snapshot rather than silently skipping state.
Control plane
EventSocket multiplexes the WebSocket and keeps the control.subscribe subscription alive across reconnects. Control events are registry entries whose delivery is control or both. The control plane stays low-volume so a token flood cannot starve task/session lifecycle or interaction delivery.
Run activity uses:
Message-generation transport
HTTP clients use:session.messageGeneration.subscribe and session.messageGeneration.unsubscribe methods. Both transports share the same handler, authorization, snapshot/delta/terminal semantics, bounded live buffer, and disposal behavior.
Every subscription is resource-scoped: the session must exist and the message must belong to it. A slow or disconnected consumer is disposed without affecting the run or other consumers.
UI projection stream
The UI stream is a presentation projection, not the canonical message log. It emits neutralSessionUiEvent frames for messages, tool cards, approvals, context notices, tasks, and extension items. Web and TUI surfaces may use it when they need the same server-derived ordering and presentation semantics.
Canonical message state still lives in message history and Message Ingress. Restore/reset may send a replacement UI snapshot; clients must replace their projected window instead of patching multiple caches independently.
Provider-raw output never enters this stream. Third-party agent raw/convenience observation has separate authorization, event pagination, cursor, and connection-epoch semantics. See Use third-party agents and build adapters.
Shared SSE engine
Standing SSE consumers useMonadClient.stream<T> and readTypedSseStream. The engine provides:
- schema validation before delivery;
- resume with
Last-Event-IDand?after=; - equal-jitter reconnect backoff;
- heartbeat-aware idle recovery;
- terminal-frame shutdown;
- abort/disposer support;
- isolation of consumer callback errors.
SSE_IDLE_TIMEOUT_MS must remain at least twice SSE_HEARTBEAT_MS.
The inline POST stream is request-scoped and intentionally has no reconnect semantics. It drains the response for the submitted turn and stops when that request ends.
Ordering and recovery rules
- There is no ordering guarantee between WebSocket and SSE delivery.
- Durable history plus
messageRevisionis authoritative. - The same terminal event ID on both planes makes either arrival order safe.
- A lost control notification is repaired by history/session refetch after reconnect.
- A lost delta is repaired by message snapshot replacement.
- Bounded caches and buffers must evict old replay keys together with their retained state.
- Generation subscriptions are view resources, not run ownership handles.
Conformance checklist
- Is low-frequency lifecycle routed through registry delivery to control?
- Are token/reasoning deltas scoped to one message-generation subscription?
- Does the message stream authorize both session and message ownership?
- Does reconnect resume or return an authoritative replacement snapshot?
- Is the terminal
Eventidentical on control and generation delivery? - Does the client deduplicate by event ID and reconcile by message revision?
- Are settled/off-screen/session-switched subscriptions disposed?
- Are all growing replay buffers, indexes, and caches bounded?
- Are provider-raw frames confined to observation APIs?
- Does every standing SSE endpoint send heartbeats and use the shared decoder?