The compiled FlowChart returned by flowChart(...).build()
OptionalfactoryOrOptions: ScopeFactory<TScope> | FlowChartExecutorOptions<TScope>
A ScopeFactory<TScope> OR a FlowChartExecutorOptions<TScope> options object.
Attach a recorder to every channel (scope, flow, emit) it has OWN on*
methods for — preferred over single-channel calls, where forgetting one
silently loses events. Idempotent by id across all channels; a flow
method enables the narrative; delivery comes from the options or the
recorder's own field.
Optionaloptions: AttachRecorderOptionsAttach an EmitRecorder for scope.$emit(name, payload) events. It rides
the scope list, so onEmit fires exactly once per event. Idempotent by id.
Optionaloptions: AttachRecorderOptionsAttach a FlowRecorder to observe control flow events. Automatically
enables narrative. Must be called before run(). Idempotent by id;
{ delivery: 'deferred' } as for attachScopeRecorder.
Optionaloptions: AttachRecorderOptionsAttach a ScopeRecorder to observe data operations (reads, writes, commits)
on every stage scope. Call before run(). Idempotent by id (replaced,
never duplicated). { delivery: 'deferred' } moves it off the hot path —
delivered at the next microtask checkpoint; re-attaching an id on the
other tier SWAPS tiers (docs/guides/observers-deferred.md).
Optionaloptions: AttachRecorderOptionsDetach a child flowchart on the given driver and DISCARD the handle — for
fire-and-forget side effects. A child's error lands on the discarded handle;
for observable detach prefer detachAndJoinLater + .wait().catch().
Optionalinput: unknownDetach a child flowchart on the given driver and return a DetachHandle
the caller can wait() on (Promise) or read .status from (sync). The
driver is REQUIRED — there is no library default.
Optionalinput: unknownDetach a combined recorder from every channel it was attached to (safe if never attached).
Detach an EmitRecorder by id. Safe to call if never attached.
Detach all FlowRecorders with the given ID — both delivery tiers.
Detach all scope Recorders with the given ID — both delivery tiers.
Flush the deferred-observer backlog, then await async listener completions
under a deadline — call before a serverless process freezes or exits.
Zeros when no deferred observer was attached; pending > 0 reports what
was still outstanding at the deadline. timeoutMs defaults to 30 000;
Infinity waits for every listener and arms no timer, and a limit past
2^31 − 1 ms is waited in full (timers/longTimeout.ts).
Optionalopts: { timeoutMs?: number }Optionaloptions: CombinedNarrativeRecorderOptionsFlush the record sink (10.1, P6): await the adapter's flush() — for an executor built with
sink: { adapter, flush: 'manual' }, or before a serverless process freezes. timeoutMs bounds the wait; a
flush still running then is pending (and keeps running). Without timeoutMs it waits until the flush settles,
with no limit (unlike drainObservers, whose default is 30 000). A given limit follows the one long-timer law:
Infinity waits for the flush and arms no timer, a limit past 2^31 − 1 ms is waited in full, and a value that
is not a number > 0 (0, NaN, a negative, -Infinity) gives up at once. A failed flush is counted on
getSnapshot().sink.
Done at once without a sink, or with an adapter that has no flush.
Optionalopts: { timeoutMs?: number }Returns the checkpoint from the most recent paused execution, or undefined
if the last run completed without pausing.
JSON-serializable (store it in Redis, Postgres, localStorage…) and fully
DETACHED from engine state (checkpoint.ts): mutating or persisting it
cannot affect the executor, nor a later resume a checkpoint you stored.
LEAN: what resume() reads and the pause's own record — its size is the
state's, never the run's length. The run's history (execution tree,
commit log, subflow results) is getSnapshot's.
The opted-in immutable recorded native prefix. Ordinary snapshots remain live observations. A closed failed prefix stays failed; channel coverage, outside work and semantic causes are separate.
Number of commits in the ROOT log now. O(1), no snapshot materialization. Not an emitted event's source position: a deferred callback runs later and a subflow owns another log. Use EmitEvent.sourcePosition for that.
Returns 0 before any run. A fresh run resets the log; a same-executor
resume continues it. A fresh-executor resume starts a new local log.
Always equals getSnapshot().commitLog.length (the log is the
EventLog's executionHistory; the snapshot serves a frozen copy).
Returns a defensive copy of attached recorders (both tiers) that implement onEmit.
Safe native context for the latest rejected leg; undefined before, during, after success or pause.
Returns a defensive copy of attached FlowRecorders (both tiers).
The executor-owned narrative view; .map(e => e.text) for flat strings.
enableNarrative(options) configures this view. Attaching a flow recorder
also enables it, but that recorder's options and entries remain independent
(read an attached narrative() instance with recorder.getEntries()).
Same-executor resume retains history; a fresh executor records its resumed
leg only. The internal narrator is not an attached snapshot recorder row.
Returns a compliance-friendly report of all redaction activity from the most recent run. Never includes actual values.
Returns a defensive copy of attached scope Recorders (both tiers).
Returns the runtime snapshot.
Optionaloptions: { redact?: boolean }
Optionalredact?: booleantrue serves sharedState (and each subflow's
globalContext) from the redacted mirror — the safe view to export. A
no-op without a redaction policy. The commit log is redacted at write
time regardless. Default false.
Treat sharedState as READ-ONLY. In production it is a live view of
engine memory; in dev mode a deep-frozen clone, so a mutation throws.
Returns the subflow manifest from an attached ManifestFlowRecorder. Returns empty array if no ManifestFlowRecorder is attached.
Returns the full spec for a dynamically-registered subflow. Requires an attached ManifestFlowRecorder that observed the registration.
Returns true if the most recent run() was paused (checkpoint available).
Resume a paused flowchart from a checkpoint.
Restores the scope state, runs the paused stage's resumeFn with the input
(an interrupt() pause re-runs the stage with the answer), then continues
with whatever ran after that stage on the run — its next, or its
dispatcher's continuation, at every level of the pause path. Parallel
siblings that paused together (checkpoint.pendingPauses) are asked in
turn; the join runs after the last answer. See ResumeEntry.
The checkpoint comes from getCheckpoint() or from storage. On the SAME
executor, narrative/recorder state accumulates across the pause; on a
FRESH executor it starts empty. A fresh runId either way.
OptionalresumeInput: unknownOptionaloptions: ResumeOptions// Process A — after a pause, persist the checkpoint:
const checkpoint = executor.getCheckpoint()!;
await redis.set(`session:${id}`, JSON.stringify(checkpoint));
// Process B (possibly different server, same chart) — restore and resume:
const restored = JSON.parse(await redis.get(`session:${id}`));
const executor = new FlowChartExecutor(chart);
const result = await executor.resume(restored, { approved: true });
Execute the chart. Resolves when the run finishes — or pauses, if a
pausable stage returned data (check isPaused() afterward).
Optionaloptions: RunOptions
{ input, env } — input is the frozen business input
(read in a stage via scope.$getArgs()); env is infrastructure context
like { signal, timeoutMs, traceId } (read via scope.$getEnv()); deadline / timeoutMs give the run
its deadline (env.deadline, scope.$attempt().timeLeftMs()).
After it resolves, read results off the executor:
getSnapshot() — full state, commit log, execution tree.getNarrativeEntries() — the plain-English trace (call enableNarrative()
or attach a narrative() recorder first).isPaused() — true if a stage paused; then use getCheckpoint() / resume().One run at a time per executor — it holds per-run state (runId, recorders, checkpoint). Create one executor per concurrent run.
The commitValues dial (see FlowChartExecutorOptions.commitValues). Call before run().
The readTracking dial (see FlowChartExecutorOptions.readTracking). Call before run().
Set a declarative redaction policy that applies to all stages. Must be called before run().
The writeTracking dial (see FlowChartExecutorOptions.writeTracking). Call before run().
Create a FlowChartExecutor.
Options object form (preferred):
2-param form (also supported):