Receiving a stream
Receive session events over SSE, and resume with Last-Event-ID after a disconnect
sessions.events.stream (GET /v1/sessions/{id}/events) receives the session's events as Server-Sent Events. It first replays the events already stored from the beginning, then continues with new events as they are created. The scope is sessions:read. Use the session from Send (sessionId in the SDK, SID in the HTTP API).
Receive
The stream sends a keepalive every 15 seconds, so it does not close even when everything is normal. The example below cuts it off after 5 seconds. The SDK cuts it off with signal or stream.close(), and curl with --max-time.
import { AbortedError, type SessionEvent } from "@werft-ai/sdk";
const seen: SessionEvent[] = [];
const stream = werft.sessions.events.stream(sessionId, {
signal: AbortSignal.timeout(5_000),
});
try {
for await (const event of stream) seen.push(event);
} catch (error) {
if (!(error instanceof AbortedError)) throw error;
}
const counts = new Map<string, number>();
for (const event of seen) counts.set(event.kind, (counts.get(event.kind) ?? 0) + 1);
console.log(counts);Map(6) {
'status' => 5,
'system' => 8,
'assistant' => 2,
'result' => 2,
'tool_use' => 1,
'tool_result' => 1
}The SDK's event has id, turn_id, attempt_id, occurred_at, kind (the event in the table below), and payload (the inner data).
All events of this session's two turns (the preparation message and ls) arrived. One frame is three lines, event:, data:, and id:, plus a blank line.
event: tool_use
data: {"schema_version":1,"session_id":"…","turn_id":"2","attempt_id":"att_…","occurred_at":"…","data":{…}}
id: ev_3mdata is JSON. It identifies the session, turn, and attempt the event belongs to, includes a timestamp, and holds the event content in the nested data field.
| event | Inner data |
|---|---|
system | Start and configuration information from the engine |
assistant | A model message. The text is in text, the thinking in thinking |
tool_use | The tool the model called and its input (id, name and input of tool_call) |
tool_result | The tool's result (tool_call_id and text of tool_result). is_error: true on failure |
question | A permission request or question was raised. How to answer: Waiting for approval and resuming |
result | The engine finished the turn. This is not a completion verdict |
status | The session state changed (phase: running, idle, and so on) |
error | An execution error |
Inner fields not in this table (message, session_id and so on) differ by engine. They can go away or change shape with another engine, so do not read them.
Extract text and tool results
for (const event of seen) {
if (event.kind === "assistant")
console.log((event.payload as { text?: string }).text);
if (event.kind === "tool_result") {
const result = (event.payload as { tool_result?: { is_error?: boolean } })
.tool_result;
console.log({ is_error: result?.is_error });
}
}ready
{ is_error: false }
listedCheck whether a turn has finished with the turn's status, not with events (Send). Judge whether the work was done from the contents of assistant and tool_result.
Resume after a disconnect
Pass the last id you received to receive from the next event on. The SDK passes stream.lastEventId as the lastEventId option, and the HTTP API passes it in the Last-Event-ID header. When the connection drops, the SDK reconnects on its own with this value. Events you already received do not come again.
const more = werft.sessions.events.stream(sessionId, {
lastEventId: stream.lastEventId,
signal: AbortSignal.timeout(3_000),
});
let count = 0;
try {
for await (const _event of more) count++;
} catch (error) {
if (!(error instanceof AbortedError)) throw error;
}
console.log(count);0No new events came after that, so nothing arrived. If you sent the next message in the meantime, that turn's events would arrive.
| Response | Meaning |
|---|---|
410 CURSOR_EXPIRED | That id is outside the retention range. Re-read the session and turns over HTTP and connect anew. The server currently does not delete events and so does not send this response, but clients should handle it |
| 429 | Too many streams are open at the same time |
Next steps
- Attachments
- Request: send the commit a session made to the repository