---
title: Events
description: Subscribe to lifecycle events. Full event union lives in Types.
---

`SmithersEvent` is the discriminated union of lifecycle events the runtime and observability layer understand. Most variants are emitted by the runtime; reserved variants are called out in [Event Types](/reference/event-types). The full type definition lives in [Types](/reference/types); that's the source of truth for field shapes.

## Subscribe via `onProgress`

```ts
import { runWorkflow } from "smithers-orchestrator";
import { Effect } from "effect";
import workflow from "./workflow";

await Effect.runPromise(runWorkflow(workflow, {
  input: { task: "fix bug" },
  onProgress: (event) => {
    if (event.type === "NodeStarted")  console.log(`▶ ${event.nodeId} (attempt ${event.attempt})`);
    if (event.type === "NodeFinished") console.log(`✓ ${event.nodeId}`);
    if (event.type === "NodeFailed")   console.error(`✗ ${event.nodeId}`, event.error);
  },
}));
```

## Read from the NDJSON log

Events append to `.smithers/executions/<runId>/logs/stream.ndjson` (configure with `logDir` / `--log-dir`; disable with `--no-log`).

```bash
tail -f .smithers/executions/<runId>/logs/stream.ndjson | jq .
jq 'select(.type == "NodeFailed")' .smithers/executions/<runId>/logs/stream.ndjson
jq -r .type .smithers/executions/<runId>/logs/stream.ndjson | sort | uniq -c | sort -rn
```

Or with the CLI:

```bash
bunx smithers-orchestrator events RUN_ID --json
bunx smithers-orchestrator events RUN_ID --type tool-call --node analyze
```

## Common fields

```ts
type CommonFields    = { type: string; runId: string; timestampMs: number };
type NodeScoped      = CommonFields & { nodeId: string; iteration: number };
type AttemptScoped   = NodeScoped   & { attempt: number };
```

Every event includes `type`, `runId`, `timestampMs`. Node-scoped events add `nodeId` and `iteration`. Attempt-scoped add `attempt`.

## Event categories

Used by `bunx smithers-orchestrator events --type <category>` and the metrics layer.

| Category | Events |
|---|---|
| `run` | RunAutoResumed, RunAutoResumeSkipped, RunStarted, RunStatusChanged, RunStateChanged, RunFinished, RunFailed, RunCancelled, RunContinuedAsNew, RunHijackRequested, RunHijacked, RetryTaskStarted, RetryTaskFinished, RunForked, ReplayStarted |
| `frame` | FrameCommitted |
| `node` | NodePending, NodeStarted, TaskHeartbeat, TaskHeartbeatTimeout, NodeFinished, NodeFailed, NodeCancelled, NodeSkipped, NodeRetrying, NodeWaitingApproval, NodeWaitingTimer |
| `approval` | ApprovalRequested, ApprovalGranted, ApprovalAutoApproved, ApprovalDenied |
| `tool-call` | ToolCallStarted, ToolCallFinished |
| `agent` | AgentEvent, AgentTraceEvent, AgentTraceSummary, AgentSessionEvent |
| `output` | NodeOutput |
| `revert` | RevertStarted, RevertFinished, TimeTravelStarted, TimeTravelFinished, TimeTravelJumped |
| `workflow` | WorkflowReloadDetected, WorkflowReloaded, WorkflowReloadFailed, WorkflowReloadUnsafe |
| `scorer` | ScorerStarted, ScorerFinished, ScorerFailed |
| `token` | TokenUsageReported |
| `timer` | TimerCreated, TimerFired, TimerCancelled |
| `memory` | MemoryFactSet, MemoryRecalled, MemoryMessageSaved |
| `openapi` | OpenApiToolCalled |
| `sandbox` | SandboxCreated, SandboxShipped, SandboxHeartbeat, SandboxBundleReceived, SandboxCompleted, SandboxFailed, SandboxDiffReviewRequested, SandboxDiffAccepted, SandboxDiffRejected |
| `snapshot` | SnapshotCaptured |
| `supervisor` | SupervisorStarted, SupervisorPollCompleted |

`RunStateChanged` is categorized as `run` because the type is part of the public event union, but the current runtime does not emit it. Use `RunStatusChanged` from the stream plus `getRun` / `computeRunState` for derived run state.

`OpenApiToolCalled` is categorized as `openapi` 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.

## Built-in metrics

| Event | Metric |
|---|---|
| `RunStarted` | `smithers.runs.total` |
| `NodeStarted` | `smithers.nodes.started` |
| `NodeFinished` | `smithers.nodes.finished` |
| `NodeFailed` | `smithers.nodes.failed` |
| `ApprovalGranted` / `ApprovalDenied` | Approval counters |
| `TokenUsageReported` | Token usage counters per model/agent |

`trackSmithersEvent` from `smithers-orchestrator/observability` exposes this mapping for custom integrations. See [Observability](/guides/monitoring-logs) for the full OTLP/Prometheus setup.
