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.
Dual-Channel Architecture
Section titled “Dual-Channel Architecture”| Channel | Identifier | Storage | Purpose | Features |
|---|---|---|---|---|
| Topic | topic:u=<userID> | Persisted | State change events | Reliable, ordered, supports offline recovery |
| Live | live:u=<userID> | Non-persisted | Streaming message intermediate chunks | Low 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 Distribution
Section titled “Event Distribution”| Event Type | Topic Channel | Live Channel | Description |
|---|---|---|---|
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 |
Update Model
Section titled “Update Model”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}| Field | Type | Description |
|---|---|---|
id | UUID | Update unique identifier |
items | array | List of change entries |
items[].entity | string | Entity type: session / turn / message / rtc (file is reserved, currently unused) |
items[].action | string | Action type: created / updated (deleted is reserved, currently unused; deletion is expressed via deleted_at in data_list) |
items[].entity_id | UUID | Entity ID |
data_list | array | Complete entity data (optional, corresponds one-to-one with items) |
offset | integer | Monotonically increasing offset per user |
session.updated New Fields
Section titled “session.updated New Fields”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:
Token Statistics Fields
Section titled “Token Statistics Fields”| Field | Type | Description |
|---|---|---|
total_tokens | int64 | Cumulative total token count (all types) |
total_input_tokens | int64 | Cumulative pure input tokens (excluding cached read/write) |
total_output_tokens | int64 | Cumulative output token count |
total_cached_read_tokens | int64 | Cumulative cached read token count |
total_cached_write_tokens | int64 | Cumulative cached write token count |
total_reasoning_tokens | int64 | Cumulative reasoning token count |
total_cost_usd | float64 | Cumulative cost in USD |
last_token_update_at | time | Last token statistics update timestamp |
Token Estimation & Compression Status Fields
Section titled “Token Estimation & Compression Status Fields”| Field | Type | Description |
|---|---|---|
current_context_tokens | int64 | Current actual context token count (written back by the server’s cumulativeTokenCounter, updated to the real value after compression) |
estimated_next_round_tokens | int64 | EWMA-based prediction for next round tokens |
compression_progress | float64 | Compression progress (0-100), computed based on current_context_tokens vs compression_threshold |
compression_threshold | int64 | Compression trigger threshold (contextTokensLimit - autoCompactBufferTokens) |
rounds_until_compression | int | Rounds until compression (-1 means threshold exceeded) |
💡
current_context_tokensreplaces the oldtotal_tokensas 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 tototal_tokens.
Other New Fields
Section titled “Other New Fields”| Field | Type | Description |
|---|---|---|
device_id | string | Device ID that created this Session (from JWT Token), used by the frontend to determine RTC request ownership |
Sub-agent Hierarchy Fields
Section titled “Sub-agent Hierarchy Fields”When a Session is created by the Sub-agent system, the following fields describe its position in the Session tree:
| Field | Type | Description |
|---|---|---|
parent_client_session_id | string? | Parent Session’s Client ID |
parent_server_session_id | UUID? | Parent Session’s Server ID |
root_client_session_id | string? | Root Session’s Client ID (top of the tree) |
root_server_session_id | UUID? | Root Session’s Server ID (top of the tree) |
sub_agent_parent_message_id | UUID? | 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 viaroot_*fields. The frontend can use these to render hierarchical navigation (e.g.rtc-session-treecomponent). For top-level Sessions, these fields are allnull.
💡 Token statistics fields use a throttling mechanism (Throttle) to control push frequency, avoiding event storms. The frontend displays this data via the
rtc-token-usagecomponent. See Session Management and Context Management for details.
Offset Mechanism
Section titled “Offset Mechanism”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.
| Feature | Description |
|---|---|
| Monotonically increasing | Topic event Offsets for the same user are strictly increasing |
| Continuity detection | Client automatically backfills when an Offset gap is detected |
| Persisted | Offset is stored in IndexedDB, restored after page refresh |
| Epoch mechanism | When historical data is cleaned up, the Epoch changes and the client starts from the latest position |
| Gap placeholder | During 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.
Streaming Message Flow
Section titled “Streaming Message Flow”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.
| Phase | Channel | Behavior |
|---|---|---|
| First chunk | Topic | Create message record, push message.created, also write to Redis buffer |
| Intermediate chunks | Live | Append to Redis buffer, push to Live channel for real-time display |
| Stream ends | Topic | Read all chunks from Redis, assemble complete content, update database, push message.updated |
Offline Recovery
Section titled “Offline Recovery”After a client reconnects following a network disconnection, the system automatically checks the Offset and backfills missing events.
| Scenario | Behavior | User Perception |
|---|---|---|
| Brief disconnection | Automatically pushes messages from the offline period | Seamless; messages appear automatically |
| Extended disconnection | Detects Offset gap, fetches history | Short loading, then messages are filled in |
| History cleaned up | Epoch changes, starts from the latest position | Historical messages no longer displayed |
Reconnection Behavior
Section titled “Reconnection Behavior”| Component | Responsibility |
|---|---|
| Centrifuge SDK | Auto-reconnection, Token refresh |
| IndexedDB | Persist Offset, restore after page refresh |
| Topic Channel | Ensures offline events are not lost |
Next Steps
Section titled “Next Steps”- WebSocket RPC — Learn how to perform business operations via RPC
- Remote Tool Calling — Learn about the full RTC tool call lifecycle
- Protocol Overview — Return to the protocol panorama