Documentation
Streaming and events
run() waits for the full answer. UIs need progress: tokens, tools, handoffs, errors.
What it is
run() waits for the full answer. UIs need progress: tokens (or the final chunk), tool start/stop, handoffs, errors.
Shirube exposes the same timeline as:
- async iterator —
for await (const event of agent.runStream(...)) - callback —
agent.run(prompt, { onEvent })
Use streaming for chat bubbles, terminals, and live traces. The iterator completes when the run succeeds; it throws if the run fails (run.failed is still emitted first).
Event types
| Type | When |
|---|---|
run.started | Loop begins |
model.called | A provider request finished |
text.streamed | Assistant text (data.delta) |
tool.started / tool.completed | Function call lifecycle (data.name, denied if approval failed) |
handoff.started | Specialist takes over |
guardrail.triggered | A rail redacted or blocked |
memory.updated | Long-term memory write |
graph.updated | Conversation queued for graph workers |
run.completed / run.failed | Terminal |
stream.ts
for await (const event of agent.runStream("Hello", { userId: "ada" })) {
switch (event.type) {
case "text.streamed":
process.stdout.write(String(event.data?.delta ?? ""));
break;
case "tool.started":
console.log("tool", event.data?.name);
break;
case "handoff.started":
console.log("handoff", event.data?.from, "→", event.data?.to);
break;
case "run.completed":
console.log("done");
break;
}
}Each event has runId and timestamp. This is enough to drive a React log or an OpenTelemetry span per tool.