Skip to main content
Smithers emits typed SmithersEvent objects throughout a run. Subscribe via onProgress in runWorkflow, or read persisted events from NDJSON log files. Events serve as the durable replay/audit log, correlate with structured logs through runId/nodeId/attempt, and drive built-in lifecycle counters. For OTLP export and Prometheus/Grafana setup, see Observability.

Subscribing

onProgress Callback

import { runWorkflow } from "smithers-orchestrator";
import workflow from "./workflow";

await runWorkflow(workflow, {
  input: { description: "Fix bug" },
  onProgress: (event) => {
    console.log(`[${event.type}] at ${event.timestampMs}`);

    if (event.type === "NodeStarted") {
      console.log(`  node: ${event.nodeId}, attempt: ${event.attempt}`);
    }

    if (event.type === "NodeFailed") {
      console.error(`  node: ${event.nodeId}, error:`, event.error);
    }
  },
});

NDJSON Log Files

Events are appended as JSON lines to:
.smithers/executions/<runId>/logs/stream.ndjson
# Watch events in real time
tail -f .smithers/executions/abc123/logs/stream.ndjson | jq .

# Filter for failures
cat .smithers/executions/abc123/logs/stream.ndjson | jq 'select(.type == "NodeFailed")'

# Count events by type
cat .smithers/executions/abc123/logs/stream.ndjson | jq -r .type | sort | uniq -c | sort -rn
Configure with logDir in runWorkflow or --log-dir / --no-log in the CLI.

Event-Driven Metrics

EventMetric
RunStartedsmithers.runs.total
NodeStartedsmithers.nodes.started
NodeFinishedsmithers.nodes.finished
NodeFailedsmithers.nodes.failed
Approval eventsApproval counters
trackSmithersEvent from smithers-orchestrator/observability exposes this mapping for custom integrations.

Common Fields

Every SmithersEvent:
FieldTypeDescription
typestringEvent type discriminator.
runIdstringRun this event belongs to.
timestampMsnumberUnix timestamp in milliseconds.
Node-scoped events add:
FieldTypeDescription
nodeIdstringTask node ID.
iterationnumberLoop iteration number.
Attempt-scoped events add:
FieldTypeDescription
attemptnumberAttempt number (starts at 1).

Event Types

Supervisor

SupervisorStarted

Emitted when the supervisor process starts polling for stale runs.
{
  type: "SupervisorStarted",
  runId: string,
  pollIntervalMs: number,
  staleThresholdMs: number,
  timestampMs: number,
}
pollIntervalMs: How often the supervisor checks for stale runs. staleThresholdMs: Age after which a run is considered stale.

SupervisorPollCompleted

Emitted after each supervisor poll cycle.
{
  type: "SupervisorPollCompleted",
  runId: string,
  staleCount: number,
  resumedCount: number,
  skippedCount: number,
  durationMs: number,
  timestampMs: number,
}
staleCount: Runs found to be stale. resumedCount: Runs successfully auto-resumed. skippedCount: Stale runs skipped (e.g. process still alive). durationMs: Wall time for this poll cycle.

Run Lifecycle

RunStarted

Emitted once at the beginning of every run (including resumes).
{ type: "RunStarted", runId: string, timestampMs: number }

RunStatusChanged

{ type: "RunStatusChanged", runId: string, status: RunStatus, timestampMs: number }
RunStatus: "running" | "waiting-approval" | "waiting-event" | "waiting-timer" | "finished" | "continued" | "failed" | "cancelled".

RunFinished

{ type: "RunFinished", runId: string, timestampMs: number }

RunFailed

{ type: "RunFailed", runId: string, error: unknown, timestampMs: number }

RunCancelled

{ type: "RunCancelled", runId: string, timestampMs: number }

RunAutoResumed

Emitted by the supervisor when a stale run is automatically restarted.
{
  type: "RunAutoResumed",
  runId: string,
  lastHeartbeatAtMs: number | null,
  staleDurationMs: number,
  timestampMs: number,
}
lastHeartbeatAtMs: Unix ms of the last recorded heartbeat, or null if no heartbeat was recorded. staleDurationMs: How long the run had been stale before resumption.

RunAutoResumeSkipped

Emitted when the supervisor decided not to resume a stale run.
{
  type: "RunAutoResumeSkipped",
  runId: string,
  reason: "pid-alive" | "missing-workflow" | "rate-limited",
  timestampMs: number,
}
reason: "pid-alive" — the original process is still running; "missing-workflow" — workflow file could not be located; "rate-limited" — resumption was throttled.

