Real-Time Event Streaming
Runner.stream() provides real-time event streaming for interactive web applications, chat interfaces, and CLI tools using native JavaScript AsyncGenerator.
Why Stream Events?
In modern AI applications, users expect real-time feedback token-by-token rather than waiting 5+ seconds for a complete response.
Runner.stream() yields structured events for:
- Token Text Deltas: Streamed partial text chunks as they arrive from the model.
- Tool Call Lifecycle: Notifications when a tool execution starts, completes, or fails.
- Agent Handoffs: Live alerts when control shifts from a triage agent to a specialist agent.
- Run Completion: Final metrics, total tokens, and full message history.
Stream Event Types
| Event Type | Properties | Description |
|---|---|---|
'text_delta' | delta: string | Next chunk of text generated by the model. |
'tool_call_started' | toolCall: { name, args } | Tool execution initiated by the model. |
'tool_call_finished' | toolCall: { name }, result | Tool execution completed with output data. |
'handoff' | fromAgent: string, toAgent: string | Active agent transferred via handoff. |
'run_complete' | result: RunResult | Entire execution loop completed cleanly. |
Complete Streaming Code Example
import { Agent, Runner } from '@kikoyu/core';
const agent = Agent.builder()
.setName('Streaming Assistant')
.setInstructions('You write creative short stories.')
.build();
const runner = new Runner();
async function main() {
const eventStream = runner.stream(agent, 'Write a 3-paragraph story about space exploration.');
for await (const event of eventStream) {
switch (event.type) {
case 'text_delta':
process.stdout.write(event.delta);
break;
case 'tool_call_started':
console.log(`\n\n[Tool Started]: ${event.toolCall.name}`);
break;
case 'tool_call_finished':
console.log(`[Tool Finished]: ${event.toolCall.name}\n`);
break;
case 'handoff':
console.log(`\n[Agent Handoff]: ${event.fromAgent} -> ${event.toAgent}\n`);
break;
case 'run_complete':
console.log(`\n\n[Run Completed in ${event.result.messages.length} messages]`);
break;
}
}
}
main();Next.js App Router Server-Sent Events (SSE)
Integrate Runner.stream() with Next.js App Router API Route handlers:
import { supportAgent } from '@/lib/agents';
import { Runner } from '@kikoyu/core';
export async function POST(req: Request) {
const { prompt } = await req.json();
const runner = new Runner();
const encoder = new TextEncoder();
const readableStream = new ReadableStream({
async start(controller) {
for await (const event of runner.stream(supportAgent, prompt)) {
if (event.type === 'text_delta') {
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ text: event.delta })}\n\n`));
}
}
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
controller.close();
},
});
return new Response(readableStream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}Best Practices
- Handle High Delta Volume: Ensure client-side UI handlers batch or throttle DOM updates if receiving rapid token deltas.
- Always Handle
run_complete: Userun_completeto mark the stream as finished and store conversation state.