Skip to content

Real-Time Events

RTC Agent pushes real-time events via Centrifuge WebSocket, using a dual-channel architecture: the Topic channel ensures reliability, while the Live channel provides low latency. The frontend updates local state upon receiving events, achieving real-time frontend-backend synchronization.

ChannelIdentifierStoragePurposeFeatures
Topictopic:u=<userID>PersistedState change eventsReliable, ordered, supports offline recovery
Livelive:u=<userID>Non-persistedStreaming message intermediate chunksLow latency, fire-and-forget

💡 Why two channels? State changes (e.g., message creation, Turn completion) must not be lost — they need persistence and ordering guarantees. But streaming intermediate chunks only need to “arrive as fast as possible” — it’s fine if one is lost, because the complete message will eventually be received. Handling them separately leverages the strengths of each.


Event TypeTopic ChannelLive ChannelDescription
session.created✅❌Session created
session.updated✅❌Session updated (title, status, token statistics, etc.)
turn.created✅❌Turn created
turn.updated✅❌Turn state changed
message.created✅❌Message created
message.updated (stream complete)✅❌Final complete version of a streamed message
message.updated (stream intermediate chunk)❌✅Real-time fragments for the typewriter effect
rtc.created❌❌Reserved (not currently emitted)
rtc.updated✅❌RTC state changed

Each event is an Update — describing changes to an entity (Session / Turn / Message / RTC):

{
"id": "update-uuid",
"items": [
{ "entity": "message", "action": "created", "entity_id": "msg-uuid" }
],
"data_list": [{ "id": "msg-uuid", "role": "assistant", "..." : "..." }],
"offset": 42
}
FieldTypeDescription
idUUIDUpdate unique identifier
itemsarrayList of change entries
items[].entitystringEntity type: session / turn / message / rtc (file is reserved, currently unused)
items[].actionstringAction type: created / updated (deleted is reserved, currently unused; deletion is expressed via deleted_at in data_list)
items[].entity_idUUIDEntity ID
data_listarrayComplete entity data (optional, corresponds one-to-one with items)
offsetintegerMonotonically increasing offset per user

The session.updated event includes token statistics and compression status fields in the Session data, used by the frontend to display real-time usage and compression progress:

FieldTypeDescription
total_tokensint64Cumulative total token count (all types)
total_input_tokensint64Cumulative pure input tokens (excluding cached read/write)
total_output_tokensint64Cumulative output token count
total_cached_read_tokensint64Cumulative cached read token count
total_cached_write_tokensint64Cumulative cached write token count
total_reasoning_tokensint64Cumulative reasoning token count
total_cost_usdfloat64Cumulative cost in USD
last_token_update_attimeLast token statistics update timestamp

Token Estimation & Compression Status Fields

Section titled “Token Estimation & Compression Status Fields”
FieldTypeDescription
current_context_tokensint64Current actual context token count (written back by the server’s cumulativeTokenCounter, updated to the real value after compression)
estimated_next_round_tokensint64EWMA-based prediction for next round tokens
compression_progressfloat64Compression progress (0-100), computed based on current_context_tokens vs compression_threshold
compression_thresholdint64Compression trigger threshold (contextTokensLimit - autoCompactBufferTokens)
rounds_until_compressionintRounds until compression (-1 means threshold exceeded)

💡 current_context_tokens replaces the old total_tokens as the baseline for compression progress display. After compression completes, this value is written back to the actual context size, and the frontend progress bar reflects the real state immediately. For older Sessions that haven’t initialized this field, the frontend falls back to total_tokens.

FieldTypeDescription
device_idstringDevice ID that created this Session (from JWT Token), used by the frontend to determine RTC request ownership

When a Session is created by the Sub-agent system, the following fields describe its position in the Session tree:

FieldTypeDescription
parent_client_session_idstring?Parent Session’s Client ID
parent_server_session_idUUID?Parent Session’s Server ID
root_client_session_idstring?Root Session’s Client ID (top of the tree)
root_server_session_idUUID?Root Session’s Server ID (top of the tree)
sub_agent_parent_message_idUUID?Message ID in the parent Session that triggered the Sub-agent

💡 Session Tree: Sessions created by Sub-agents reference their parent Session via parent_* fields and the root Session of the tree via root_* fields. The frontend can use these to render hierarchical navigation (e.g. rtc-session-tree component). For top-level Sessions, these fields are all null.

💡 Token statistics fields use a throttling mechanism (Throttle) to control push frequency, avoiding event storms. The frontend displays this data via the rtc-token-usage component. See Session Management and Context Management for details.


Offset is the core guarantee for event reliability — each Topic event is assigned a strictly increasing Offset, and the client detects lost events by checking Offset continuity.

FeatureDescription
Monotonically increasingTopic event Offsets for the same user are strictly increasing
Continuity detectionClient automatically backfills when an Offset gap is detected
PersistedOffset is stored in IndexedDB, restored after page refresh
Epoch mechanismWhen historical data is cleaned up, the Epoch changes and the client starts from the latest position
Gap placeholderDuring offline recovery, the server sends {"type": "gap", "data": {}} events to fill Offset gaps; the client only advances the Offset without any business processing

💡 Analogy: Offset is like the numbering on letters. If you receive letters #1, #2, and #4, you realize #3 is missing — then you go to the post office to claim it.


AI replies use streaming output. Intermediate chunks are pushed in real-time via the Live channel, and the final complete message is delivered via the Topic channel to ensure reliability.

PhaseChannelBehavior
First chunkTopicCreate message record, push message.created, also write to Redis buffer
Intermediate chunksLiveAppend to Redis buffer, push to Live channel for real-time display
Stream endsTopicRead all chunks from Redis, assemble complete content, update database, push message.updated

After a client reconnects following a network disconnection, the system automatically checks the Offset and backfills missing events.

ScenarioBehaviorUser Perception
Brief disconnectionAutomatically pushes messages from the offline periodSeamless; messages appear automatically
Extended disconnectionDetects Offset gap, fetches historyShort loading, then messages are filled in
History cleaned upEpoch changes, starts from the latest positionHistorical messages no longer displayed
ComponentResponsibility
Centrifuge SDKAuto-reconnection, Token refresh
IndexedDBPersist Offset, restore after page refresh
Topic ChannelEnsures offline events are not lost