RunContinuedAsNew

Emitted when a long-running workflow continues as a fresh run, carrying forward state.
{
  type: "RunContinuedAsNew",
  runId: string,
  newRunId: string,
  iteration: number,
  carriedStateSize: number,
  ancestryDepth?: number,
  timestampMs: number,
}
newRunId: The run ID of the continuation. carriedStateSize: Byte size of the state passed to the new run. ancestryDepth: How many continuation hops have occurred (omitted on first continuation).

RunForked

Emitted when a run is forked from a parent run’s snapshot for time-travel or branching.
{
  type: "RunForked",
  runId: string,
  parentRunId: string,
  parentFrameNo: number,
  branchLabel?: string,
  timestampMs: number,
}
parentRunId: The run this fork originated from. parentFrameNo: Frame number in the parent run where the fork was taken. branchLabel: Optional human-readable label for the branch.

ReplayStarted

Emitted when a run begins replaying from a parent run’s snapshot.
{
  type: "ReplayStarted",
  runId: string,
  parentRunId: string,
  parentFrameNo: number,
  restoreVcs: boolean,
  timestampMs: number,
}
parentRunId: The run being replayed from. parentFrameNo: Snapshot frame to replay from. restoreVcs: Whether VCS state was restored as part of the replay.

Frame Events

FrameCommitted

Emitted each time the engine renders a new frame.
{
  type: "FrameCommitted",
  runId: string,
  frameNo: number,
  xmlHash: string,
  timestampMs: number,
}
xmlHash: SHA-256 hex digest of the canonicalized XML tree.

Snapshot

SnapshotCaptured

Emitted when the engine captures a point-in-time snapshot of the workflow frame, enabling time-travel and forking.
{
  type: "SnapshotCaptured",
  runId: string,
  frameNo: number,
  contentHash: string,
  timestampMs: number,
}
frameNo: The frame this snapshot was taken at. contentHash: Hash of the snapshot content, used to detect duplicate snapshots.

Node Lifecycle

NodePending

Task identified, waiting to be scheduled.
{ type: "NodePending", runId: string, nodeId: string, iteration: number, timestampMs: number }

NodeStarted

{
  type: "NodeStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  timestampMs: number,
}

NodeFinished

{
  type: "NodeFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  timestampMs: number,
}

NodeFailed

{
  type: "NodeFailed",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  error: unknown,
  timestampMs: number,
}

NodeCancelled

{
  type: "NodeCancelled",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt?: number,
  reason?: string,
  timestampMs: number,
}
reason may be "unmounted" if the task disappeared from the tree after re-render.

NodeSkipped

{ type: "NodeSkipped", runId: string, nodeId: string, iteration: number, timestampMs: number }

NodeRetrying

Fires before the next attempt starts.
{
  type: "NodeRetrying",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  timestampMs: number,
}
attempt is the upcoming attempt number.

NodeWaitingApproval

{
  type: "NodeWaitingApproval",
  runId: string,
  nodeId: string,
  iteration: number,
  timestampMs: number,
}

NodeWaitingTimer

Emitted when a node is suspended waiting for a timer to fire.
{
  type: "NodeWaitingTimer",
  runId: string,
  nodeId: string,
  iteration: number,
  firesAtMs: number,
  timestampMs: number,
}
firesAtMs: Unix ms when the timer is scheduled to fire.

Approval Events

ApprovalRequested

{
  type: "ApprovalRequested",
  runId: string,
  nodeId: string,
  iteration: number,
  timestampMs: number,
}

ApprovalGranted

{
  type: "ApprovalGranted",
  runId: string,
  nodeId: string,
  iteration: number,
  timestampMs: number,
}

ApprovalAutoApproved

Emitted when an approval is granted automatically by a configured policy without human intervention.
{
  type: "ApprovalAutoApproved",
  runId: string,
  nodeId: string,
  iteration: number,
  timestampMs: number,
}

ApprovalDenied

{
  type: "ApprovalDenied",
  runId: string,
  nodeId: string,
  iteration: number,
  timestampMs: number,
}

Tool Events

ToolCallStarted

{
  type: "ToolCallStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  toolName: string,
  seq: number,
  timestampMs: number,
}
seq: sequential counter for tool calls within the attempt.

ToolCallFinished

{
  type: "ToolCallFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  toolName: string,
  seq: number,
  status: "success" | "error",
  timestampMs: number,
}

Output Events

