---
title: Event Types
description: Current SmithersEvent variants, event categories, and top-level fields.
---

`SmithersEvent` is the discriminated union understood by the runtime and observability layer. Runtime-emitted variants are persisted to the event log, passed to `runWorkflow({ onProgress })`, streamed by Gateway, and filtered by `bunx smithers-orchestrator events --type <category>`. Reserved variants are documented here when they are typed but not currently emitted.

Every variant has `type`, `runId`, and `timestampMs`. Node-scoped variants add `nodeId` and `iteration`. Attempt-scoped variants add `attempt`.

For subscription examples, CLI usage, and built-in metrics, see [Events](/runtime/events).

## Status Types

```ts
type RunStatus =
  | "running"
  | "waiting-approval"
  | "waiting-event"
  | "waiting-timer"
  | "waiting-quota"
  | "paused"
  | "finished"
  | "continued"
  | "failed"
  | "cancelled";

type RunState =
  | "running"
  | "waiting-approval"
  | "waiting-event"
  | "waiting-timer"
  | "waiting-quota"
  | "paused"
  | "recovering"
  | "stale"
  | "orphaned"
  | "failed"
  | "cancelled"
  | "succeeded"
  | "unknown";
```

## Event Variants

Discriminate on the `type` field, e.g. `event.type === "NodeFailed"`, to handle specific lifecycle moments.

<Warning>
**In a browser UI the shape is different.** `useGatewayRunEvents` (and the vanilla
`streamRunEventsResilient`) yield **`GatewayEventFrame`** objects, not raw
`SmithersEvent`s. On a frame the event NAME is `frame.event` and every other field
lives under `frame.payload`. So the client-side equivalents are:

```tsx
const { events } = useGatewayRunEvents(runId, { afterSeq: 0 });
for (const frame of events) {
  if (frame.event === "NodeFailed") {
    const { nodeId, error } = frame.payload;   // NOT frame.type / frame.nodeId
    // error is the errorToJson value: read error.message
  }
}
```

Use `frame.event === "NodeStarted" | "NodeFinished" | "NodeFailed"` to build a
per-node status view, and `frame.payload.nodeId` to key it. The table below lists
each variant's fields; in a UI those fields are on `frame.payload`.
</Warning>

