Streaming
Every token, tool call, and step is streamed as typed events over SSE. swarmlord gives you two APIs for consuming them: callbacks for simple integrations, and an async iterator for full control.
Callback API
Pass callbacks to session.send() to react to events as they arrive:
typescript
const result = await session.send("Write a haiku about distributed systems", {
onConnect: () => console.log("Connected"),
onText: delta => process.stdout.write(delta),
onReasoning: delta => process.stderr.write(delta),
onToolStart: part => console.log(`[tool] ${part.tool} started`),
onToolComplete: part => console.log(`[tool] ${part.tool} done`),
onToolError: (part, error) => console.error(`[tool] ${part.tool}: ${error}`),
onStepFinish: (tokens, cost) => console.log(`[step] ${tokens.input}in/${tokens.output}out — $${cost.toFixed(4)}`),
onAttachment: file => console.log(`[file] ${file.filename} (${file.mime})`),
onPermission: async req => "once",
onStatus: status => console.log(`[status] ${status}`),
onError: err => console.error(err),
});
// result contains the complete response after streaming finishes
console.log(result.text);
console.log(result.parts);Callback Reference
| Callback | Fires when | Argument |
|---|---|---|
onConnect | SSE stream established | — |
onText | New text token | delta: string |
onReasoning | Reasoning/thinking token | delta: string |
onToolStart | Tool call begins | part: ToolPart |
onToolComplete | Tool call finishes | part: ToolPart |
onToolError | Tool call fails | part: ToolPart, error: string |
onStepFinish | Model step completes | tokens: Tokens, cost: number |
onAttachment | Agent produces a file | file: FilePart |
onPermission | Agent needs permission | req: PermissionRequest → return "once" | "always" | "reject" |
onStatus | Session status changes | status: SessionStatus |
onError | Stream error | err: Error |
Async Iterator API
For full control over every event, use session.stream():
typescript
for await (const event of session.stream("List the files in /workspace")) {
switch (event.type) {
case "connect":
console.log("Connected");
break;
case "text-delta":
process.stdout.write(event.delta);
break;
case "reasoning-delta":
process.stderr.write(event.delta);
break;
case "tool-start":
console.log(`> ${event.part.tool}(${JSON.stringify(event.part.state.input)})`);
break;
case "tool-complete":
if (event.part.state.status === "completed") {
console.log(`< ${event.part.state.title}`);
}
break;
case "tool-error":
console.error(`Tool error: ${event.error}`);
break;
case "step-finish":
console.log(`[${event.tokens.input}in/${event.tokens.output}out]`);
break;
case "attachment":
console.log(`File: ${event.file.filename}`);
break;
case "permission-asked":
// Handle with session.replyToPermission()
break;
case "status-change":
console.log(`Status: ${event.status}`);
break;
case "error":
console.error(event.error);
break;
}
}Event Types
| Event | Fields |
|---|---|
connect | — |
text-delta | delta: string |
reasoning-delta | delta: string |
tool-start | part: ToolPart |
tool-complete | part: ToolPart |
tool-error | part: ToolPart, error: string |
step-finish | tokens: Tokens, cost: number, reason: string |
attachment | file: FilePart |
permission-asked | request: PermissionRequest |
status-change | status: SessionStatus |
session-idle | sessionId: string |
error | error: string |
message-updated | info: MessageInfo |
Handling Permissions
When the agent needs to do something that requires approval, you'll get a permission event. With callbacks:
typescript
await session.send("Install lodash and refactor utils.ts", {
onText: delta => process.stdout.write(delta),
onPermission: async req => {
console.log(`Agent wants: ${req.permission}`);
console.log(`Patterns: ${req.patterns.join(", ")}`);
return "once"; // or "always" or "reject"
},
});With the iterator, use session.replyToPermission():
typescript
for await (const event of session.stream("Install lodash")) {
if (event.type === "permission-asked") {
await session.replyToPermission(event.request.id, "once");
}
}Which API Should I Use?
| Use case | API |
|---|---|
| Simple CLI tool | Callbacks — less boilerplate |
| Chat UI with custom rendering | Iterator — full event control |
| Auto-approve permissions | Callbacks with onPermission |
| Manual permission UI | Iterator with replyToPermission |
| Log everything | Iterator — you see every event type |