NodeOutput

Streaming text from an agent.
{
  type: "NodeOutput",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  text: string,
  stream: "stdout" | "stderr",
  timestampMs: number,
}

Timer Events

TimerCreated

Emitted when a durable timer is registered with the engine.
{
  type: "TimerCreated",
  runId: string,
  timerId: string,
  firesAtMs: number,
  timerType: "duration" | "absolute",
  timestampMs: number,
}
timerId: Stable identifier for this timer. firesAtMs: Unix ms when the timer will fire. timerType: "duration" — created from a relative delay; "absolute" — created from a specific wall-clock time.

TimerFired

Emitted when a timer fires and resumes its waiting node.
{
  type: "TimerFired",
  runId: string,
  timerId: string,
  firesAtMs: number,
  firedAtMs: number,
  delayMs: number,
  timestampMs: number,
}
firesAtMs: Scheduled fire time. firedAtMs: Actual fire time. delayMs: Difference between actual and scheduled fire time; non-zero indicates scheduler lag.

TimerCancelled

Emitted when a timer is cancelled before it fires.
{
  type: "TimerCancelled",
  runId: string,
  timerId: string,
  timestampMs: number,
}

Task Heartbeat Events

TaskHeartbeat

Emitted periodically by long-running tasks to signal they are still alive.
{
  type: "TaskHeartbeat",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  hasData: boolean,
  dataSizeBytes: number,
  intervalMs?: number,
  timestampMs: number,
}
hasData: Whether the heartbeat carries a checkpoint payload. dataSizeBytes: Byte size of any checkpoint data. intervalMs: Configured heartbeat interval, if set.

TaskHeartbeatTimeout

Emitted when a task fails to send a heartbeat within its configured timeout window.
{
  type: "TaskHeartbeatTimeout",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  lastHeartbeatAtMs: number,
  timeoutMs: number,
  timestampMs: number,
}
lastHeartbeatAtMs: Unix ms of the last heartbeat received before timeout. timeoutMs: The configured timeout duration.

Sandbox Events

SandboxCreated

Emitted when a sandboxed execution environment is provisioned.
{
  type: "SandboxCreated",
  runId: string,
  sandboxId: string,
  runtime: "bubblewrap" | "docker" | "codeplane",
  configJson: string,
  timestampMs: number,
}
sandboxId: Unique identifier for this sandbox instance. runtime: The isolation backend used. configJson: JSON-serialized sandbox configuration.

SandboxShipped

Emitted when the initial code bundle has been uploaded to the sandbox.
{
  type: "SandboxShipped",
  runId: string,
  sandboxId: string,
  runtime: "bubblewrap" | "docker" | "codeplane",
  bundleSizeBytes: number,
  timestampMs: number,
}
bundleSizeBytes: Size of the uploaded bundle in bytes.

SandboxHeartbeat

Emitted periodically while a sandbox is executing to indicate liveness.
{
  type: "SandboxHeartbeat",
  runId: string,
  sandboxId: string,
  remoteRunId?: string,
  progress?: number,
  timestampMs: number,
}
remoteRunId: Run ID assigned by the remote sandbox environment, if available. progress: Optional 0–1 progress fraction reported by the sandbox.

SandboxBundleReceived

Emitted when the sandbox returns an output bundle to the orchestrator.
{
  type: "SandboxBundleReceived",
  runId: string,
  sandboxId: string,
  bundleSizeBytes: number,
  patchCount: number,
  hasOutputs: boolean,
  timestampMs: number,
}
bundleSizeBytes: Size of the received bundle. patchCount: Number of file patches included in the bundle. hasOutputs: Whether structured task outputs were included.

SandboxCompleted

Emitted when a sandbox execution finishes (regardless of outcome).
{
  type: "SandboxCompleted",
  runId: string,
  sandboxId: string,
  remoteRunId?: string,
  runtime: "bubblewrap" | "docker" | "codeplane",
  status: "finished" | "failed" | "cancelled",
  durationMs: number,
  timestampMs: number,
}
status: Final execution status. durationMs: Total sandbox execution time.

SandboxFailed

Emitted when a sandbox encounters an unrecoverable error.
{
  type: "SandboxFailed",
  runId: string,
  sandboxId: string,
  runtime: "bubblewrap" | "docker" | "codeplane",
  error: unknown,
  timestampMs: number,
}

SandboxDiffReviewRequested