| Event | Top-level fields |
|---|---|
| `SupervisorStarted` | `runId`, `pollIntervalMs`, `staleThresholdMs`, `timestampMs` |
| `SupervisorPollCompleted` | `runId`, `staleCount`, `resumedCount`, `skippedCount`, `durationMs`, `timestampMs` |
| `RunAutoResumed` | `runId`, `lastHeartbeatAtMs`, `staleDurationMs`, `timestampMs` |
| `RunAutoResumeSkipped` | `runId`, `reason`, `timestampMs` |
| `RunStarted` | `runId`, `timestampMs` |
| `RunStatusChanged` | `runId`, `status`, `timestampMs` |
| `RunStateChanged` | `runId`, `before`, `after`, `timestampMs` |
| `RunFinished` | `runId`, `timestampMs` |
| `RunFailed` | `runId`, `error`, `timestampMs` |
| `RunCancelled` | `runId`, `timestampMs` |
| `RunContinuedAsNew` | `runId`, `newRunId`, `iteration`, `carriedStateSize`, `ancestryDepth?`, `timestampMs` |
| `RunHijackRequested` | `runId`, `target?`, `timestampMs` |
| `RunHijacked` | `runId`, `nodeId`, `iteration`, `attempt`, `engine`, `mode`, `resume?`, `cwd`, `timestampMs` |
| `SandboxCreated` | `runId`, `sandboxId`, `runtime`, `configJson`, `timestampMs` |
| `SandboxShipped` | `runId`, `sandboxId`, `runtime`, `bundleSizeBytes`, `timestampMs` |
| `SandboxHeartbeat` | `runId`, `sandboxId`, `remoteRunId?`, `progress?`, `timestampMs` |
| `SandboxBundleReceived` | `runId`, `sandboxId`, `bundleSizeBytes`, `patchCount`, `hasOutputs`, `timestampMs` |
| `SandboxCompleted` | `runId`, `sandboxId`, `remoteRunId?`, `runtime`, `status`, `durationMs`, `timestampMs` |
| `SandboxFailed` | `runId`, `sandboxId`, `runtime`, `error`, `timestampMs` |
| `SandboxDiffReviewRequested` | `runId`, `sandboxId`, `patchCount`, `totalDiffLines`, `timestampMs` |
| `SandboxDiffAccepted` | `runId`, `sandboxId`, `patchCount`, `timestampMs` |
| `SandboxDiffRejected` | `runId`, `sandboxId`, `reason?`, `timestampMs` |
| `FrameCommitted` | `runId`, `frameNo`, `xmlHash`, `timestampMs` |
| `NodePending` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `NodeStarted` | `runId`, `nodeId`, `iteration`, `attempt`, `timestampMs` |
| `TaskHeartbeat` | `runId`, `nodeId`, `iteration`, `attempt`, `hasData`, `dataSizeBytes`, `intervalMs?`, `timestampMs` |
| `TaskHeartbeatTimeout` | `runId`, `nodeId`, `iteration`, `attempt`, `lastHeartbeatAtMs`, `timeoutMs`, `timestampMs` |
| `NodeFinished` | `runId`, `nodeId`, `iteration`, `attempt`, `timestampMs` |
| `NodeFailed` | `runId`, `nodeId`, `iteration`, `attempt`, `error`, `timestampMs` |
| `NodeCancelled` | `runId`, `nodeId`, `iteration`, `attempt?`, `reason?`, `timestampMs` |
| `NodeSkipped` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `NodeRetrying` | `runId`, `nodeId`, `iteration`, `attempt`, `timestampMs` |
| `NodeWaitingApproval` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `NodeWaitingTimer` | `runId`, `nodeId`, `iteration`, `firesAtMs`, `timestampMs` |
| `ApprovalRequested` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `ApprovalGranted` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `ApprovalAutoApproved` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `ApprovalDenied` | `runId`, `nodeId`, `iteration`, `timestampMs` |
| `ToolCallStarted` | `runId`, `nodeId`, `iteration`, `attempt`, `toolCallId`, `toolName`, `seq`, `timestampMs` |
| `ToolCallFinished` | `runId`, `nodeId`, `iteration`, `attempt`, `toolCallId`, `toolName`, `seq`, `status`, `timestampMs` |
| `NodeOutput` | `runId`, `nodeId`, `iteration`, `attempt`, `text`, `stream`, `timestampMs` |
| `AgentEvent` | `runId`, `nodeId`, `iteration`, `attempt`, `engine`, `event`, `timestampMs` |
| `RetryTaskStarted` | `runId`, `nodeId`, `iteration`, `resetDependents`, `resetNodes`, `timestampMs` |
| `RetryTaskFinished` | `runId`, `nodeId`, `iteration`, `resetNodes`, `success`, `error?`, `timestampMs` |
| `RevertStarted` | `runId`, `nodeId`, `iteration`, `attempt`, `jjPointer`, `timestampMs` |
| `RevertFinished` | `runId`, `nodeId`, `iteration`, `attempt`, `jjPointer`, `success`, `error?`, `timestampMs` |
| `TimeTravelStarted` | `runId`, `nodeId`, `iteration`, `attempt`, `jjPointer?`, `timestampMs` |
| `TimeTravelFinished` | `runId`, `nodeId`, `iteration`, `attempt`, `jjPointer?`, `success`, `vcsRestored`, `resetNodes`, `error?`, `timestampMs` |
| `TimeTravelJumped` | `runId`, `fromFrameNo`, `toFrameNo`, `timestampMs`, `caller?` |
| `WorkflowReloadDetected` | `runId`, `changedFiles`, `timestampMs` |
| `WorkflowReloaded` | `runId`, `generation`, `changedFiles`, `timestampMs` |
| `WorkflowReloadFailed` | `runId`, `error`, `changedFiles`, `timestampMs` |
| `WorkflowReloadUnsafe` | `runId`, `reason`, `changedFiles`, `timestampMs` |
| `ScorerStarted` | `runId`, `nodeId`, `scorerId`, `scorerName`, `timestampMs` |
| `ScorerFinished` | `runId`, `nodeId`, `scorerId`, `scorerName`, `score`, `timestampMs` |
| `ScorerFailed` | `runId`, `nodeId`, `scorerId`, `scorerName`, `error`, `timestampMs` |
| `TokenUsageReported` | `runId`, `nodeId`, `iteration`, `attempt`, `model`, `agent`, `inputTokens`, `outputTokens`, `cacheReadTokens?`, `cacheWriteTokens?`, `reasoningTokens?`, `timestampMs` |
| `SnapshotCaptured` | `runId`, `frameNo`, `contentHash`, `timestampMs` |
| `RunForked` | `runId`, `parentRunId`, `parentFrameNo`, `branchLabel?`, `timestampMs` |
| `ReplayStarted` | `runId`, `parentRunId`, `parentFrameNo`, `restoreVcs`, `timestampMs` |
| `MemoryFactSet` | `runId`, `namespace`, `key`, `timestampMs` |
| `MemoryRecalled` | `runId`, `namespace`, `query`, `resultCount`, `timestampMs` |
| `MemoryMessageSaved` | `runId`, `threadId`, `role`, `timestampMs` |
| `OpenApiToolCalled` | `runId`, `operationId`, `method`, `path`, `durationMs`, `status`, `timestampMs` |
| `TimerCreated` | `runId`, `timerId`, `firesAtMs`, `timerType`, `timestampMs` |
| `TimerFired` | `runId`, `timerId`, `firesAtMs`, `firedAtMs`, `delayMs`, `timestampMs` |
| `TimerCancelled` | `runId`, `timerId`, `timestampMs` |
| `AgentTraceEvent` | `runId`, `nodeId`, `iteration`, `attempt`, `trace`, `timestampMs` |
| `AgentTraceSummary` | `runId`, `nodeId`, `iteration`, `attempt`, `summary`, `timestampMs` |
| `AgentSessionEvent` | `runId`, `nodeId`, `iteration`, `attempt`, `transcript`, `timestampMs` |

`RunStateChanged` is typed and categorized for forward compatibility, but the current runtime does not emit it. Read derived run state with `getRun` or `computeRunState`, and watch `RunStatusChanged` for persisted status transitions.

`OpenApiToolCalled` is typed and categorized for forward compatibility, but the current OpenAPI tool factory records Effect metrics and log spans rather than emitting that event onto the run event bus.

## Agent Event Payload

This type describes the `.event` field of the `AgentEvent` variant above. For structured traces, prefer `AgentTraceEvent`, `AgentTraceSummary`, and `AgentSessionEvent`.

`AgentEvent.event` is the normalized CLI-agent event payload:

```ts
type AgentCliEvent =
  | { type: "started"; engine: string; title: string; resume?: string; detail?: Record<string, unknown> }
  | {
      type: "action";
      engine: string;
      phase: "started" | "updated" | "completed";
      entryType?: "thought" | "message";
      action: { id: string; kind: AgentCliActionKind; title: string; detail?: Record<string, unknown> };
      message?: string;
      ok?: boolean;
      level?: "debug" | "info" | "warning" | "error";
    }
  | { type: "completed"; engine: string; ok: boolean; answer?: string; error?: string; resume?: string; usage?: Record<string, unknown> };
```
