/** * Workflow run events: what changed on a run, derived from what was persisted. * * A caller who wants to react to a run, such as a dashboard, CI job, or operator * console, has had one option: poll `GET /runs/:id` on a timer. Polling picks * a latency floor and pays for it whether or not anything happened, and it * cannot report a transition that started and finished inside one interval. * * The events here are **derived, not emitted**. Nothing in the executor calls * into this module. A backend observation supplies every persisted transition * in revision order. Deriving from persisted state rather than from * in-process callbacks means an event is only reported once the change it * describes actually survived, and a run driven by a worker in another process * is observed on the same terms as a local one. * * `StepExecutorConfig` does carry `onStepStart`/`onStepComplete` hooks, which * look like the obvious source. They are deliberately not used: they fire * in-process before persistence, so a crash between hook and write would * report a step that never happened, and a run executing elsewhere emits * nothing at all. * * ## Granularity is bounded by what the engine persists * * Before a top-level node batch executes, the engine persists every node in the * batch as `running`. It persists the settled batch again before dependents can * run or the graph can return. A sequential workflow therefore reports a * distinct `step.started` and terminal step event for each node. * * Parallel nodes start together and settle after the full batch has joined, * because their context patches must be merged deterministically before the * durable root state advances. Synthetic child graphs are never written over * the root run. A top-level composite reports its own start and settled state; * its child detail becomes durable when the composite parks or settles. * * @module workflow/events */ import type { NodeState, WorkflowRun, WorkflowStatus } from "./types.js"; import type { WorkflowRunObservation } from "./backends/types.js"; /** Whether a run in this status can still produce events. */ export declare function isTerminalRunStatus(status: WorkflowStatus): boolean; /** A step began executing. */ export interface WorkflowStepStartedEvent { type: "step.started"; runId: string; nodeId: string; attempt: number; } /** A step finished successfully. */ export interface WorkflowStepCompletedEvent { type: "step.completed"; runId: string; nodeId: string; attempt: number; } /** A step failed. `error` is the persisted message, absent when none was set. */ export interface WorkflowStepFailedEvent { type: "step.failed"; runId: string; nodeId: string; attempt: number; error?: string; } /** A step was skipped, typically by an unmet branch condition. */ export interface WorkflowStepSkippedEvent { type: "step.skipped"; runId: string; nodeId: string; } /** The run as a whole moved to a new status. */ export interface WorkflowRunStatusEvent { type: "run.status"; runId: string; status: WorkflowStatus; /** Set only for a run that reached `failed`. */ error?: string; } /** * A pending approval was persisted; the run is parked until it is decided. * * The run flips to `waiting` before the approval exists, so a subscriber who * reacts to `run.status: "waiting"` with a fetch can race the approval write. * This event names the approval directly and removes that second fetch. */ export interface WorkflowApprovalPendingEvent { type: "approval.pending"; runId: string; approvalId: string; nodeId: string; /** The persisted request message, absent when none was recorded. */ message?: string; } /** * A persisted workflow transition suitable for streaming to run observers. * * Events contain identifiers, statuses, attempts, persisted error messages, * and approval request messages only. Workflow inputs, outputs, approval * payloads, context, and tenant metadata are never part of this stream. */ export type WorkflowRunEvent = WorkflowStepStartedEvent | WorkflowStepCompletedEvent | WorkflowStepFailedEvent | WorkflowStepSkippedEvent | WorkflowRunStatusEvent | WorkflowApprovalPendingEvent; /** * The slice of a run this module diffs against. * * Deliberately not the whole `WorkflowRun`: holding one per subscriber would * retain every step's input and output for the life of a connection, and a * workflow that passes large payloads between steps would make an idle * subscriber expensive. Status, per-node status/attempt, and per-approval * identifiers are all a diff needs; approval payloads stay out for the same * reason step payloads do. */ export interface RunEventSnapshot { status: WorkflowStatus; nodes: Record; /** * Pending approvals by id. Absent means the producer did not observe * approvals; the diff then treats them as unchanged, never as revoked. */ approvals?: Record; } /** Reduce a run to the state {@linkcode deriveRunEvents} compares. */ export declare function snapshotRun(run: Pick & Partial>): RunEventSnapshot; /** * Events describing how a run got from `previous` to `next`. * * A retry is a real transition even though the status repeats: a node going * `failed` → `running` on attempt 2 reads identically to attempt 1 unless * `attempt` is compared too, so both are part of a node's identity here. * * Step events precede the run-status event when both are present, because a * `completed` run whose last step is still reported as running is a state no * consumer should ever have to render. */ export declare function deriveRunEvents(runId: string, previous: RunEventSnapshot | undefined, next: RunEventSnapshot, runError?: string, nodeErrors?: Record): WorkflowRunEvent[]; /** Subscriber-local event stream derived from one atomic backend observation. */ export interface WorkflowRunEventObservation { initial: WorkflowRun; events: AsyncIterable; close(): Promise; } /** Derive public events from a backend observation without shared baselines. */ export declare function deriveWorkflowRunEventObservation(observation: WorkflowRunObservation): WorkflowRunEventObservation; //# sourceMappingURL=events.d.ts.map