Emitted when a sandbox produces patches that require human review before being applied.
{
  type: "SandboxDiffReviewRequested",
  runId: string,
  sandboxId: string,
  patchCount: number,
  totalDiffLines: number,
  timestampMs: number,
}
patchCount: Number of patches awaiting review. totalDiffLines: Total lines across all diffs.

SandboxDiffAccepted

Emitted when a human reviewer accepts the sandbox’s proposed patches.
{
  type: "SandboxDiffAccepted",
  runId: string,
  sandboxId: string,
  patchCount: number,
  timestampMs: number,
}

SandboxDiffRejected

Emitted when a human reviewer rejects the sandbox’s proposed patches.
{
  type: "SandboxDiffRejected",
  runId: string,
  sandboxId: string,
  reason?: string,
  timestampMs: number,
}
reason: Optional explanation for the rejection.

Revert Events

RevertStarted

{
  type: "RevertStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  jjPointer: string,
  timestampMs: number,
}

RevertFinished

{
  type: "RevertFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  jjPointer: string,
  success: boolean,
  error?: string,
  timestampMs: number,
}

Retry / Time-Travel Events

RetryTaskStarted

Emitted when a manual or programmatic retry is initiated for a specific task node.
{
  type: "RetryTaskStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  resetDependents: boolean,
  resetNodes: string[],
  timestampMs: number,
}
resetDependents: Whether nodes that depend on this task are also being reset. resetNodes: Full list of node IDs being cleared as part of this retry.

RetryTaskFinished

Emitted when the retry operation completes.
{
  type: "RetryTaskFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  resetNodes: string[],
  success: boolean,
  error?: string,
  timestampMs: number,
}
resetNodes: Node IDs that were actually reset. error: Set if the retry operation itself failed (not the retried task).

TimeTravelStarted

Emitted when a time-travel operation begins, rewinding the run to a prior state.
{
  type: "TimeTravelStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  jjPointer?: string,
  timestampMs: number,
}
jjPointer: VCS change identifier to restore to, if VCS state is being rewound.

TimeTravelFinished

Emitted when the time-travel operation completes.
{
  type: "TimeTravelFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  attempt: number,
  jjPointer?: string,
  success: boolean,
  vcsRestored: boolean,
  resetNodes: string[],
  error?: string,
  timestampMs: number,
}
vcsRestored: Whether VCS state was successfully rewound. resetNodes: Node IDs that were cleared as part of the rewind. error: Set if time-travel failed.

Voice Events

VoiceStarted

Emitted when a voice operation begins.
{
  type: "VoiceStarted",
  runId: string,
  nodeId: string,
  iteration: number,
  operation: "speak" | "listen",
  provider: string,
  timestampMs: number,
}
operation: "speak" for text-to-speech; "listen" for speech-to-text. provider: The voice provider in use (e.g. "openai", "elevenlabs").

VoiceFinished

Emitted when a voice operation completes successfully.
{
  type: "VoiceFinished",
  runId: string,
  nodeId: string,
  iteration: number,
  operation: "speak" | "listen",
  provider: string,
  durationMs: number,
  timestampMs: number,
}
durationMs: Wall time for the voice operation.

VoiceError

Emitted when a voice operation fails.
{
  type: "VoiceError",
  runId: string,
  nodeId: string,
  iteration: number,
  operation: "speak" | "listen",
  provider: string,
  error: unknown,
  timestampMs: number,
}

RAG Events

RagIngested

Emitted after documents are chunked and embedded into a vector store namespace.
{
  type: "RagIngested",
  runId: string,
  documentCount: number,
  chunkCount: number,
  namespace: string,
  timestampMs: number,
}
documentCount: Number of source documents ingested. chunkCount: Number of chunks stored after splitting. namespace: The vector store namespace written to.

RagRetrieved

Emitted after a semantic search query completes.
{
  type: "RagRetrieved",
  runId: string,
  query: string,
  resultCount: number,
  namespace: string,
  topScore: number,
  timestampMs: number,
}
query: The query string submitted. resultCount: Number of chunks returned. topScore: Similarity score of the highest-ranked result.

Memory Events

MemoryFactSet

Emitted when a key-value fact is written to the memory store.
{
  type: "MemoryFactSet",
  runId: string,
  namespace: string,
  key: string,
  timestampMs: number,
}
namespace: Memory namespace the fact belongs to. key: Key under which the fact was stored.

MemoryRecalled

Emitted when the memory store is queried for relevant facts.
{
  type: "MemoryRecalled",
  runId: string,
  namespace: string,
  query: string,
  resultCount: number,
  timestampMs: number,
}
query: The recall query. resultCount: Number of facts returned.

