SSE Events
AEGIS uses Server-Sent Events (SSE) to stream real-time updates from agent executions, workspace assessments, and conversation interactions.
Connection Setup
SSE endpoints return a stream of text/event-stream content. Connect using the standard EventSource API or any SSE client.
Browser clients authenticate by the aegis_token cookie (same-origin
EventSource cannot set an Authorization header). The conversation stream
emits named events, so listen per event name rather than using
onmessage:
const eventSource = new EventSource(
'/api/v1/conversations/{conversation_id}/stream?message=Hello'
// same-origin: the browser sends the aegis_token cookie automatically
);
for (const name of ['node_start', 'node_end', 'token', 'tool_result', 'done', 'error']) {
eventSource.addEventListener(name, (event) => {
const data = JSON.parse(event.data);
console.log(data.event, data.node, data.data);
if (name === 'done' || name === 'error') eventSource.close();
});
}
eventSource.onerror = (error) => {
console.error('SSE connection error:', error);
};# Using curl (Bearer header also accepted)
curl -N -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/v1/conversations/{conversation_id}/stream?message=Hello"SSE Endpoints
| Endpoint | Purpose |
|---|---|
GET /api/v1/conversations/{id}/stream | Stream conversation events (agent responses, tool calls) |
GET /api/v1/workspaces/{checklist_id}/stream | Stream workspace assessment events |
Resumable Conversation Streams (R39 Phase 1)
The conversation stream is rejoinable: the turn executes as a detached server-side task writing every event to a per-turn log, and the SSE endpoint is a reader of that log. A dropped connection never kills the turn.
- Event ids. Every event carries an SSE
id:(the log entry id). The browser’sEventSourcetracks the last id automatically. - Resume. On reconnect the endpoint honors the
Last-Event-IDheader (or alast_event_idquery parameter, for proxies that strip the header) and replays strictly after that event, then tails live. - Single-flight. One turn per conversation at a time. A request arriving
while a turn is active attaches as a reader — its
messageparameter is ignored (this makes retries safe). A reconnect carryingLast-Event-IDafter the turn finished replays the completed log; it never re-executes the message. messageis now optional. Required only to start a turn; a request with neither an active turn nor amessagereturns 400. A reconnect whose log has expired (default TTL 1 hour) receives a terminalerrorevent withturn_lost: true— fall back toGET /conversations/{id}/messages.- Process restarts. With
R39_CHECKPOINTER=on(Phase 3 Slice B), an in-flight turn RESUMES from its last checkpoint at startup — the resumed run appends to the same event log, so a reconnecting reader’s live view continues seamlessly. With the flag off (default), or when a thread has no checkpoint, readers get a fast terminalerrorwithturn_lost: trueinstead of hanging.
Config: TURN_EVENT_LOG_TTL_S (3600), TURN_EVENT_LOG_MAXLEN (1000),
TURN_ACTIVE_TTL_S (900), TURN_STREAM_RETRY_MS (3000),
R39_CHECKPOINTER (off).
Event Format
Each conversation SSE frame is a named event whose data is a JSON object with
event, node, and data fields (StreamEvent in orchestration
schemas.py):
event: node_end
data: {"event": "node_end", "node": "initial_llm_call", "data": {"status": "running", "tokens_used": 1892, "message": {…}}}(Workspace events below use a flatter event_type payload — the two streams
have different shapes.)
Conversation Events
Events emitted during agent conversation streaming:
| Event | Description | Key data Fields |
|---|---|---|
node_start / node_end | Pipeline node progress (system_prompt, memory, initial_llm_call, tool_node, approval, output_format) | status, tokens_used; for LLM nodes message (the assistant message incl. tool_calls); for tool_node skills_loaded |
token | Streaming text token | content (partial text) |
tool_result | Structured result of a skill code-block tool or an artifact-bearing core tool (render_chart, R43) (emitted before the tool_node’s node_end; only successful envelopes) — the chat UI renders registered block results as charts/tables/cards | tool_name (the block_key), tool_call_id, envelope ({status, message, result} — the chartable payload is envelope.result) |
error | Processing error | message, code |
done | Turn complete | execution_id (the R40a verdict-capture key — the frontend stamps it on the assistant message so the feedback affordance mounts only after completion), status, tokens_used, cost_usd, skills_injected, thinking_mode (the effective post-clamp mode, which may differ from the request), referenced_entities (entities resolved/created this turn: {entity_id, name, entity_type} — feeds the context panel), hitl_approval_id + hitl_checkpoint_type (the pause identity when status is awaiting_hitl — the client renders checkpoint-aware banner copy and routes the Review CTA from these; both null when the turn is not paused) |
Example: Conversation Stream
event: node_end
data: {"event": "node_end", "node": "system_prompt_node", "data": {"status": "running", "tokens_used": 0}}
event: node_end
data: {"event": "node_end", "node": "initial_llm_call", "data": {"status": "running", "tokens_used": 1892, "message": {"role": "assistant", "content": "", "tool_calls": [{"function": {"name": "flaring_portfolio_scan", "arguments": "{}"}}]}}}
event: tool_result
data: {"event": "tool_result", "node": "tool_node", "data": {"tool_name": "flaring_portfolio_scan", "tool_call_id": "toolu_…", "envelope": {"status": "success", "message": "Execution completed.", "result": {"authorizations": [], "total_count": 0, "truncated": false}}}}
event: node_end
data: {"event": "node_end", "node": "tool_node", "data": {"status": "running", "tokens_used": 1892}}
event: node_end
data: {"event": "node_end", "node": "initial_llm_call", "data": {"status": "running", "tokens_used": 4210, "message": {"role": "assistant", "content": "Across the portfolio…"}}}
event: done
data: {"event": "done", "node": null, "data": {"execution_id": "a1b2c3d4-…", "status": "completed", "tokens_used": 4210, "cost_usd": 0.031, "skills_injected": ["flaring_watch"], "thinking_mode": "adaptive", "referenced_entities": [], "hitl_approval_id": null, "hitl_checkpoint_type": null}}Workspace Events
Events emitted during entity compliance workspace assessments:
| Event Type | Description | Key Fields |
|---|---|---|
checklist_item_update | Checklist item status changed | item_index, status, label |
artifact_generated | Document/artifact produced | item_index, artifact_type, artifact_id |
data_table_update | Data table populated/updated | item_index, rows, columns |
form_field_update | Form field value set | item_index, field_name, value |
validation_result | Compliance check completed | rule, passed, message |
spatial_update | Map/spatial data updated | coordinates, features |
agent_status | Agent processing status | status, phase |
assessment_complete | Full assessment finished | summary, risk_level |
Example: Workspace Assessment Stream
data: {"event_type": "agent_status", "status": "assessing", "phase": "data_collection"}
data: {"event_type": "checklist_item_update", "item_index": 0, "status": "in_progress", "label": "Well Data Review"}
data: {"event_type": "data_table_update", "item_index": 0, "rows": [...], "columns": ["API", "Operator", "Distance"]}
data: {"event_type": "checklist_item_update", "item_index": 0, "status": "complete"}
data: {"event_type": "checklist_item_update", "item_index": 1, "status": "in_progress", "label": "Spacing Analysis"}
data: {"event_type": "validation_result", "rule": "Rule 37 Spacing", "passed": false, "message": "340ft < 467ft minimum"}
data: {"event_type": "artifact_generated", "item_index": 1, "artifact_type": "spacing_report", "artifact_id": "art-123"}
data: {"event_type": "checklist_item_update", "item_index": 1, "status": "complete"}
data: {"event_type": "assessment_complete", "summary": "2 of 5 checks passed", "risk_level": "high"}Reconnection
The browser’s built-in EventSource automatically reconnects on connection drops. If using a custom SSE client, implement reconnection with exponential backoff.
If the connection drops:
- Wait 1 second, then reconnect
- Double the wait on each subsequent failure (max 30 seconds)
- The server does not replay missed events — reconnection starts from the current state
Error Events
If an error occurs during streaming, the server emits an error event before closing:
data: {"event_type": "error", "message": "Budget exceeded: 150000 token limit reached", "code": "BUDGET_EXCEEDED"}Common error codes:
| Code | Description |
|---|---|
BUDGET_EXCEEDED | Agent token or cost budget exhausted |
TOOL_FAILURE | A tool call failed during execution |
APPROVAL_TIMEOUT | HITL approval request timed out |
INTERNAL_ERROR | Unexpected server-side error |