Events are the units of information that flow through harnesses. Every harness invocation yields a sequence of events — from streaming text chunks to tool calls to usage metrics. Events form an immutable log that can be reduced into a conversation graph.All events carry two fields for graph construction:
{ type: "text", runId: string, parentId?: string, id: string, // Stable across chunks of the same text stream content: string // The text fragment}
Example:
let fullText = "";for await (const event of harness.invoke(params)) { if (event.type === "text") { fullText += event.content; process.stdout.write(event.content); }}
Emitted by: Provider harnesses (e.g., harness/providers/zen.ts:269) Passthrough: Agent harness re-yields text events with provider’s runId
A chunk of streamed reasoning or chain-of-thought content (provider-dependent).
{ type: "reasoning", runId: string, parentId?: string, id: string, // Stable across chunks of the same reasoning block content: string // The reasoning fragment}
Example:
for await (const event of harness.invoke(params)) { if (event.type === "reasoning") { console.log("[thinking]", event.content); }}
Supported by: Zen (via reasoning_content), models that expose chain-of-thought Emitted by: Provider harnesses that support reasoning (e.g., harness/providers/zen.ts:259)
for await (const event of agent.invoke(params)) { if (event.type === "tool_call") { console.log(`Calling ${event.name} with`, event.input); }}
Emitted by:
Provider harnesses yield raw tool calls after the stream ends
Agent harness re-yields approved calls with namespaced IDs (format: {agentRunId}/{rawId})
If the model emits malformed JSON for tool arguments, input will be an object with __toolParseError: true, parseError, and rawArguments fields. The agent harness will yield a tool_result with the error and continue.
Result of executing a tool.
{ type: "tool_result", runId: string, parentId?: string, id: string, // Matches the tool_call id name: string, // Tool name output: unknown // { context, result } on success // { status: "denied", reason } on denial // { error } on failure}
Example:
for await (const event of agent.invoke(params)) { if (event.type === "tool_result") { if (event.output.error) { console.error(`Tool ${event.name} failed:`, event.output.error); } else if (event.output.status === "denied") { console.log(`Tool ${event.name} was denied:`, event.output.reason); } else { console.log(`Tool ${event.name} result:`, event.output.context); } }}
Emitted by: Agent harness only (harness/agent.ts:193, harness/agent.ts:280)
Live progress updates during long-running tool execution.
{ type: "tool_progress", runId: string, parentId?: string, id: string, toolCallId: string, // The tool call this progress belongs to name: string, // Tool name content: unknown // Progress data (format depends on tool)}
Example:
for await (const event of agent.invoke(params)) { if (event.type === "tool_progress") { console.log(`[${event.name}]`, event.content); }}
These events are specific to the Recursive Language Model harness:
repl_input
repl_progress
repl_output
Code extracted from the model’s response, about to be executed.
{ type: "repl_input", runId: string, id: string, parentId?: string, code: string, // The JavaScript code to execute iteration?: number // Zero-based loop index}
Live output streamed during REPL execution.
{ type: "repl_progress", runId: string, id: string, parentId?: string, chunk: string, // The output fragment stream: "stdout" | "stderr" // Which stream this came from}
Complete result of a REPL execution.
{ type: "repl_output", runId: string, id: string, parentId?: string, stdout: string, // Full accumulated output error?: string, // Error message if execution failed done: boolean, // Whether FINAL() was called iteration?: number, // Zero-based loop index durationMs?: number, // Wall-clock execution time truncated?: boolean // True if stdout was truncated}
Events are reduced into an immutable conversation graph using runId and parentId for edges. The graph reducer in packages/ai/client/graph.ts accumulates events:
import { createGraph, reduceEvent } from "./packages/ai/client/graph";let graph = createGraph();for await (const event of orchestrator.events()) { graph = reduceEvent(graph, event); // Now graph.nodes contains all events as nodes // and graph.edges links them by runId → parentId}