footprintjs - v11.2.0
    Preparing search index...

    Class FlowChartExecutor<TOut, TScope>

    Type Parameters

    • TOut = any
    • TScope = any
    Index
    • 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.

      Parameters

      Returns void

      const audit: CombinedRecorder = {
      id: 'audit',
      onWrite: (e) => log('scope write', e.key),
      onDecision: (e) => log('routed to', e.chosen),
      };
      executor.attachCombinedRecorder(audit);
    • Attach an EmitRecorder for scope.$emit(name, payload) events. It rides the scope list, so onEmit fires exactly once per event. Idempotent by id.

      Parameters

      Returns void

      executor.attachEmitRecorder({
      id: 'token-meter',
      onEmit: (e) => {
      if (e.name === 'agentfootprint.llm.tokens') trackTokens(e.payload);
      },
      });
    • Attach 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).

      Parameters

      Returns void

      executor.attachScopeRecorder(new MetricRecorder());
      executor.attachScopeRecorder(new MetricRecorder('my-metrics')); // replaces same id
    • Detach 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().

      Parameters

      Returns void

    • Detach 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.

      Parameters

      Returns DetachHandle

      import { microtaskBatchDriver } from 'footprintjs/detach';

      const exec = new FlowChartExecutor(parentChart);
      const handle = exec.detachAndJoinLater(microtaskBatchDriver, telemetryChart, { event: 'x' });
      await handle.wait(); // optional
    • Detach a combined recorder from every channel it was attached to (safe if never attached).

      Parameters

      • id: string

      Returns void

    • Detach an EmitRecorder by id. Safe to call if never attached.

      Parameters

      • id: string

      Returns void

    • Detach all FlowRecorders with the given ID — both delivery tiers.

      Parameters

      • id: string

      Returns void

    • Detach all scope Recorders with the given ID — both delivery tiers.

      Parameters

      • id: string

      Returns void

    • 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).

      Parameters

      • Optionalopts: { timeoutMs?: number }

      Returns Promise<DrainResult>

    • Flush 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.

      Parameters

      • Optionalopts: { timeoutMs?: number }

      Returns Promise<RecordFlushResult>

      const executor = new FlowChartExecutor(chart, { sink: { adapter: fileSink(path), flush: 'manual' } });
      await executor.run();
      await executor.flushRecord({ timeoutMs: 2_000 });
    • 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.

      Returns FlowchartCheckpoint | undefined

      const result = await executor.run({ input });
      if (executor.isPaused()) {
      const checkpoint = executor.getCheckpoint()!;
      await redis.set(`session:${id}`, JSON.stringify(checkpoint));
      }
    • 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 number

    • 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 CombinedNarrativeEntry[]

    • Returns the runtime snapshot.

      Parameters

      • Optionaloptions: { redact?: boolean }
        • Optionalredact?: boolean

          true 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 RuntimeSnapshot

    • Returns the full spec for a dynamically-registered subflow. Requires an attached ManifestFlowRecorder that observed the registration.

      Parameters

      • subflowId: string

      Returns unknown

    • 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.

      Parameters

      Returns Promise<ExecutorResult>

      // 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).

      Parameters

      • 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.

      Returns Promise<ExecutorResult>