MemoryMessageSaved

Emitted when a conversation message is persisted to memory.
{
  type: "MemoryMessageSaved",
  runId: string,
  threadId: string,
  role: string,
  timestampMs: number,
}
threadId: Identifier of the conversation thread. role: Message role (e.g. "user", "assistant").

OpenAPI Events

OpenApiToolCalled

Emitted when a generated OpenAPI tool executes an HTTP operation.
{
  type: "OpenApiToolCalled",
  runId: string,
  operationId: string,
  method: string,
  path: string,
  durationMs: number,
  status: "success" | "error",
  timestampMs: number,
}
operationId: The OpenAPI operationId of the called operation. method: HTTP method (e.g. "GET", "POST"). path: URL path template. durationMs: Round-trip duration. status: Whether the HTTP call succeeded or errored.

Hot Reload

WorkflowReloadDetected

{
  type: "WorkflowReloadDetected",
  runId: string,
  changedFiles: string[],
  timestampMs: number
}

WorkflowReloaded

{
  type: "WorkflowReloaded",
  runId: string,
  generation: number,
  changedFiles: string[],
  timestampMs: number
}
generation: monotonically increasing reload counter.

WorkflowReloadFailed

{
  type: "WorkflowReloadFailed",
  runId: string,
  error: unknown,
  changedFiles: string[],
  timestampMs: number
}
The engine continues with the previous valid code.

WorkflowReloadUnsafe

{
  type: "WorkflowReloadUnsafe",
  runId: string,
  reason: string,
  changedFiles: string[],
  timestampMs: number
}
Schema changes require a process restart.

Scorer Events

ScorerStarted

Emitted when a scorer begins evaluating a task’s output.
{
  type: "ScorerStarted",
  runId: string,
  nodeId: string,
  scorerId: string,
  scorerName: string,
  timestampMs: number,
}
scorerId: Unique identifier of the scorer. scorerName: Human-readable scorer name.

ScorerFinished

Emitted when a scorer completes successfully.
{
  type: "ScorerFinished",
  runId: string,
  nodeId: string,
  scorerId: string,
  scorerName: string,
  score: number,
  timestampMs: number,
}
score: The 0–1 normalized score produced by the scorer.

ScorerFailed

Emitted when a scorer throws an error during evaluation.
{
  type: "ScorerFailed",
  runId: string,
  nodeId: string,
  scorerId: string,
  scorerName: string,
  error: unknown,
  timestampMs: number,
}
error: The error thrown by the scorer. Scorer failures never fail the parent task — they are logged and the workflow continues. See Evals & Scorers for the full scoring system documentation.

Quick Reference

