AgentEvent Union Type and Event Streaming
Specification v1.0 | @a5c-ai/agent-mux
Note: hermes-agent is included as a 10th supported agent per project requirements, extending the original scope's 9 agents. All ten built-in agents (claude, codex, gemini, copilot, cursor, opencode, pi, omp, openclaw, hermes) share the same event contract.
Last refreshed: 2026-04-25
1. Overview
This specification defines the complete AgentEvent union type -- 67 event types across 18 categories -- the streaming model that controls how events are emitted, event ordering guarantees, correlation keys for grouping related events, and the per-agent event support matrix.
Every agent run produces a stream of AgentEvent values. Regardless of the underlying agent's output format (JSON streaming, line-buffered text, PTY raw output, structured API responses), each adapter parses native output into the normalized event types defined here. Consumers never see agent-specific output formats.
1.1 Cross-References
| Type / Concept | Spec | Section |
|---|---|---|
AgentName, BuiltInAgentName | 01-core-types-and-client.md | 1.4 |
AgentMuxClient, createClient() | 01-core-types-and-client.md | 5 |
RunOptions, stream option | 02-run-options-and-profiles.md | 2, 5 |
RunHandle, AsyncIterable<AgentEvent> | 03-run-handle-and-interaction.md | 2 |
RunResult | 03-run-handle-and-interaction.md | 3 |
CostRecord | 01-core-types-and-client.md | 4.2.3 |
AgentCapabilities | 06-capabilities-and-models.md | 2 |
ErrorCode, AgentMuxError | 01-core-types-and-client.md | 3.1 |
InteractionChannel | 03-run-handle-and-interaction.md | 5 |
1.2 Design Principles
- Uniform shape. Every event extends
BaseEvent. Consumers can always accesstype,runId,agent, andtimestampwithout type narrowing. - Additive-only. New event types may be added in minor versions; existing types are never removed or changed in breaking ways.
- Same types in all modes. Whether
streamis'auto',true, orfalse, the consumer always receives the same set of event types. The mode only controls granularity and timing. - Correlation by key. Related events share a correlation key (
toolCallId,subagentId,interactionId,pluginId) so consumers can group them without position-based logic. - No agent-specific events. The union is closed for agent-specific data. The
rawfield onBaseEventand thedebugevent provide escape hatches for agent-specific diagnostics.
2. BaseEvent Interface
/**
* Shared fields present on every AgentEvent.
*
* All 67 event types extend this interface. Consumers can read these
* fields without narrowing to a specific event type.
*/
interface BaseEvent {
/**
* Discriminant field. Uniquely identifies the event type within the
* AgentEvent union. Always a lowercase, underscore-separated string
* literal (e.g., 'text_delta', 'tool_call_start').
*/
type: string;
/**
* The ULID of the run that produced this event. Matches
* `RunHandle.runId`. Format: 26-character Crockford Base32 string.
*
* @see RunHandle.runId in 03-run-handle-and-interaction.md, Section 2.
*/
runId: string;
/**
* The agent that produced this event.
*
* @see AgentName in 01-core-types-and-client.md, Section 1.4.
*/
agent: AgentName;
/**
* Unix epoch timestamp in milliseconds when this event was created
* by the adapter. Uses `Date.now()` at event construction time.
*
* Events are emitted in causal order; timestamps are monotonically
* non-decreasing within a single run.
*/
timestamp: number;
/**
* The raw, unparsed output from the agent subprocess that produced
* this event. Present only when the client was created with
* `ClientOptions.debug: true` (see `01-core-types-and-client.md` §5.1.1).
*
* Format is agent-specific (JSON line, PTY escape sequence, etc.).
* Intended for debugging adapter implementations, not for
* production consumption.
*/
raw?: string;
}
2.1 BaseEvent Field Constraints
| Field | Type | Required | Constraints |
|---|---|---|---|
type | string (literal) | Yes | One of the 67 defined event type literals. |
runId | string | Yes | ULID format, 26 characters, Crockford Base32. |
agent | AgentName | Yes | One of the 10 built-in agents or a registered plugin adapter name. |
timestamp | number | Yes | Positive integer, Unix epoch milliseconds, monotonically non-decreasing within a run. |
raw | string | No | Present only in debug mode. Arbitrary string from the agent subprocess. |
3. CostRecord Type
Used by session_end, turn_end, subagent_result, cost, and RunResult. Defined canonically in 01-core-types-and-client.md Section 4.2.3; reproduced here for reference.
/**
* Aggregated cost and token usage for a run, turn, or subagent invocation.
*
* All token counts are integers. Cost is a floating-point estimate in USD.
* Fields are optional when the agent does not report the corresponding data.
*
* @see 01-core-types-and-client.md, Section 4.2.3.
*/
interface CostRecord {
/** Total estimated cost in US dollars. */
totalUsd: number;
/** Input tokens consumed. */
inputTokens: number;
/** Output tokens generated. */
outputTokens: number;
/** Thinking/reasoning tokens consumed (if the model supports thinking). */
thinkingTokens?: number;
/** Cached input tokens reused from a previous turn or session (if applicable). */
cachedTokens?: number;
}
3.1 CostRecord Field Constraints
| Field | Type | Required | Constraints |
|---|---|---|---|
totalUsd | number | Yes | Non-negative. 0 when cost data is unavailable. |
inputTokens | number | Yes | Non-negative integer. |
outputTokens | number | Yes | Non-negative integer. |
thinkingTokens | number | No | Non-negative integer. Omitted when thinking was not used. |
cachedTokens | number | No | Non-negative integer. Omitted when caching is unsupported. |
4. AgentEvent Union Type
The AgentEvent type is a discriminated union of 67 event interfaces grouped into 18 categories. The type field is the discriminant.
type AgentEvent =
// Session lifecycle (5)
| SessionStartEvent
| SessionResumeEvent
| SessionForkEvent
| SessionCheckpointEvent
| SessionEndEvent
// Turn / step lifecycle (4)
| TurnStartEvent
| TurnEndEvent
| StepStartEvent
| StepEndEvent
// Text / message streaming (3)
| MessageStartEvent
| TextDeltaEvent
| MessageStopEvent
// Thinking / reasoning (3)
| ThinkingStartEvent
| ThinkingDeltaEvent
| ThinkingStopEvent
// Tool calling (5)
| ToolCallStartEvent
| ToolInputDeltaEvent
| ToolCallReadyEvent
| ToolResultEvent
| ToolErrorEvent
// File operations (5)
| FileReadEvent
| FileWriteEvent
| FileCreateEvent
| FileDeleteEvent
| FilePatchEvent
// Shell operations (4)
| ShellStartEvent
| ShellStdoutDeltaEvent
| ShellStderrDeltaEvent
| ShellExitEvent
// MCP tool calling (3)
| McpToolCallStartEvent
| McpToolResultEvent
| McpToolErrorEvent
// Subagent dispatch (3)
| SubagentSpawnEvent
| SubagentResultEvent
| SubagentErrorEvent
// Plugin events (3)
| PluginLoadedEvent
| PluginInvokedEvent
| PluginErrorEvent
// Skill / agent doc loading (3)
| SkillLoadedEvent
| SkillInvokedEvent
| AgentdocReadEvent
// Multimodal (2)
| ImageOutputEvent
| ImageInputAckEvent
// Cost and tokens (2)
| CostEvent
| TokenUsageEvent
// Interaction / waiting (4)
| InputRequiredEvent
| ApprovalRequestEvent
| ApprovalGrantedEvent
| ApprovalDeniedEvent
// Rate / context limits (4)
| RateLimitedEvent
| ContextLimitWarningEvent
| ContextCompactedEvent
| RetryEvent
// Run lifecycle / control (7)
| InterruptedEvent
| AbortedEvent
| PausedEvent
| ResumedEvent
| TimeoutEvent
| TurnLimitEvent
| StreamFallbackEvent
// Errors (5)
| AuthErrorEvent
| RateLimitErrorEvent
| ContextExceededEvent
| CrashEvent
| ErrorEvent
// Debug (2)
| DebugEvent
| LogEvent;
5. Session Lifecycle Events (5 events)
Session lifecycle events mark the creation, resumption, forking, checkpointing, and completion of an agent session. A session maps to the agent's native persistent conversation record.
5.1 SessionStartEvent
Emitted once at the beginning of every run. Always the first domain event (after any debug or log events from subprocess startup).
interface SessionStartEvent extends BaseEvent {
type: 'session_start';
/**
* The agent's native session identifier. Format is agent-dependent:
* - Claude Code: UUID v4
* - Codex CLI: session directory name
* - Gemini CLI: conversation ID
* - OpenCode: session file stem
* - hermes: session UUID
*
* When `RunOptions.noSession` is true, this is a synthetic transient
* ID generated by the adapter, prefixed with 'transient-'.
*/
sessionId: string;
/**
* Whether this session is a continuation of a prior conversation.
* True when `RunOptions.sessionId` was provided and the session
* already existed. False for new sessions.
*/
resumed: boolean;
/**
* When this session was created by forking another, the source
* session's ID. Only present when `RunOptions.forkSessionId` was
* set and the agent supports forking.
*
* @capability Requires `AgentCapabilities.canFork: true`.
*/
forkedFrom?: string;
}
5.2 SessionResumeEvent
Emitted when an existing session is resumed. Provides context about the prior conversation state. Only emitted when resumed on SessionStartEvent is true.
interface SessionResumeEvent extends BaseEvent {
type: 'session_resume';
/** The session being resumed. Matches `SessionStartEvent.sessionId`. */
sessionId: string;
/**
* Number of turns in the session before this run's first turn.
* Useful for UIs that display turn numbering.
*/
priorTurnCount: number;
}
5.3 SessionForkEvent
Emitted when a session is created by forking an existing one. The new session inherits the conversation history of the source up to the fork point.
interface SessionForkEvent extends BaseEvent {
type: 'session_fork';
/** The newly created session ID. */
sessionId: string;
/** The source session that was forked. */
forkedFrom: string;
}
5.4 SessionCheckpointEvent
Emitted when the agent creates a checkpoint within a session. Checkpoints are agent-specific save points that can be restored later.
interface SessionCheckpointEvent extends BaseEvent {
type: 'session_checkpoint';
/** The session that was checkpointed. */
sessionId: string;
/**
* Agent-specific checkpoint identifier. Can be passed to future
* runs to restore state to this point.
*/
checkpointId: string;
}
5.5 SessionEndEvent
Emitted when the session completes. Always the last session lifecycle event. Contains optional aggregated cost data for the entire session.
interface SessionEndEvent extends BaseEvent {
type: 'session_end';
/** The session that ended. */
sessionId: string;
/** Total number of turns completed in this session (including prior turns if resumed). */
turnCount: number;
/**
* Aggregated cost for all turns in this run (not including prior
* sessions if resumed). May be absent if the agent does not
* report cost data.
*
* @see CostRecord in Section 3.
*/
cost?: CostRecord;
}
5.6 Session Event Ordering
session_start
-> session_resume? (only if resumed)
-> session_fork? (only if forked)
-> [turn events...]
-> session_checkpoint* (zero or more, at any point during turns)
-> session_end
session_start is guaranteed before any turn, tool, text, or thinking events. session_end is guaranteed after all turn events have completed.
6. Turn / Step Lifecycle Events (4 events)
Turns represent complete request-response cycles with the agent. Steps are sub-units within a turn (e.g., thinking, generating text, calling a tool). Not all agents expose step-level granularity; adapters that lack step data emit turns without steps.
6.1 TurnStartEvent
Emitted at the beginning of each turn. Turn indices are zero-based within the current run.
interface TurnStartEvent extends BaseEvent {
type: 'turn_start';
/**
* Zero-based turn index within this run. The first turn is 0,
* the second is 1, etc. Does not account for prior turns in
* a resumed session.
*/
turnIndex: number;
}
6.2 TurnEndEvent
Emitted when a turn completes. Contains optional per-turn cost data.
interface TurnEndEvent extends BaseEvent {
type: 'turn_end';
/** Zero-based turn index. Matches the corresponding `TurnStartEvent.turnIndex`. */
turnIndex: number;
/**
* Cost for this specific turn. Absent if the agent does not
* provide per-turn cost breakdown.
*
* @see CostRecord in Section 3.
*/
cost?: CostRecord;
}
6.3 StepStartEvent
Emitted at the beginning of a step within a turn. Steps represent distinct phases of agent processing.
interface StepStartEvent extends BaseEvent {
type: 'step_start';
/** The turn this step belongs to. */
turnIndex: number;
/** Zero-based step index within the turn. */
stepIndex: number;
/**
* The kind of step. Common values:
* - 'thinking' -- reasoning/planning phase
* - 'generation' -- text generation phase
* - 'tool_use' -- tool execution phase
* - 'review' -- agent reviewing its own output
*
* Agent-specific step types may appear; consumers should handle
* unknown values gracefully.
*/
stepType: string;
}
6.4 StepEndEvent
Emitted when a step within a turn completes.
interface StepEndEvent extends BaseEvent {
type: 'step_end';
/** The turn this step belongs to. */
turnIndex: number;
/** Zero-based step index. Matches the corresponding `StepStartEvent.stepIndex`. */
stepIndex: number;
}
6.5 Turn/Step Event Ordering
turn_start(turnIndex=0)
-> step_start(turnIndex=0, stepIndex=0)?
-> [thinking/text/tool events...]
-> step_end(turnIndex=0, stepIndex=0)?
-> step_start(turnIndex=0, stepIndex=1)?
-> [more events...]
-> step_end(turnIndex=0, stepIndex=1)?
-> turn_end(turnIndex=0)
turn_start(turnIndex=1)
-> ...
-> turn_end(turnIndex=1)
Steps are optional. A turn always has exactly one turn_start followed by exactly one turn_end. Step events, when present, are always properly nested within their enclosing turn and never overlap.
7. Text / Message Streaming Events (3 events)
Text streaming events deliver the agent's textual response. In streaming mode, text_delta events arrive incrementally. In buffered mode, a single text_delta with the full text arrives before message_stop.
7.1 MessageStartEvent
Emitted when the agent begins generating a textual response. Precedes the first text_delta in the current generation.
interface MessageStartEvent extends BaseEvent {
type: 'message_start';
}
7.2 TextDeltaEvent
Emitted for each chunk of generated text. In streaming mode, delta contains a small fragment. In buffered mode (stream: false), a single text_delta is emitted with delta equal to the complete text.
interface TextDeltaEvent extends BaseEvent {
type: 'text_delta';
/**
* The new text fragment. In streaming mode, typically 1-50 characters.
* In buffered mode, the complete response text.
*/
delta: string;
/**
* All text generated so far in this message, including this delta.
* Consumers can use this instead of manually concatenating deltas.
*/
accumulated: string;
}
7.3 MessageStopEvent
Emitted when the agent finishes generating a textual response. The text field contains the complete generated text.
interface MessageStopEvent extends BaseEvent {
type: 'message_stop';
/** The complete generated text for this message. */
text: string;
}
7.4 Text Streaming Event Ordering
message_start
-> text_delta+ (one or more)
-> message_stop
message_start always precedes any text_delta events. message_stop always follows the last text_delta and contains the same text as the final text_delta.accumulated. Multiple message sequences can occur within a single turn (e.g., when the agent generates text, calls a tool, then generates more text).
8. Thinking / Reasoning Events (3 events)
Thinking events expose the agent's internal reasoning process. Only emitted when the agent and model support thinking and the user has enabled it via RunOptions.thinkingEffort or the model defaults to thinking.
8.1 ThinkingStartEvent
Emitted when the agent begins a thinking/reasoning phase.
interface ThinkingStartEvent extends BaseEvent {
type: 'thinking_start';
/**
* The effective thinking effort level for this invocation.
* Absent if the agent does not report effort level.
*/
effort?: string;
}
8.2 ThinkingDeltaEvent
Emitted for each chunk of thinking content. Follows the same streaming/buffered pattern as text_delta.
interface ThinkingDeltaEvent extends BaseEvent {
type: 'thinking_delta';
/** The new thinking text fragment. */
delta: string;
/** All thinking text generated so far, including this delta. */
accumulated: string;
}
8.3 ThinkingStopEvent
Emitted when the thinking phase completes.
interface ThinkingStopEvent extends BaseEvent {
type: 'thinking_stop';
/** The complete thinking/reasoning text. */
thinking: string;
}
8.4 Thinking Event Ordering
thinking_start
-> thinking_delta+ (one or more)
-> thinking_stop
Thinking events always precede the corresponding text generation events within the same step. When thinking is used, the sequence within a turn is:
thinking_start -> thinking_delta+ -> thinking_stop -> message_start -> text_delta+ -> message_stop
9. Tool Calling Events (5 events)
Tool calling events track the lifecycle of native tool invocations (as opposed to MCP tools, which have their own event group). Events are correlated by toolCallId.
9.1 Correlation Key: toolCallId
All tool calling events for a single invocation share the same toolCallId. This is a unique identifier generated by the adapter, typically derived from the agent's native tool call ID. Format is adapter-dependent but guaranteed unique within a run.
9.2 ToolCallStartEvent
Emitted when the agent begins constructing a tool call. In streaming mode, the input may still be accumulating.
interface ToolCallStartEvent extends BaseEvent {
type: 'tool_call_start';
/** Unique identifier for this tool call. Correlates all events for this invocation. */
toolCallId: string;
/** The name of the tool being called. */
toolName: string;
/**
* The tool input accumulated so far. In streaming mode, this may be
* a partial JSON string. In buffered mode, this is the complete input.
*/
inputAccumulated: string;
}
9.3 ToolInputDeltaEvent
Emitted for each chunk of tool input as it streams in. Only emitted in streaming mode when the agent supports tool call streaming.
interface ToolInputDeltaEvent extends BaseEvent {
type: 'tool_input_delta';
/** Correlates to the `ToolCallStartEvent` for this tool call. */
toolCallId: string;
/** The new input fragment. */
delta: string;
/** All input accumulated so far, including this delta. */
inputAccumulated: string;
}
9.4 ToolCallReadyEvent
Emitted when the tool input is fully assembled and the tool is ready to execute (or is executing). The input field contains the parsed, complete input.
interface ToolCallReadyEvent extends BaseEvent {
type: 'tool_call_ready';
/** Correlates to the `ToolCallStartEvent` for this tool call. */
toolCallId: string;
/** The name of the tool being called. */
toolName: string;
/**
* The complete, parsed tool input. Type depends on the tool's schema;
* typically a plain object but may be any JSON-serializable value.
*/
input: unknown;
}
9.5 ToolResultEvent
Emitted when a tool call completes successfully.
interface ToolResultEvent extends BaseEvent {
type: 'tool_result';
/** Correlates to the `ToolCallStartEvent` for this tool call. */
toolCallId: string;
/** The name of the tool that was called. */
toolName: string;
/**
* The tool's output. Type depends on the tool; typically a string
* or plain object. May be any JSON-serializable value.
*/
output: unknown;
/** Wall-clock duration of the tool execution in milliseconds. */
durationMs: number;
}
9.6 ToolErrorEvent
Emitted when a tool call fails.
interface ToolErrorEvent extends BaseEvent {
type: 'tool_error';
/** Correlates to the `ToolCallStartEvent` for this tool call. */
toolCallId: string;
/** The name of the tool that failed. */
toolName: string;
/** Human-readable error message. */
error: string;
}
9.7 Tool Event Ordering
tool_call_start(toolCallId='tc-1')
-> tool_input_delta(toolCallId='tc-1')* (zero or more, streaming only)
-> tool_call_ready(toolCallId='tc-1')
-> tool_result(toolCallId='tc-1') (success path)
OR tool_error(toolCallId='tc-1') (failure path)
A tool call always begins with tool_call_start and terminates with exactly one of tool_result or tool_error. tool_call_ready is guaranteed between the last tool_input_delta and the result/error. Parallel tool calls may interleave events from different toolCallId values, but events for a single toolCallId are always in order.
10. File Operations Events (5 events)
File operation events are emitted when the agent reads, writes, creates, deletes, or patches files. These are high-level semantic events; the underlying tool call events are emitted separately.
interface FileReadEvent extends BaseEvent {
type: 'file_read';
/** Absolute or workspace-relative path of the file that was read. */
path: string;
}
interface FileWriteEvent extends BaseEvent {
type: 'file_write';
/** Absolute or workspace-relative path of the file that was written. */
path: string;
/** Number of bytes written to the file. */
byteCount: number;
}
interface FileCreateEvent extends BaseEvent {
type: 'file_create';
/** Absolute or workspace-relative path of the newly created file. */
path: string;
/** Number of bytes in the newly created file. */
byteCount: number;
}
interface FileDeleteEvent extends BaseEvent {
type: 'file_delete';
/** Absolute or workspace-relative path of the deleted file. */
path: string;
}
interface FilePatchEvent extends BaseEvent {
type: 'file_patch';
/** Absolute or workspace-relative path of the patched file. */
path: string;
/**
* Unified diff of the changes applied. Format follows the standard
* unified diff format (--- a/file, +++ b/file, @@ hunks).
*/
diff: string;
}
10.1 File Event Ordering
File events are emitted after the corresponding tool_result or tool_call_ready event. They are informational projections of tool activity, not a separate execution stream. A single tool call may produce multiple file events (e.g., a multi-file edit tool).
tool_call_ready(toolName='file_write') -> tool_result -> file_write
tool_call_ready(toolName='edit_file') -> tool_result -> file_patch
11. Shell Operations Events (4 events)
Shell events track command execution by the agent. The shell_start event identifies the command; stdout/stderr deltas stream the output; shell_exit marks completion.
11.1 ShellStartEvent
interface ShellStartEvent extends BaseEvent {
type: 'shell_start';
/** The command string as executed by the agent. */
command: string;
/** The working directory for the command. */
cwd: string;
}
11.2 ShellStdoutDeltaEvent
interface ShellStdoutDeltaEvent extends BaseEvent {
type: 'shell_stdout_delta';
/** A chunk of stdout output from the running command. */
delta: string;
}
11.3 ShellStderrDeltaEvent
interface ShellStderrDeltaEvent extends BaseEvent {
type: 'shell_stderr_delta';
/** A chunk of stderr output from the running command. */
delta: string;
}
11.4 ShellExitEvent
interface ShellExitEvent extends BaseEvent {
type: 'shell_exit';
/**
* The exit code of the command. 0 indicates success; non-zero
* indicates failure. -1 indicates the process was killed by a signal.
*/
exitCode: number;
/** Wall-clock duration of the command execution in milliseconds. */
durationMs: number;
}
11.5 Shell Event Ordering
shell_start
-> shell_stdout_delta* (zero or more, interleaved with stderr)
-> shell_stderr_delta* (zero or more, interleaved with stdout)
-> shell_exit
shell_start always precedes any stdout/stderr deltas. shell_exit is always the final shell event for a given command. Stdout and stderr deltas may interleave in any order. Shell events are nested within the corresponding tool call events (the shell execution is a tool invocation).
12. MCP Tool Calling Events (3 events)
MCP (Model Context Protocol) tool events track invocations of tools provided by MCP servers configured via RunOptions.mcpServers. They are distinct from native tool events to allow consumers to differentiate between built-in and MCP-provided tools.
12.1 Correlation Key: toolCallId
MCP tool events share the toolCallId correlation key with native tool events. The server field distinguishes MCP tool calls from native ones.
interface McpToolCallStartEvent extends BaseEvent {
type: 'mcp_tool_call_start';
/** Unique identifier for this tool call. */
toolCallId: string;
/** The name of the MCP server providing the tool. */
server: string;
/** The tool name within the MCP server's namespace. */
toolName: string;
/** The complete parsed input for the tool call. */
input: unknown;
}
interface McpToolResultEvent extends BaseEvent {
type: 'mcp_tool_result';
/** Correlates to the `McpToolCallStartEvent`. */
toolCallId: string;
/** The MCP server name. */
server: string;
/** The tool name. */
toolName: string;
/** The tool's output value. */
output: unknown;
}
interface McpToolErrorEvent extends BaseEvent {
type: 'mcp_tool_error';
/** Correlates to the `McpToolCallStartEvent`. */
toolCallId: string;
/** The MCP server name. */
server: string;
/** The tool name. */
toolName: string;
/** Human-readable error message from the MCP server or transport layer. */
error: string;
}
12.2 MCP Tool Event Ordering
mcp_tool_call_start(toolCallId='mcp-1')
-> mcp_tool_result(toolCallId='mcp-1') (success path)
OR mcp_tool_error(toolCallId='mcp-1') (failure path)
MCP tool events do not have input delta streaming. The full input is available at mcp_tool_call_start. Each MCP tool call produces exactly one start event followed by exactly one result or error event.
13. Subagent Dispatch Events (3 events)
Subagent events track when the primary agent spawns a secondary agent to handle a subtask. These events are emitted by agents that support parallel or delegated execution.
13.1 Correlation Key: subagentId
All subagent events for a single spawned agent share the same subagentId. This is generated by the adapter and is unique within the parent run.
interface SubagentSpawnEvent extends BaseEvent {
type: 'subagent_spawn';
/** Unique identifier for this subagent invocation. */
subagentId: string;
/** The name of the agent being spawned as a subagent. */
agentName: string;
/** The prompt/task delegated to the subagent. */
prompt: string;
}
interface SubagentResultEvent extends BaseEvent {
type: 'subagent_result';
/** Correlates to the `SubagentSpawnEvent`. */
subagentId: string;
/** The agent name. */
agentName: string;
/** A summary of the subagent's result. */
summary: string;
/**
* Cost incurred by the subagent invocation.
*
* @see CostRecord in Section 3.
*/
cost?: CostRecord;
}
interface SubagentErrorEvent extends BaseEvent {
type: 'subagent_error';
/** Correlates to the `SubagentSpawnEvent`. */
subagentId: string;
/** The agent name. */
agentName: string;
/** Human-readable error message from the subagent. */
error: string;
}
13.2 Subagent Event Ordering
subagent_spawn(subagentId='sa-1')
-> subagent_result(subagentId='sa-1') (success path)
OR subagent_error(subagentId='sa-1') (failure path)
Subagent events do not stream intermediate results from the subagent. The parent adapter collects the subagent's output and emits a single result or error. Multiple subagents may run concurrently; their events may interleave but are correlated by subagentId.
14. Plugin Events (3 events)
Plugin events track the loading, invocation, and failure of agent plugins (skills, extensions, channel plugins, MCP servers managed as plugins).
14.1 Correlation Key: pluginId
All plugin events for a specific plugin share the same pluginId. The pluginId is the plugin's unique identifier as registered with the agent.
interface PluginLoadedEvent extends BaseEvent {
type: 'plugin_loaded';
/** Unique identifier for the plugin (e.g., npm package name). */
pluginId: string;
/** Human-readable plugin name. */
pluginName: string;
/** Semantic version string of the loaded plugin. */
version: string;
}
interface PluginInvokedEvent extends BaseEvent {
type: 'plugin_invoked';
/** The plugin being invoked. */
pluginId: string;
/** Human-readable plugin name. */
pluginName: string;
}
interface PluginErrorEvent extends BaseEvent {
type: 'plugin_error';
/** The plugin that errored. */
pluginId: string;
/** Human-readable plugin name. */
pluginName: string;
/** Human-readable error message. */
error: string;
}
14.2 Plugin Event Ordering
plugin_loaded(pluginId='p-1') (during session startup)
-> plugin_invoked(pluginId='p-1')* (zero or more, during turns)
-> plugin_error(pluginId='p-1')* (zero or more, on failure)
plugin_loaded events are emitted during session initialization, before the first turn. plugin_invoked and plugin_error events occur during turn processing. A plugin_error does not necessarily terminate the run; the agent may continue with remaining plugins.
15. Skill / Agent Doc Loading Events (3 events)
Skill and agent doc events track the loading and invocation of skills (reusable prompt/tool bundles) and AGENTS.md-style documentation files.
interface SkillLoadedEvent extends BaseEvent {
type: 'skill_loaded';
/** The name of the skill that was loaded. */
skillName: string;
/**
* The source of the skill. Common values:
* - File path for file-based skills
* - npm package name for package skills
* - 'built-in' for agent-native skills
*/
source: string;
}
interface SkillInvokedEvent extends BaseEvent {
type: 'skill_invoked';
/** The name of the skill that was invoked. */
skillName: string;
}
interface AgentdocReadEvent extends BaseEvent {
type: 'agentdoc_read';
/**
* Path to the agent documentation file that was read.
* Typically AGENTS.md or similar.
*/
path: string;
}
15.1 Skill/Agentdoc Event Ordering
skill_loaded events are emitted during session initialization. skill_invoked events occur during turn processing. agentdoc_read events occur during session initialization, after session_start but before the first turn_start.
16. Multimodal Events (2 events)
Multimodal events handle image input acknowledgement and image output generation.
interface ImageOutputEvent extends BaseEvent {
type: 'image_output';
/**
* MIME type of the image (e.g., 'image/png', 'image/jpeg', 'image/svg+xml').
*/
mimeType: string;
/**
* Base64-encoded image data. Present when the image is delivered inline.
* Mutually exclusive with `filePath` in typical usage, though both may
* be present if the adapter provides both.
*/
base64?: string;
/**
* Absolute path to the generated image file on disk. Present when the
* agent writes the image to a file rather than streaming it inline.
*/
filePath?: string;
}
interface ImageInputAckEvent extends BaseEvent {
type: 'image_input_ack';
/**
* MIME type of the acknowledged input image.
*/
mimeType: string;
}
17. Cost and Token Events (2 events)
Cost and token events provide real-time cost tracking and token usage telemetry. These are emitted independently of turn_end cost data and may arrive at different granularities.
17.1 CostEvent
Emitted when the agent reports cumulative or incremental cost data. May be emitted multiple times per run as cost information becomes available.
interface CostEvent extends BaseEvent {
type: 'cost';
/**
* The cost record. Whether this is cumulative (total run cost so far)
* or incremental (cost of the latest operation) is adapter-dependent.
* Consumers should treat the final `cost` event as the authoritative
* total, and the `session_end` cost as the canonical aggregate.
*
* @see CostRecord in Section 3.
*/
cost: CostRecord;
}
17.2 TokenUsageEvent
Emitted when the agent reports token usage for a specific API call or turn.
interface TokenUsageEvent extends BaseEvent {
type: 'token_usage';
/** Number of input tokens consumed in this API call. */
inputTokens: number;
/** Number of output tokens generated in this API call. */
outputTokens: number;
/** Number of thinking/reasoning tokens consumed (if applicable). */
thinkingTokens?: number;
/** Number of cached input tokens reused (if applicable). */
cachedTokens?: number;
}
17.3 Cost/Token Event Ordering
Cost and token events are not ordered relative to text or tool events. They may arrive at any point during a turn, typically after the API call that generated the usage completes. The last cost event in a run contains the most up-to-date cumulative cost.
18. Interaction / Waiting Events (4 events)
Interaction events implement the bidirectional communication channel between the agent and the consumer. They enable approval workflows and free-form input prompts.
18.1 Correlation Key: interactionId
All interaction events for a single interaction share the same interactionId. This ID is used with InteractionChannel.respond() on the RunHandle to send a response back to the agent.
interface InputRequiredEvent extends BaseEvent {
type: 'input_required';
/** Unique identifier for this interaction. Used with `RunHandle.interaction.respond()`. */
interactionId: string;
/** The question or prompt the agent is asking the user. */
question: string;
/** Additional context for the question (e.g., what the agent is trying to do). */
context?: string;
/**
* Whether the input request originated from the agent itself or from
* a tool the agent is using.
*/
source: 'agent' | 'tool';
}
interface ApprovalRequestEvent extends BaseEvent {
type: 'approval_request';
/** Unique identifier for this approval interaction. */
interactionId: string;
/** Short description of the action requiring approval (e.g., 'Execute shell command'). */
action: string;
/** Detailed description of what will be done (e.g., the full command). */
detail: string;
/** The tool requesting approval, if applicable. */
toolName?: string;
/**
* Assessed risk level of the action:
* - 'low': read-only operations, safe transformations
* - 'medium': file writes, non-destructive commands
* - 'high': destructive operations, network access, privileged commands
*/
riskLevel: 'low' | 'medium' | 'high';
}
interface ApprovalGrantedEvent extends BaseEvent {
type: 'approval_granted';
/** Correlates to the `ApprovalRequestEvent`. */
interactionId: string;
}
interface ApprovalDeniedEvent extends BaseEvent {
type: 'approval_denied';
/** Correlates to the `ApprovalRequestEvent`. */
interactionId: string;
/** The reason the approval was denied, if provided. */
reason?: string;
}
18.2 Interaction Event Ordering
approval_request(interactionId='ia-1')
-> approval_granted(interactionId='ia-1') (approved path)
OR approval_denied(interactionId='ia-1') (denied path)
input_required(interactionId='ia-2')
-> [consumer responds via InteractionChannel]
An approval_request is always followed by exactly one approval_granted or approval_denied. When RunOptions.approvalMode is 'yolo', no approval_request events are emitted; tools execute without approval. When approvalMode is 'deny', every tool call is auto-denied and an approval_denied event is emitted.
An input_required event pauses the run until the consumer responds. If RunOptions.onInputRequired is set, it is called automatically; otherwise the event is surfaced on RunHandle.interaction.pending for manual response.
19. Rate / Context Limit Events (4 events)
These events surface rate limiting, context window pressure, automatic compaction, and retry behavior.
interface RateLimitedEvent extends BaseEvent {
type: 'rate_limited';
/**
* Suggested delay before retrying, in milliseconds. Absent if the
* agent/API did not provide retry-after guidance.
*/
retryAfterMs?: number;
}
interface ContextLimitWarningEvent extends BaseEvent {
type: 'context_limit_warning';
/** Number of tokens currently used in the context window. */
usedTokens: number;
/** Maximum context window size in tokens for the current model. */
maxTokens: number;
/**
* Percentage of context window used (0-100). Warnings are typically
* emitted when this exceeds 80%.
*/
pctUsed: number;
}
interface ContextCompactedEvent extends BaseEvent {
type: 'context_compacted';
/**
* A human-readable summary of what was removed or compressed
* during compaction.
*/
summary: string;
/** Number of tokens freed by compaction. */
tokensSaved: number;
}
interface RetryEvent extends BaseEvent {
type: 'retry';
/** The current retry attempt number (1-based). */
attempt: number;
/** The maximum number of retry attempts configured. */
maxAttempts: number;
/** Human-readable reason for the retry (e.g., 'rate_limited', 'transient_error'). */
reason: string;
/** Delay in milliseconds before the retry will be attempted. */
delayMs: number;
}
19.1 Rate/Context Event Ordering
rate_limited may occur at any point during a run, typically between API calls. A retry event follows a rate_limited event when auto-retry is enabled. context_limit_warning may occur at any point during a turn. context_compacted follows a context_limit_warning when the agent automatically compacts its context.
20. Run Lifecycle / Control Events (7 events)
Run lifecycle events reflect state transitions triggered by consumer control methods (interrupt(), abort(), pause(), resume()) or by system limits.
interface InterruptedEvent extends BaseEvent {
type: 'interrupted';
}
interface AbortedEvent extends BaseEvent {
type: 'aborted';
}
interface PausedEvent extends BaseEvent {
type: 'paused';
}
interface ResumedEvent extends BaseEvent {
type: 'resumed';
}
interface TimeoutEvent extends BaseEvent {
type: 'timeout';
/**
* Which timeout was hit:
* - 'run': the overall run timeout (`RunOptions.timeout`)
* - 'inactivity': the inactivity timeout (`RunOptions.inactivityTimeout`)
*/
kind: 'run' | 'inactivity';
}
interface TurnLimitEvent extends BaseEvent {
type: 'turn_limit';
/** The maximum number of turns that was configured via `RunOptions.maxTurns`. */
maxTurns: number;
}
interface StreamFallbackEvent extends BaseEvent {
type: 'stream_fallback';
/**
* The streaming capability that fell back to buffered mode:
* - 'text': text generation streaming
* - 'tool_calls': tool input streaming
* - 'thinking': thinking/reasoning streaming
*/
capability: 'text' | 'tool_calls' | 'thinking';
/** Human-readable reason for the fallback. */
reason: string;
}
20.1 Run Lifecycle Event Ordering
interrupted, aborted, and timeout are terminal control events. After any of these, no further turn/text/tool events are emitted. The run transitions to a terminal state and emits session_end.
[running events...] -> interrupted -> session_end
[running events...] -> aborted -> session_end
[running events...] -> timeout -> session_end
[running events...] -> turn_limit -> session_end
paused and resumed are non-terminal. Events stop flowing after paused and resume after resumed.
[running events...] -> paused -> resumed -> [running events...]
stream_fallback is informational and non-terminal. It is emitted once per capability per run, at the point the adapter first detects that the capability must fall back to buffered mode.
21. Error Events (5 events)
Error events represent failures of varying severity. Some are recoverable (the run continues); others are terminal (the run ends).
interface AuthErrorEvent extends BaseEvent {
type: 'auth_error';
/** Human-readable error message describing the auth failure. */
message: string;
/**
* Actionable guidance for the user to resolve the auth issue.
* E.g., 'Run "claude login" to authenticate' or 'Set OPENAI_API_KEY'.
*/
guidance: string;
}
interface RateLimitErrorEvent extends BaseEvent {
type: 'rate_limit_error';
/** Human-readable error message. */
message: string;
/** Suggested retry delay in milliseconds, if provided by the API. */
retryAfterMs?: number;
}
interface ContextExceededEvent extends BaseEvent {
type: 'context_exceeded';
/** Number of tokens used when the context was exceeded. */
usedTokens: number;
/** Maximum context window size in tokens. */
maxTokens: number;
}
interface CrashEvent extends BaseEvent {
type: 'crash';
/** Exit code from the agent subprocess. */
exitCode: number;
/** Captured stderr output from the crashed subprocess. */
stderr: string;
}
interface ErrorEvent extends BaseEvent {
type: 'error';
/**
* Machine-readable error code from the ErrorCode enum.
*
* @see ErrorCode in 01-core-types-and-client.md, Section 3.1.
*/
code: ErrorCode;
/** Human-readable error message. */
message: string;
/**
* Whether the run can continue after this error.
* When false, the run will terminate after this event.
*/
recoverable: boolean;
}
21.1 Error Event Ordering and Terminality
| Event | Terminal | Followed By |
|---|---|---|
auth_error | Yes | session_end (if session was started) |
rate_limit_error | No (if retries remain) / Yes (if exhausted) | retry (if retrying) or session_end |
context_exceeded | Yes | session_end |
crash | Yes | Nothing (subprocess is gone) |
error (recoverable: true) | No | Normal event flow continues |
error (recoverable: false) | Yes | session_end |
Terminal error events halt the run. After a terminal error, no further text, tool, or turn events are emitted. session_end follows when possible; crash events may not have a session_end if the subprocess died before session state could be finalized.
22. Debug Events (2 events)
Debug events provide adapter-level diagnostic information. They are emitted regardless of the debug flag, but the raw field on other events is only populated when debug is enabled.
interface DebugEvent extends BaseEvent {
type: 'debug';
/**
* Severity level:
* - 'verbose': detailed tracing information (high volume)
* - 'info': notable adapter-level information
* - 'warn': potential issues that do not prevent operation
*/
level: 'verbose' | 'info' | 'warn';
/** Human-readable debug message. */
message: string;
}
interface LogEvent extends BaseEvent {
type: 'log';
/**
* Which output stream the line came from:
* - 'stdout': standard output from the agent subprocess
* - 'stderr': standard error from the agent subprocess
*/
source: 'stdout' | 'stderr';
/** A single line of output from the agent subprocess. */
line: string;
}
22.1 Debug Event Ordering
Debug and log events may appear at any point in the event stream. They do not affect the ordering guarantees of other event categories. log events with source: 'stderr' during subprocess startup may appear before session_start.
23. Complete Correlation Key Reference
Correlation keys allow consumers to group related events across the stream. Each key is unique within a single run.
| Correlation Key | Events Using It | Purpose |
|---|---|---|
toolCallId | tool_call_start, tool_input_delta, tool_call_ready, tool_result, tool_error, mcp_tool_call_start, mcp_tool_result, mcp_tool_error | Groups all events for a single tool invocation (native or MCP). |
subagentId | subagent_spawn, subagent_result, subagent_error | Groups all events for a single subagent invocation. |
interactionId | input_required, approval_request, approval_granted, approval_denied | Groups all events for a single interaction prompt/approval cycle. |
pluginId | plugin_loaded, plugin_invoked, plugin_error | Groups all events for a specific plugin. |
sessionId | session_start, session_resume, session_fork, session_checkpoint, session_end | Groups all session lifecycle events. |
Note:
turnIndexis a positional index used for turn/step scoping (present onturn_start,turn_end,step_start,step_end) but is not a correlation identifier — it does not uniquely identify a resource across events the way the keys above do.
23.1 Key Format Conventions
| Key | Format | Example |
|---|---|---|
toolCallId | Adapter-dependent; typically toolu_ prefix (Claude), UUID (Codex), or numeric ID | toolu_01ABcD, tc-a1b2c3 |
subagentId | ULID or UUID generated by the adapter | 01HXYZ... |
interactionId | ULID generated by the adapter | 01HABC... |
pluginId | Plugin package name or adapter-assigned ID | @scope/plugin-name, skill-file-hash |
sessionId | Agent-native format | uuid-v4 (Claude), session-dirname (Codex) |
24. Streaming Model
The streaming model controls how events are emitted by adapters. It is configured via RunOptions.stream (or ClientOptions.stream for the default).
24.1 Streaming Modes
stream: 'auto' (default)
The adapter uses streaming for each output type that the agent and model support. For unsupported output types, the adapter silently falls back to buffered emission.
Behavior per capability:
| Capability | Supported | Not Supported |
|---|---|---|
Text streaming (supportsTextStreaming) | Multiple text_delta events with small fragments | Single text_delta with complete text |
Tool call streaming (supportsToolCallStreaming) | tool_input_delta events for tool input | tool_call_start with complete input, no tool_input_delta events |
Thinking streaming (supportsThinkingStreaming) | Multiple thinking_delta events | Single thinking_delta with complete thinking text |
When fallback activates, a stream_fallback event is emitted once per capability. This event is informational; the consumer still receives the same event types with the same ordering guarantees.
stream: true
Requires text streaming. If the adapter has supportsTextStreaming: false, mux.run() throws CapabilityError synchronously before spawning the subprocess.
Tool call and thinking streaming still fall back silently (same as 'auto'), because these are supplementary capabilities that should not block the run.
stream: false
All output is buffered. The adapter waits for the agent to complete its response, then emits events in rapid succession:
message_start- A single
text_deltawithdeltaequal to the complete text andaccumulatedequal to the complete text message_stop
Tool events are emitted after all text. Tool call streaming never occurs. Thinking events, if present, are emitted as a single thinking_delta with the complete thinking text.
24.2 Stream Fallback Behavior
When stream: 'auto' detects that a capability is unsupported, the adapter:
- Emits a
stream_fallbackevent identifying the capability and reason. - Buffers the corresponding output internally.
- Emits the buffered output as a single delta event when the agent completes that output segment.
The stream_fallback event is emitted at most once per capability per run. Multiple capabilities may fall back independently.
// Example: adapter detects thinking streaming is not supported
// Emitted once when first thinking output arrives:
{
type: 'stream_fallback',
capability: 'thinking',
reason: 'Model gpt-4o does not support thinking streaming',
runId: '01HXYZ...',
agent: 'codex',
timestamp: 1712345678901
}
// Then a single thinking_delta with accumulated text:
{
type: 'thinking_delta',
delta: '<complete thinking text>',
accumulated: '<complete thinking text>',
...
}
24.3 Backpressure
The event stream implements backpressure through the async iterator protocol. When a consumer is slow to consume events:
-
AsyncIterable consumption (
for await...of): The adapter buffers events in an internal queue. The queue has no hard limit; extremely slow consumers may cause memory growth. Consumers should avoid blocking in event handlers. -
EventEmitter consumption (
on/off): Events are delivered synchronously to all listeners. No backpressure is applied. Listeners that perform async work should queue internally. -
Promise consumption (
await): No streaming occurs from the consumer's perspective. All events are processed internally by theRunHandleto build theRunResult.
Adapter-side buffering: Each adapter maintains an internal ring buffer for subprocess stdout/stderr. The buffer size is 64 KB per stream. When the buffer fills, the oldest data is discarded and a debug event with level: 'warn' is emitted indicating data loss. This situation indicates the consumer is not draining events fast enough.
24.4 Streaming Mode Summary
| Behavior | 'auto' | true | false |
|---|---|---|---|
| Text streaming | If supported | Required (throws if not) | Buffered |
| Tool call streaming | If supported | If supported | Buffered |
| Thinking streaming | If supported | If supported | Buffered |
stream_fallback emitted | Yes, per unsupported capability | Yes, for tool/thinking only | No |
CapabilityError thrown | Never | Yes, if text streaming unsupported | Never |
| Event types received | All 67 | All 67 | All 67 |
25. Event Ordering Contracts
This section formally specifies the ordering guarantees that consumers can rely on.
25.1 Global Ordering
session_startis the first domain event in every run. Onlydebugandlogevents may precede it (from subprocess startup).session_endis the last domain event in every successful run. Onlydebugandlogevents may follow it.- All turn events occur between
session_startandsession_end. - Events within a run are totally ordered. The async iterator yields them in emission order.
25.2 Turn-Level Ordering
turn_start(N)always precedesturn_end(N).turn_end(N)always precedesturn_start(N+1).- All thinking, text, tool, file, shell, MCP, and subagent events for turn N occur between
turn_start(N)andturn_end(N). - Steps, when present, are properly nested:
step_startprecedesstep_endand both are within the enclosing turn.
25.3 Within-Turn Ordering
- Thinking events precede text events within the same generation cycle.
- Text events and tool events may interleave across generation cycles (e.g., text -> tool call -> more text).
- File events follow their corresponding tool result events.
- Shell events are nested within their corresponding tool call events.
25.4 Interaction Ordering
approval_requestalways precedesapproval_grantedorapproval_deniedfor the sameinteractionId.- The run pauses after emitting
input_requiredorapproval_request(when not auto-handled) until the consumer responds. - Tool execution events follow
approval_grantedfor the approved tool call.
25.5 Error and Terminal Ordering
- Terminal errors (
auth_error,context_exceeded,crash, non-recoverableerror) are followed only bysession_end(when possible) anddebug/logevents. interrupted,aborted,timeout, andturn_limitare followed bysession_end.- After any terminal event, no further turn/text/tool events are emitted.
25.6 Complete Run Event Sequence
[debug/log]* -- subprocess startup noise
session_start -- always first domain event
[session_resume]? -- if resuming
[session_fork]? -- if forking
[plugin_loaded]* -- zero or more during init
[skill_loaded]* -- zero or more during init
[agentdoc_read]* -- zero or more during init
turn_start(0)
[stream_fallback]* -- zero or more, when adapter first detects capability fallback
[step_start(0,0)]?
[thinking_start -> thinking_delta+ -> thinking_stop]?
[message_start -> text_delta+ -> message_stop]*
[tool_call_start -> tool_input_delta* -> tool_call_ready
-> (tool_result | tool_error)
-> (file_read | file_write | file_create | file_delete | file_patch)*
]*
[mcp_tool_call_start -> (mcp_tool_result | mcp_tool_error)]*
[shell_start -> shell_stdout_delta* -> shell_stderr_delta* -> shell_exit]*
[subagent_spawn -> (subagent_result | subagent_error)]*
[approval_request -> (approval_granted | approval_denied)]*
[input_required]*
[plugin_invoked | plugin_error]*
[skill_invoked]*
[image_output | image_input_ack]*
[cost | token_usage]*
[rate_limited -> retry?]*
[context_limit_warning -> context_compacted?]*
[step_end(0,0)]?
[step_start(0,N) -> ... -> step_end(0,N)]*
[session_checkpoint]?
turn_end(0)
[turn_start(N) -> ... -> turn_end(N)]*
[interrupted | aborted | timeout | turn_limit]? -- terminal control
[auth_error | rate_limit_error | context_exceeded | crash | error]?
session_end -- always last domain event
[debug/log]* -- subprocess teardown noise
26. Per-Agent Event Support Matrix
Each built-in adapter emits a subset of the 67 event types based on the agent's capabilities and output format. This matrix shows which event categories each adapter supports.
26.1 Category Support
| Category | claude | codex | gemini | copilot | cursor | opencode | pi | omp | openclaw | hermes |
|---|---|---|---|---|---|---|---|---|---|---|
| Session lifecycle | Full | Partial | Partial | Minimal | Partial | Full | Full | Full | Full | Full |
| Turn / step | Full | Full | Full | Turn only | Turn only | Full | Full | Full | Full | Full |
| Text streaming | Full | Full | Full | Full | Full | Full | Full | Full | Full | Full |
| Thinking | Full | Full | Full | No | No | Full | Full | Full | Full | Full |
| Tool calling | Full | Full | Full | Partial | Full | Full | Full | Full | Full | Full |
| File operations | Full | Full | Full | Full | Partial | Full | Full | Full | Full | Full |
| Shell operations | Full | Full | Full | No | Partial | Full | Full | Full | Full | Full |
| MCP | Full | No | Partial | No | Full | Full | Full | Full | Full | Full |
| Subagent dispatch | Full | No | No | No | No | No | Partial | Partial | Full | No |
| Plugin events | Partial | No | No | No | Full | Full | Full | Full | Full | No |
| Skill / agentdoc | Full | No | No | No | Full | Full | Full | Full | Full | No |
| Multimodal | Full | Partial | Full | No | Full | Partial | Partial | Partial | Full | Partial |
| Cost / tokens | Full | Full | Full | No | No | Full | Full | Full | Full | Full |
| Interaction | Full | Full | Full | Partial | Full | Full | Full | Full | Full | Full |
| Rate / context | Full | Full | Full | Partial | Partial | Full | Full | Full | Full | Full |
| Run lifecycle | Full | Full | Full | Full | Full | Full | Full | Full | Full | Full |
| Errors | Full | Full | Full | Full | Full | Full | Full | Full | Full | Full |
| Debug | Full | Full | Full | Full | Full | Full | Full | Full | Full | Full |
26.2 Support Level Definitions
| Level | Meaning |
|---|---|
| Full | All events in the category are emitted with all fields populated. |
| Partial | Some events in the category are emitted, or some fields may be absent. See per-agent notes below. |
| Minimal | Only the bare minimum events are emitted (e.g., session_start and session_end with synthetic data). |
| Turn only | Only turn-level events are emitted; step events are not available. |
| No | No events in this category are emitted by this adapter. |
26.3 Per-Agent Notes
claude: Full support across all categories. Claude Code provides the richest event data of any adapter. Subagent dispatch maps to Claude Code's built-in Agent tool. Plugin events are partial because Claude Code's skill system does not emit explicit load/invoke signals; the adapter synthesizes these from tool call patterns.
codex: No MCP, subagent, plugin, or skill support. Session lifecycle is partial: no fork or checkpoint support. Multimodal is partial: supports image input acknowledgement but not image output.
gemini: No subagent, plugin, or skill support. MCP is partial: Gemini CLI has experimental MCP support with limited error reporting. Session lifecycle is partial: no fork support.
copilot: Minimal session lifecycle (no resume, fork, or checkpoint). No thinking, shell, MCP, subagent, plugin, skill, multimodal, or cost support. Interaction is partial: supports approval but not free-form input. Tool calling is partial: tool input streaming is not available.
cursor: No thinking, subagent, or cost/token support. Turn-level only (no steps). Shell operations are partial: command output is captured but cwd is not always available. File operations are partial: byte counts may be estimated.
opencode: Full support across most categories. No subagent dispatch. Multimodal is partial: image input only. MCP and plugin support depend on OpenCode version.
pi: Full support across most categories. Subagent dispatch is partial: limited to Pi's task delegation feature. Multimodal is partial: image input only, no image output.
omp: Same as Pi with additional support through oh-my-pi extensions. Subagent dispatch is partial: same as Pi.
openclaw: Full support across all categories including its multi-channel plugin system. The richest plugin event data among all adapters.
hermes: Full support for session, turn/step, text, thinking, tool calling, file ops, shell ops, interaction, rate/context, cost/tokens, run lifecycle, errors, debug, and MCP (both client and server mode via the hermes-acp entry point). No subagent, plugin, or skill support. Multimodal is partial: image input only. hermes-agent is installed via pip install hermes-agent (Python >= 3.11) and its output is parsed from JSON-lines format.
26.4 Unsupported Event Behavior
When an adapter does not support a category, it simply never emits those events. Consumers should not assume any event type will be present. The recommended pattern is:
const handle = mux.run({ agent: 'copilot', prompt: 'Hello' });
for await (const event of handle) {
switch (event.type) {
case 'text_delta':
// Always safe -- all adapters emit text events
process.stdout.write(event.delta);
break;
case 'thinking_delta':
// Only some adapters emit this; simply not reached for copilot
displayThinking(event.delta);
break;
case 'cost':
// Only emitted by adapters that track cost; absent for copilot
updateCostDisplay(event.cost);
break;
}
}
27. Event Type Literal Constants
For convenience, the package exports a frozen object containing all event type literals:
/**
* All 67 event type string literals as a const object.
* Useful for programmatic iteration and exhaustiveness checks.
*/
const AgentEventType = {
// Session lifecycle
SESSION_START: 'session_start',
SESSION_RESUME: 'session_resume',
SESSION_FORK: 'session_fork',
SESSION_CHECKPOINT: 'session_checkpoint',
SESSION_END: 'session_end',
// Turn / step
TURN_START: 'turn_start',
TURN_END: 'turn_end',
STEP_START: 'step_start',
STEP_END: 'step_end',
// Text / message streaming
MESSAGE_START: 'message_start',
TEXT_DELTA: 'text_delta',
MESSAGE_STOP: 'message_stop',
// Thinking / reasoning
THINKING_START: 'thinking_start',
THINKING_DELTA: 'thinking_delta',
THINKING_STOP: 'thinking_stop',
// Tool calling
TOOL_CALL_START: 'tool_call_start',
TOOL_INPUT_DELTA: 'tool_input_delta',
TOOL_CALL_READY: 'tool_call_ready',
TOOL_RESULT: 'tool_result',
TOOL_ERROR: 'tool_error',
// File operations
FILE_READ: 'file_read',
FILE_WRITE: 'file_write',
FILE_CREATE: 'file_create',
FILE_DELETE: 'file_delete',
FILE_PATCH: 'file_patch',
// Shell operations
SHELL_START: 'shell_start',
SHELL_STDOUT_DELTA: 'shell_stdout_delta',
SHELL_STDERR_DELTA: 'shell_stderr_delta',
SHELL_EXIT: 'shell_exit',
// MCP tool calling
MCP_TOOL_CALL_START: 'mcp_tool_call_start',
MCP_TOOL_RESULT: 'mcp_tool_result',
MCP_TOOL_ERROR: 'mcp_tool_error',
// Subagent dispatch
SUBAGENT_SPAWN: 'subagent_spawn',
SUBAGENT_RESULT: 'subagent_result',
SUBAGENT_ERROR: 'subagent_error',
// Plugin events
PLUGIN_LOADED: 'plugin_loaded',
PLUGIN_INVOKED: 'plugin_invoked',
PLUGIN_ERROR: 'plugin_error',
// Skill / agent doc
SKILL_LOADED: 'skill_loaded',
SKILL_INVOKED: 'skill_invoked',
AGENTDOC_READ: 'agentdoc_read',
// Multimodal
IMAGE_OUTPUT: 'image_output',
IMAGE_INPUT_ACK: 'image_input_ack',
// Cost / tokens
COST: 'cost',
TOKEN_USAGE: 'token_usage',
// Interaction / waiting
INPUT_REQUIRED: 'input_required',
APPROVAL_REQUEST: 'approval_request',
APPROVAL_GRANTED: 'approval_granted',
APPROVAL_DENIED: 'approval_denied',
// Rate / context limits
RATE_LIMITED: 'rate_limited',
CONTEXT_LIMIT_WARNING: 'context_limit_warning',
CONTEXT_COMPACTED: 'context_compacted',
RETRY: 'retry',
// Run lifecycle / control
INTERRUPTED: 'interrupted',
ABORTED: 'aborted',
PAUSED: 'paused',
RESUMED: 'resumed',
TIMEOUT: 'timeout',
TURN_LIMIT: 'turn_limit',
STREAM_FALLBACK: 'stream_fallback',
// Errors
AUTH_ERROR: 'auth_error',
RATE_LIMIT_ERROR: 'rate_limit_error',
CONTEXT_EXCEEDED: 'context_exceeded',
CRASH: 'crash',
ERROR: 'error',
// Debug
DEBUG: 'debug',
LOG: 'log',
} as const;
type AgentEventTypeLiteral = typeof AgentEventType[keyof typeof AgentEventType];
28. Type Guard Utilities
The package exports type guard functions for narrowing AgentEvent to specific types or categories:
/**
* Narrow an AgentEvent to a specific type.
*/
function isEventType<T extends AgentEvent['type']>(
event: AgentEvent,
type: T
): event is Extract<AgentEvent, { type: T }> {
return event.type === type;
}
/**
* Check if an event belongs to a category.
*/
function isSessionEvent(event: AgentEvent): event is
SessionStartEvent | SessionResumeEvent | SessionForkEvent |
SessionCheckpointEvent | SessionEndEvent {
return event.type.startsWith('session_');
}
function isTurnEvent(event: AgentEvent): event is
TurnStartEvent | TurnEndEvent | StepStartEvent | StepEndEvent {
return event.type === 'turn_start' || event.type === 'turn_end'
|| event.type === 'step_start' || event.type === 'step_end';
}
function isTextEvent(event: AgentEvent): event is
MessageStartEvent | TextDeltaEvent | MessageStopEvent {
return event.type === 'message_start' || event.type === 'text_delta'
|| event.type === 'message_stop';
}
function isThinkingEvent(event: AgentEvent): event is
ThinkingStartEvent | ThinkingDeltaEvent | ThinkingStopEvent {
return event.type.startsWith('thinking_');
}
function isToolEvent(event: AgentEvent): event is
ToolCallStartEvent | ToolInputDeltaEvent | ToolCallReadyEvent |
ToolResultEvent | ToolErrorEvent {
return event.type.startsWith('tool_');
}
function isFileEvent(event: AgentEvent): event is
FileReadEvent | FileWriteEvent | FileCreateEvent |
FileDeleteEvent | FilePatchEvent {
return event.type.startsWith('file_');
}
function isShellEvent(event: AgentEvent): event is
ShellStartEvent | ShellStdoutDeltaEvent | ShellStderrDeltaEvent |
ShellExitEvent {
return event.type.startsWith('shell_');
}
function isMcpEvent(event: AgentEvent): event is
McpToolCallStartEvent | McpToolResultEvent | McpToolErrorEvent {
return event.type.startsWith('mcp_');
}
function isSubagentEvent(event: AgentEvent): event is
SubagentSpawnEvent | SubagentResultEvent | SubagentErrorEvent {
return event.type.startsWith('subagent_');
}
function isPluginEvent(event: AgentEvent): event is
PluginLoadedEvent | PluginInvokedEvent | PluginErrorEvent {
return event.type.startsWith('plugin_');
}
function isSkillEvent(event: AgentEvent): event is
SkillLoadedEvent | SkillInvokedEvent | AgentdocReadEvent {
return event.type === 'skill_loaded' || event.type === 'skill_invoked'
|| event.type === 'agentdoc_read';
}
function isMultimodalEvent(event: AgentEvent): event is
ImageOutputEvent | ImageInputAckEvent {
return event.type === 'image_output' || event.type === 'image_input_ack';
}
function isCostEvent(event: AgentEvent): event is
CostEvent | TokenUsageEvent {
return event.type === 'cost' || event.type === 'token_usage';
}
function isInteractionEvent(event: AgentEvent): event is
InputRequiredEvent | ApprovalRequestEvent |
ApprovalGrantedEvent | ApprovalDeniedEvent {
return event.type === 'input_required' || event.type === 'approval_request'
|| event.type === 'approval_granted' || event.type === 'approval_denied';
}
function isRateLimitEvent(event: AgentEvent): event is
RateLimitedEvent | ContextLimitWarningEvent |
ContextCompactedEvent | RetryEvent {
return event.type === 'rate_limited' || event.type === 'context_limit_warning'
|| event.type === 'context_compacted' || event.type === 'retry';
}
function isRunLifecycleEvent(event: AgentEvent): event is
InterruptedEvent | AbortedEvent | PausedEvent | ResumedEvent |
TimeoutEvent | TurnLimitEvent | StreamFallbackEvent {
return event.type === 'interrupted' || event.type === 'aborted'
|| event.type === 'paused' || event.type === 'resumed'
|| event.type === 'timeout' || event.type === 'turn_limit'
|| event.type === 'stream_fallback';
}
function isErrorEvent(event: AgentEvent): event is
AuthErrorEvent | RateLimitErrorEvent | ContextExceededEvent |
CrashEvent | ErrorEvent {
return event.type === 'auth_error' || event.type === 'rate_limit_error'
|| event.type === 'context_exceeded' || event.type === 'crash'
|| event.type === 'error';
}
function isDebugEvent(event: AgentEvent): event is
DebugEvent | LogEvent {
return event.type === 'debug' || event.type === 'log';
}
/**
* Check if an event is terminal (the run will end after this event).
*/
function isTerminalEvent(event: AgentEvent): boolean {
return event.type === 'interrupted'
|| event.type === 'aborted'
|| event.type === 'timeout'
|| event.type === 'turn_limit'
|| event.type === 'auth_error'
|| event.type === 'context_exceeded'
|| event.type === 'crash'
|| (event.type === 'error' && !(event as ErrorEvent).recoverable);
}
/**
* Check if an event carries a correlation key.
*/
function hasCorrelationKey(event: AgentEvent, key: string): boolean {
return key in event;
}
29. Event Count Verification
Total event types by category:
| # | Category | Count | Event Types |
|---|---|---|---|
| 1 | Session lifecycle | 5 | session_start, session_resume, session_fork, session_checkpoint, session_end |
| 2 | Turn / step | 4 | turn_start, turn_end, step_start, step_end |
| 3 | Text / message streaming | 3 | message_start, text_delta, message_stop |
| 4 | Thinking / reasoning | 3 | thinking_start, thinking_delta, thinking_stop |
| 5 | Tool calling | 5 | tool_call_start, tool_input_delta, tool_call_ready, tool_result, tool_error |
| 6 | File operations | 5 | file_read, file_write, file_create, file_delete, file_patch |
| 7 | Shell operations | 4 | shell_start, shell_stdout_delta, shell_stderr_delta, shell_exit |
| 8 | MCP tool calling | 3 | mcp_tool_call_start, mcp_tool_result, mcp_tool_error |
| 9 | Subagent dispatch | 3 | subagent_spawn, subagent_result, subagent_error |
| 10 | Plugin events | 3 | plugin_loaded, plugin_invoked, plugin_error |
| 11 | Skill / agent doc | 3 | skill_loaded, skill_invoked, agentdoc_read |
| 12 | Multimodal | 2 | image_output, image_input_ack |
| 13 | Cost / tokens | 2 | cost, token_usage |
| 14 | Interaction / waiting | 4 | input_required, approval_request, approval_granted, approval_denied |
| 15 | Rate / context limits | 4 | rate_limited, context_limit_warning, context_compacted, retry |
| 16 | Run lifecycle / control | 7 | interrupted, aborted, paused, resumed, timeout, turn_limit, stream_fallback |
| 17 | Errors | 5 | auth_error, rate_limit_error, context_exceeded, crash, error |
| 18 | Debug | 2 | debug, log |
| Total | 67 |
Implementation Status (2026-04-12)
The event union and parse-context contract are implemented as specified. The stream is produced by StreamAssembler and consumed by RunHandleImpl.events(). Adapters emit events by returning AgentEvent | AgentEvent[] | null from parseEvent(line, context), driven by line buffering in spawn-runner.ts. No changes to the wire shape.