Skip to main content

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 / ConceptSpecSection
AgentName, BuiltInAgentName01-core-types-and-client.md1.4
AgentMuxClient, createClient()01-core-types-and-client.md5
RunOptions, stream option02-run-options-and-profiles.md2, 5
RunHandle, AsyncIterable<AgentEvent>03-run-handle-and-interaction.md2
RunResult03-run-handle-and-interaction.md3
CostRecord01-core-types-and-client.md4.2.3
AgentCapabilities06-capabilities-and-models.md2
ErrorCode, AgentMuxError01-core-types-and-client.md3.1
InteractionChannel03-run-handle-and-interaction.md5

1.2 Design Principles

  1. Uniform shape. Every event extends BaseEvent. Consumers can always access type, runId, agent, and timestamp without type narrowing.
  2. Additive-only. New event types may be added in minor versions; existing types are never removed or changed in breaking ways.
  3. Same types in all modes. Whether stream is 'auto', true, or false, the consumer always receives the same set of event types. The mode only controls granularity and timing.
  4. Correlation by key. Related events share a correlation key (toolCallId, subagentId, interactionId, pluginId) so consumers can group them without position-based logic.
  5. No agent-specific events. The union is closed for agent-specific data. The raw field on BaseEvent and the debug event 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

FieldTypeRequiredConstraints
typestring (literal)YesOne of the 67 defined event type literals.
runIdstringYesULID format, 26 characters, Crockford Base32.
agentAgentNameYesOne of the 10 built-in agents or a registered plugin adapter name.
timestampnumberYesPositive integer, Unix epoch milliseconds, monotonically non-decreasing within a run.
rawstringNoPresent 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

FieldTypeRequiredConstraints
totalUsdnumberYesNon-negative. 0 when cost data is unavailable.
inputTokensnumberYesNon-negative integer.
outputTokensnumberYesNon-negative integer.
thinkingTokensnumberNoNon-negative integer. Omitted when thinking was not used.
cachedTokensnumberNoNon-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

EventTerminalFollowed By
auth_errorYessession_end (if session was started)
rate_limit_errorNo (if retries remain) / Yes (if exhausted)retry (if retrying) or session_end
context_exceededYessession_end
crashYesNothing (subprocess is gone)
error (recoverable: true)NoNormal event flow continues
error (recoverable: false)Yessession_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 KeyEvents Using ItPurpose
toolCallIdtool_call_start, tool_input_delta, tool_call_ready, tool_result, tool_error, mcp_tool_call_start, mcp_tool_result, mcp_tool_errorGroups all events for a single tool invocation (native or MCP).
subagentIdsubagent_spawn, subagent_result, subagent_errorGroups all events for a single subagent invocation.
interactionIdinput_required, approval_request, approval_granted, approval_deniedGroups all events for a single interaction prompt/approval cycle.
pluginIdplugin_loaded, plugin_invoked, plugin_errorGroups all events for a specific plugin.
sessionIdsession_start, session_resume, session_fork, session_checkpoint, session_endGroups all session lifecycle events.