Event TypeSectionExtra Fields
SupervisorStartedSupervisorpollIntervalMs, staleThresholdMs
SupervisorPollCompletedSupervisorstaleCount, resumedCount, skippedCount, durationMs
RunStartedRun Lifecycle
RunStatusChangedRun Lifecyclestatus
RunFinishedRun Lifecycle
RunFailedRun Lifecycleerror
RunCancelledRun Lifecycle
RunAutoResumedRun LifecyclelastHeartbeatAtMs, staleDurationMs
RunAutoResumeSkippedRun Lifecyclereason
RunContinuedAsNewRun LifecyclenewRunId, iteration, carriedStateSize, ancestryDepth?
RunForkedRun LifecycleparentRunId, parentFrameNo, branchLabel?
ReplayStartedRun LifecycleparentRunId, parentFrameNo, restoreVcs
FrameCommittedFrame EventsframeNo, xmlHash
SnapshotCapturedSnapshotframeNo, contentHash
NodePendingNode LifecyclenodeId, iteration
NodeStartedNode LifecyclenodeId, iteration, attempt
NodeFinishedNode LifecyclenodeId, iteration, attempt
NodeFailedNode LifecyclenodeId, iteration, attempt, error
NodeCancelledNode LifecyclenodeId, iteration, attempt?, reason?
NodeSkippedNode LifecyclenodeId, iteration
NodeRetryingNode LifecyclenodeId, iteration, attempt
NodeWaitingApprovalNode LifecyclenodeId, iteration
NodeWaitingTimerNode LifecyclenodeId, iteration, firesAtMs
ApprovalRequestedApprovalnodeId, iteration
ApprovalGrantedApprovalnodeId, iteration
ApprovalAutoApprovedApprovalnodeId, iteration
ApprovalDeniedApprovalnodeId, iteration
ToolCallStartedToolnodeId, iteration, attempt, toolName, seq
ToolCallFinishedToolnodeId, iteration, attempt, toolName, seq, status
NodeOutputOutputnodeId, iteration, attempt, text, stream
TimerCreatedTimertimerId, firesAtMs, timerType
TimerFiredTimertimerId, firesAtMs, firedAtMs, delayMs
TimerCancelledTimertimerId
TaskHeartbeatTask HeartbeatnodeId, iteration, attempt, hasData, dataSizeBytes, intervalMs?
TaskHeartbeatTimeoutTask HeartbeatnodeId, iteration, attempt, lastHeartbeatAtMs, timeoutMs
SandboxCreatedSandboxsandboxId, runtime, configJson
SandboxShippedSandboxsandboxId, runtime, bundleSizeBytes
SandboxHeartbeatSandboxsandboxId, remoteRunId?, progress?
SandboxBundleReceivedSandboxsandboxId, bundleSizeBytes, patchCount, hasOutputs
SandboxCompletedSandboxsandboxId, remoteRunId?, runtime, status, durationMs
SandboxFailedSandboxsandboxId, runtime, error
SandboxDiffReviewRequestedSandboxsandboxId, patchCount, totalDiffLines
SandboxDiffAcceptedSandboxsandboxId, patchCount
SandboxDiffRejectedSandboxsandboxId, reason?
RevertStartedRevertnodeId, iteration, attempt, jjPointer
RevertFinishedRevertnodeId, iteration, attempt, jjPointer, success, error?
RetryTaskStartedRetry / Time-TravelnodeId, iteration, resetDependents, resetNodes
RetryTaskFinishedRetry / Time-TravelnodeId, iteration, resetNodes, success, error?
TimeTravelStartedRetry / Time-TravelnodeId, iteration, attempt, jjPointer?
TimeTravelFinishedRetry / Time-TravelnodeId, iteration, attempt, jjPointer?, success, vcsRestored, resetNodes, error?
VoiceStartedVoicenodeId, iteration, operation, provider
VoiceFinishedVoicenodeId, iteration, operation, provider, durationMs
VoiceErrorVoicenodeId, iteration, operation, provider, error
RagIngestedRAGdocumentCount, chunkCount, namespace
RagRetrievedRAGquery, resultCount, namespace, topScore
MemoryFactSetMemorynamespace, key
MemoryRecalledMemorynamespace, query, resultCount
MemoryMessageSavedMemorythreadId, role
OpenApiToolCalledOpenAPIoperationId, method, path, durationMs, status
AgentEventOutputnodeId, iteration, attempt, engine, event
WorkflowReloadDetectedHot ReloadchangedFiles
WorkflowReloadedHot Reloadgeneration, changedFiles
WorkflowReloadFailedHot Reloaderror, changedFiles
WorkflowReloadUnsafeHot Reloadreason, changedFiles
RunHijackRequestedRun Lifecycletarget?
RunHijackedRun LifecyclenodeId, iteration, attempt, engine, mode, resume?, cwd
ScorerStartedScorernodeId, scorerId, scorerName
ScorerFinishedScorernodeId, scorerId, scorerName, score
ScorerFailedScorernodeId, scorerId, scorerName, error
TokenUsageReportedOutputnodeId, iteration, attempt, model, agent, inputTokens, outputTokens, cacheReadTokens?, cacheWriteTokens?, reasoningTokens?
AlertFiredAlert LifecyclealertId, policyName, severity, fingerprint, nodeId?, iteration?, message
AlertAcknowledgedAlert LifecyclealertId, acknowledgedBy?
AlertSilencedAlert LifecyclealertId, silencedUntilMs?
AlertResolvedAlert LifecyclealertId, resolvedBy?
AlertReopenedAlert LifecyclealertId, fingerprint, occurrenceCount
AlertEscalatedAlert LifecyclealertId, fromSeverity, toSeverity
BudgetExceededNormalized InputnodeId, iteration, budgetType, limit, actual
ProviderDisconnectedNormalized InputnodeId, iteration, attempt, engine
ProviderReconnectedNormalized InputnodeId, iteration, attempt, engine

Persistence

Events are persisted in two places:
  1. SQLite_smithers_events table with sequential seq number. Source of truth.
  2. NDJSONstream.ndjson in the run’s log directory. Best-effort.
Both are asynchronous. onProgress fires synchronously before persistence.