Note: turnIndex is a positional index used for turn/step scoping (present on turn_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

KeyFormatExample
toolCallIdAdapter-dependent; typically toolu_ prefix (Claude), UUID (Codex), or numeric IDtoolu_01ABcD, tc-a1b2c3
subagentIdULID or UUID generated by the adapter01HXYZ...
interactionIdULID generated by the adapter01HABC...
pluginIdPlugin package name or adapter-assigned ID@scope/plugin-name, skill-file-hash
sessionIdAgent-native formatuuid-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:

CapabilitySupportedNot Supported
Text streaming (supportsTextStreaming)Multiple text_delta events with small fragmentsSingle text_delta with complete text
Tool call streaming (supportsToolCallStreaming)tool_input_delta events for tool inputtool_call_start with complete input, no tool_input_delta events
Thinking streaming (supportsThinkingStreaming)Multiple thinking_delta eventsSingle 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:

  1. message_start
  2. A single text_delta with delta equal to the complete text and accumulated equal to the complete text
  3. 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:

  1. Emits a stream_fallback event identifying the capability and reason.
  2. Buffers the corresponding output internally.
  3. 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:

  1. 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.

  2. EventEmitter consumption (on/off): Events are delivered synchronously to all listeners. No backpressure is applied. Listeners that perform async work should queue internally.

  3. Promise consumption (await): No streaming occurs from the consumer's perspective. All events are processed internally by the RunHandle to build the RunResult.

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'truefalse
Text streamingIf supportedRequired (throws if not)Buffered
Tool call streamingIf supportedIf supportedBuffered
Thinking streamingIf supportedIf supportedBuffered
stream_fallback emittedYes, per unsupported capabilityYes, for tool/thinking onlyNo
CapabilityError thrownNeverYes, if text streaming unsupportedNever
Event types receivedAll 67All 67All 67

25. Event Ordering Contracts

This section formally specifies the ordering guarantees that consumers can rely on.

25.1 Global Ordering

  1. session_start is the first domain event in every run. Only debug and log events may precede it (from subprocess startup).
  2. session_end is the last domain event in every successful run. Only debug and log events may follow it.
  3. All turn events occur between session_start and session_end.
  4. Events within a run are totally ordered. The async iterator yields them in emission order.

25.2 Turn-Level Ordering

  1. turn_start(N) always precedes turn_end(N).
  2. turn_end(N) always precedes turn_start(N+1).
  3. All thinking, text, tool, file, shell, MCP, and subagent events for turn N occur between turn_start(N) and turn_end(N).
  4. Steps, when present, are properly nested: step_start precedes step_end and both are within the enclosing turn.

25.3 Within-Turn Ordering

  1. Thinking events precede text events within the same generation cycle.
  2. Text events and tool events may interleave across generation cycles (e.g., text -> tool call -> more text).
  3. File events follow their corresponding tool result events.
  4. Shell events are nested within their corresponding tool call events.

25.4 Interaction Ordering

  1. approval_request always precedes approval_granted or approval_denied for the same interactionId.
  2. The run pauses after emitting input_required or approval_request (when not auto-handled) until the consumer responds.
  3. Tool execution events follow approval_granted for the approved tool call.

25.5 Error and Terminal Ordering

  1. Terminal errors (auth_error, context_exceeded, crash, non-recoverable error) are followed only by session_end (when possible) and debug/log events.
  2. interrupted, aborted, timeout, and turn_limit are followed by session_end.
  3. 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

Categoryclaudecodexgeminicopilotcursoropencodepiompopenclawhermes
Session lifecycleFullPartialPartialMinimalPartialFullFullFullFullFull
Turn / stepFullFullFullTurn onlyTurn onlyFullFullFullFullFull
Text streamingFullFullFullFullFullFullFullFullFullFull
ThinkingFullFullFullNoNoFullFullFullFullFull
Tool callingFullFullFullPartialFullFullFullFullFullFull
File operationsFullFullFullFullPartialFullFullFullFullFull
Shell operationsFullFullFullNoPartialFullFullFullFullFull
MCPFullNoPartialNoFullFullFullFullFullFull
Subagent dispatchFullNoNoNoNoNoPartialPartialFullNo
Plugin eventsPartialNoNoNoFullFullFullFullFullNo
Skill / agentdocFullNoNoNoFullFullFullFullFullNo
MultimodalFullPartialFullNoFullPartialPartialPartialFullPartial
Cost / tokensFullFullFullNoNoFullFullFullFullFull
InteractionFullFullFullPartialFullFullFullFullFullFull
Rate / contextFullFullFullPartialPartialFullFullFullFullFull
Run lifecycleFullFullFullFullFullFullFullFullFullFull
ErrorsFullFullFullFullFullFullFullFullFullFull
DebugFullFullFullFullFullFullFullFullFullFull

26.2 Support Level Definitions

LevelMeaning
FullAll events in the category are emitted with all fields populated.
PartialSome events in the category are emitted, or some fields may be absent. See per-agent notes below.
MinimalOnly the bare minimum events are emitted (e.g., session_start and session_end with synthetic data).
Turn onlyOnly turn-level events are emitted; step events are not available.
NoNo 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:

#CategoryCountEvent Types
1Session lifecycle5session_start, session_resume, session_fork, session_checkpoint, session_end
2Turn / step4turn_start, turn_end, step_start, step_end
3Text / message streaming3message_start, text_delta, message_stop
4Thinking / reasoning3thinking_start, thinking_delta, thinking_stop
5Tool calling5tool_call_start, tool_input_delta, tool_call_ready, tool_result, tool_error
6File operations5file_read, file_write, file_create, file_delete, file_patch
7Shell operations4shell_start, shell_stdout_delta, shell_stderr_delta, shell_exit
8MCP tool calling3mcp_tool_call_start, mcp_tool_result, mcp_tool_error
9Subagent dispatch3subagent_spawn, subagent_result, subagent_error
10Plugin events3plugin_loaded, plugin_invoked, plugin_error
11Skill / agent doc3skill_loaded, skill_invoked, agentdoc_read
12Multimodal2image_output, image_input_ack
13Cost / tokens2cost, token_usage
14Interaction / waiting4input_required, approval_request, approval_granted, approval_denied
15Rate / context limits4rate_limited, context_limit_warning, context_compacted, retry
16Run lifecycle / control7interrupted, aborted, paused, resumed, timeout, turn_limit, stream_fallback
17Errors5auth_error, rate_limit_error, context_exceeded, crash, error
18Debug2debug, log
Total67

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.