footprintjs 8.2.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,15 @@
8
8
  * - String ID → reference to existing node (resolve via NodeResolver)
9
9
  * - StageNode with fn → truly dynamic node (execute directly)
10
10
  * - StageNode without fn → reference by ID (resolve via NodeResolver)
11
+ *
12
+ * Two entry points:
13
+ * - `resolveTarget` — resolves the continuation to `{ node, context }` and
14
+ * fires every side effect (iteration counting, debug logs, `onLoop`
15
+ * narrative) WITHOUT executing. The traverser's trampoline driver uses
16
+ * this to follow loop edges iteratively — flat stack, so the iteration
17
+ * limit (not call-stack depth) is what bounds a loop.
18
+ * - `resolve` — resolveTarget + immediate execution via the provided
19
+ * `executeNode` callback. Kept for direct/advanced callers.
11
20
  */
12
21
  import type { StageContext } from '../../memory/StageContext.js';
13
22
  import type { StageNode } from '../graph/StageNode.js';
@@ -16,6 +25,16 @@ import type { HandlerDeps } from '../types.js';
16
25
  import type { NodeResolver } from './NodeResolver.js';
17
26
  import type { ExecuteNodeFn } from './types.js';
18
27
  export declare const DEFAULT_MAX_ITERATIONS = 1000;
28
+ /**
29
+ * A resolved continuation target — the node to execute next plus the
30
+ * StageContext to execute it in. All side effects (iteration counting,
31
+ * debug logs, `onLoop` narrative) have already fired by the time this
32
+ * is returned.
33
+ */
34
+ export interface ResolvedContinuation<TOut = any, TScope = any> {
35
+ node: StageNode<TOut, TScope>;
36
+ context: StageContext;
37
+ }
19
38
  export declare class ContinuationResolver<TOut = any, TScope = any> {
20
39
  private readonly deps;
21
40
  private readonly nodeResolver;
@@ -28,19 +47,27 @@ export declare class ContinuationResolver<TOut = any, TScope = any> {
28
47
  private readonly maxIterations;
29
48
  constructor(deps: HandlerDeps<TOut, TScope>, nodeResolver: NodeResolver<TOut, TScope>, onIterationUpdate?: (nodeId: string, count: number) => void, maxIterations?: number);
30
49
  /**
31
- * Resolve a dynamic continuation.
32
- * Dispatches to handleStringReference, handleDirectNode, or handleNodeReference
33
- * based on the dynamicNext type.
50
+ * Resolve a dynamic continuation and execute it immediately.
51
+ * Equivalent to `executeNode(...resolveTarget(...))` the traverser's
52
+ * driver loop calls `resolveTarget` directly instead so the continuation
53
+ * becomes a flat trampoline hop rather than a retained recursive frame.
34
54
  */
35
55
  resolve(dynamicNext: string | StageNode<TOut, TScope>, node: StageNode<TOut, TScope>, context: StageContext, breakFlag: {
36
56
  shouldBreak: boolean;
37
57
  }, branchPath: string | undefined, executeNode: ExecuteNodeFn<TOut, TScope>, traversalContext?: TraversalContext): Promise<any>;
38
- /** dynamicNext is a string ID → resolve from graph, track iteration. */
39
- private handleStringReference;
40
- /** dynamicNext is a StageNode with fn execute directly (truly dynamic). */
41
- private handleDirectNode;
42
- /** dynamicNext is a StageNode without fn → reference by ID, resolve + track iteration. */
43
- private handleNodeReference;
58
+ /**
59
+ * Resolve a dynamic continuation to its target node + next StageContext
60
+ * WITHOUT executing it. Fires the same side effects `resolve` always did
61
+ * (iteration counting + limit, `dynamicNext*` logs, loop debug message,
62
+ * `onLoop` narrative), in the same order.
63
+ *
64
+ * Three dynamicNext patterns:
65
+ * - StageNode with fn → truly dynamic node, returned as-is (no iteration
66
+ * tracking — it is a fresh node, not a back-edge).
67
+ * - String ID → reference to an existing node, resolved via NodeResolver.
68
+ * - StageNode without fn → reference by ID, resolved via NodeResolver.
69
+ */
70
+ resolveTarget(dynamicNext: string | StageNode<TOut, TScope>, currentNode: StageNode<TOut, TScope>, context: StageContext, branchPath: string | undefined, traversalContext?: TraversalContext): ResolvedContinuation<TOut, TScope>;
44
71
  /**
45
72
  * Get the next iteration number for a node and increment.
46
73
  * Returns 0 for first visit, 1 for second, etc.
@@ -3,6 +3,16 @@
3
3
  *
4
4
  * Handles scope-based deciders (stage IS the decider, returns branch ID).
5
5
  * Logs flow control decisions and narrative sentences.
6
+ *
7
+ * Two entry points:
8
+ * - `prepareDispatch` — runs the decider stage, commits, resolves the chosen
9
+ * branch, fires narrative, and returns the chosen node + branch context
10
+ * WITHOUT executing it. The traverser's trampoline driver uses this so a
11
+ * decider with no continuation of its own can hand the branch to the
12
+ * driver as a flat hop (loop-heavy decider charts stay flat-stacked).
13
+ * - `handleScopeBased` — prepareDispatch + immediate branch execution via
14
+ * the provided `executeNode` callback. Kept for direct/advanced callers
15
+ * and for deciders whose own `.next` must run after the branch completes.
6
16
  */
7
17
  import type { StageContext } from '../../memory/StageContext.js';
8
18
  import type { StageNode } from '../graph/StageNode.js';
@@ -10,6 +20,19 @@ import type { TraversalContext } from '../narrative/types.js';
10
20
  import type { HandlerDeps, StageFunction } from '../types.js';
11
21
  import type { ExecuteNodeFn, RunStageFn } from './types.js';
12
22
  export type { ExecuteNodeFn, RunStageFn };
23
+ /**
24
+ * Result of `prepareDispatch` — either the decider stage broke (no branch
25
+ * runs; `branchId` is the decider's return value), or a branch was chosen
26
+ * and is ready to execute in `branchContext`.
27
+ */
28
+ export type DeciderDispatch<TOut = any, TScope = any> = {
29
+ kind: 'break';
30
+ branchId: string;
31
+ } | {
32
+ kind: 'dispatch';
33
+ chosen: StageNode<TOut, TScope>;
34
+ branchContext: StageContext;
35
+ };
13
36
  export declare class DeciderHandler<TOut = any, TScope = any> {
14
37
  private readonly deps;
15
38
  constructor(deps: HandlerDeps<TOut, TScope>);
@@ -21,4 +44,13 @@ export declare class DeciderHandler<TOut = any, TScope = any> {
21
44
  handleScopeBased(node: StageNode<TOut, TScope>, stageFunc: StageFunction<TOut, TScope>, context: StageContext, breakFlag: {
22
45
  shouldBreak: boolean;
23
46
  }, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, executeNode: ExecuteNodeFn<TOut, TScope>, traversalContext?: TraversalContext): Promise<any>;
47
+ /**
48
+ * Run the decider stage and resolve the chosen branch WITHOUT executing it.
49
+ * Everything up to (and including) the `onDecision`/`onStageExecuted`
50
+ * narrative and the branch context creation happens here — only the
51
+ * branch execution itself is left to the caller.
52
+ */
53
+ prepareDispatch(node: StageNode<TOut, TScope>, stageFunc: StageFunction<TOut, TScope>, context: StageContext, breakFlag: {
54
+ shouldBreak: boolean;
55
+ }, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, traversalContext?: TraversalContext): Promise<DeciderDispatch<TOut, TScope>>;
24
56
  }
@@ -11,7 +11,15 @@ import type { HandlerDeps } from '../types.js';
11
11
  export declare class NodeResolver<TOut = any, TScope = any> {
12
12
  private deps;
13
13
  private readonly nodeIdMap;
14
- constructor(deps: HandlerDeps<TOut, TScope>, nodeIdMap?: Map<string, StageNode<TOut, TScope>>);
14
+ private readonly getEffectiveChildren?;
15
+ constructor(deps: HandlerDeps<TOut, TScope>, nodeIdMap?: Map<string, StageNode<TOut, TScope>>,
16
+ /**
17
+ * Overlay-aware children accessor injected by the traverser. The DFS
18
+ * fallback resolves loop targets against the LIVE runtime shape — a node
19
+ * patched by a dynamic StageNode return carries its children in the
20
+ * traverser-local overlay, not on the shared built-chart node.
21
+ */
22
+ getEffectiveChildren?: (node: StageNode<TOut, TScope>) => StageNode<TOut, TScope>[] | undefined);
15
23
  /**
16
24
  * O(1) node lookup via pre-built ID map.
17
25
  * Falls back to DFS from startNode (for dynamic nodes added at runtime
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * FlowchartTraverser — Pre-order DFS traversal of StageNode graph.
3
3
  *
4
- * Unified traversal algorithm for all node shapes:
5
- * const pre = await prep();
6
- * const [x, y] = await Promise.all([fx(pre), fy(pre)]);
7
- * return await next(x, y);
4
+ * Unified traversal algorithm for all node shapes. `executeNode` is a
5
+ * TRAMPOLINE driver: it runs `executeNodeStep` (one node, all 7 phases) in
6
+ * a flat loop, following tail continuations (linear `next`, loop edges,
7
+ * dynamic next, flat decider dispatch) iteratively — so chain length and
8
+ * loop iterations never grow the call stack. Only true tree nesting (fork
9
+ * children, with-continuation decider/selector branches, subflow mounts)
10
+ * recurses.
8
11
  *
9
- * For each node, executeNode follows 7 phases:
12
+ * For each node, executeNodeStep follows 7 phases:
10
13
  * 0. CLASSIFY — subflow detection, early delegation
11
14
  * 1. VALIDATE — node invariants, role markers
12
15
  * 2. EXECUTE — run stage fn, commit, break check
@@ -21,7 +24,7 @@
21
24
  import type { ScopeProtectionMode } from '../../scope/protection/types.js';
22
25
  import { FlowRecorderDispatcher } from '../narrative/FlowRecorderDispatcher.js';
23
26
  import type { FlowRecorder, IControlFlowNarrative } from '../narrative/types.js';
24
- import type { IExecutionRuntime, ILogger, ScopeFactory, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowResult, TraversalResult } from '../types.js';
27
+ import type { IExecutionRuntime, ILogger, ScopeFactory, Selector, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowMountOptions, SubflowResult, TraversalResult } from '../types.js';
25
28
  export interface TraverserOptions<TOut = any, TScope = any> {
26
29
  root: StageNode<TOut, TScope>;
27
30
  stageMap: Map<string, StageFunction<TOut, TScope>>;
@@ -49,10 +52,17 @@ export interface TraverserOptions<TOut = any, TScope = any> {
49
52
  */
50
53
  narrativeGenerator?: IControlFlowNarrative;
51
54
  /**
52
- * Maximum recursive executeNode depth. Defaults to FlowchartTraverser.MAX_EXECUTE_DEPTH (500).
53
- * Override in tests or unusually deep pipelines.
55
+ * Maximum nested executeNode depth (tree nesting branch/fork dispatch and
56
+ * dynamic recursion, NOT linear chains or loop iterations, which run flat).
57
+ * Defaults to FlowchartTraverser.MAX_EXECUTE_DEPTH (500).
54
58
  */
55
59
  maxDepth?: number;
60
+ /**
61
+ * Maximum loop iterations per node (the ContinuationResolver guard).
62
+ * Defaults to DEFAULT_MAX_ITERATIONS (1000). Propagated to subflow
63
+ * traversers. Must be >= 1.
64
+ */
65
+ maxIterations?: number;
56
66
  /**
57
67
  * When this traverser runs inside a subflow, set this to the subflow's ID.
58
68
  * Propagated to TraversalContext so narrative entries carry the correct subflowId.
@@ -78,6 +88,37 @@ export interface TraverserOptions<TOut = any, TScope = any> {
78
88
  */
79
89
  runId: string;
80
90
  }
91
+ /**
92
+ * Traverser-local overlay entry for a node whose stage function returned a
93
+ * StageNode (dynamic continuation). Holds the dynamic values that earlier
94
+ * versions wrote DIRECTLY onto the shared built-chart node — which leaked the
95
+ * dynamic shape into every later run of the same built chart and raced
96
+ * concurrent executors. The overlay keeps the built graph immutable: patches
97
+ * live in a per-traverser Map keyed by `node.id` and die with the run.
98
+ *
99
+ * `next` is intentionally ABSENT: a dynamic `next` only ever applies to the
100
+ * visit that produced it (the old code wrote `node.next` and restored it
101
+ * before anything could observe the write), so it stays a local variable in
102
+ * `executeNode` and is routed through `ContinuationResolver` directly.
103
+ */
104
+ export interface DynamicNodePatch<TOut = any, TScope = any> {
105
+ /**
106
+ * Subflow mount metadata from a dynamic-subflow return. Grouped so the
107
+ * merged view reproduces the old field-wise overwrite exactly — including
108
+ * `subflowName`/`subflowMountOptions` becoming undefined when the dynamic
109
+ * return omitted them.
110
+ */
111
+ subflowMeta?: {
112
+ isSubflowRoot: true;
113
+ subflowId: string;
114
+ subflowName: string | undefined;
115
+ subflowMountOptions: SubflowMountOptions | undefined;
116
+ };
117
+ /** Dynamic fork children (replaces the built node's children for this run). */
118
+ children?: StageNode<TOut, TScope>[];
119
+ /** Dynamic output-based selector accompanying dynamic children. */
120
+ nextNodeSelector?: Selector;
121
+ }
81
122
  export declare class FlowchartTraverser<TOut = any, TScope = any> {
82
123
  private readonly root;
83
124
  private stageMap;
@@ -112,11 +153,41 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
112
153
  */
113
154
  private readonly resolvedLazySubflows;
114
155
  /**
115
- * Recursion depth counter for executeNode.
116
- * Each recursive executeNode call increments this; decrements on exit (try/finally).
117
- * Prevents call-stack overflow on infinite loops or excessively deep stage chains.
156
+ * Per-traverser overlay of dynamic StageNode returns, keyed by `node.id`.
157
+ * Phase 4 writes patches HERE instead of mutating the shared built-chart
158
+ * node objects (same isolation convention as `resolvedLazySubflows`).
159
+ * All engine reads of the patched fields go through the `eff*` accessors
160
+ * below. The map dies with the traverser — one run, one overlay — so a
161
+ * fresh executor over the same built chart always sees the original graph.
162
+ *
163
+ * Keyed by the node OBJECT (WeakMap), not `node.id`: a dynamic child that
164
+ * reuses a built node's id must NOT make the built node inherit the patch
165
+ * (id-keyed lookup caused phantom double-execution). `patchCount` is the
166
+ * fast-path check — WeakMap has no `size`.
167
+ */
168
+ private readonly dynamicPatches;
169
+ private patchCount;
170
+ /**
171
+ * TREE-nesting depth counter for executeNode (the trampoline driver).
172
+ * Each driver invocation increments this; decrements on exit (try/finally).
173
+ *
174
+ * Linear `next` chains, loop edges, and dynamic continuations are followed
175
+ * ITERATIVELY inside one driver invocation, so they never grow this
176
+ * counter. Only true tree recursion does: fork children, decider/selector
177
+ * branch dispatch (when the decider has its own continuation), and
178
+ * unbounded dynamic recursion. Prevents call-stack overflow on runaway
179
+ * recursive composition.
118
180
  */
119
181
  private _executeDepth;
182
+ /**
183
+ * Memoized parent-chain depth per StageContext. The context tree deepens
184
+ * by one per executed stage along a chain, so the naive parent-walk in
185
+ * `computeContextDepth` is O(chain length) per stage — O(n²) per run once
186
+ * the trampoline allows chains of tens of thousands of stages. Contexts
187
+ * are visited parent-before-child, so the memo makes each lookup O(1)
188
+ * amortized. WeakMap — dies with the traverser.
189
+ */
190
+ private readonly contextDepthCache;
120
191
  /**
121
192
  * Shared mutable execution counter — monotonic, incremented per stage execution.
122
193
  * Shared with child traversers (subflows) so indices are globally unique within a run.
@@ -127,18 +198,26 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
127
198
  */
128
199
  private readonly _maxDepth;
129
200
  /**
130
- * Default maximum recursive executeNode depth before an error is thrown.
131
- * 500 comfortably covers any realistic pipeline depth (including deeply nested
132
- * subflows) while preventing call-stack overflow (~10 000 frames in V8).
201
+ * Per-instance loop-iteration limit forwarded to the ContinuationResolver
202
+ * and propagated to subflow traversers. Undefined resolver default (1000).
203
+ */
204
+ private readonly _maxIterations?;
205
+ /**
206
+ * Default maximum nested executeNode depth before an error is thrown.
133
207
  *
134
- * **Note on counting:** the counter increments once per `executeNode` call, not once per
135
- * logical user stage. Subflow root entry and subflow continuation after return each cost
136
- * one tick. For pipelines with many nested subflows, budget roughly 2 × (avg stages per
137
- * subflow) of headroom when computing a custom `maxDepth` via `RunOptions.maxDepth`.
208
+ * **What counts as depth (trampoline model):** `executeNode` is an iterative
209
+ * driver linear `next` hops, loop edges (`loopTo`/dynamic next), and
210
+ * dynamic-subflow re-entry are followed in a flat loop and consume NO depth.
211
+ * Depth grows only with true tree nesting: one tick per fork child, one per
212
+ * decider/selector branch dispatch that must return to its invoker (decider
213
+ * with its own `next`), one per subflow mount frame in the parent (the
214
+ * subflow body itself runs on a FRESH traverser with its own budget).
138
215
  *
139
- * **Note on loops:** for `loopTo()` pipelines, this depth guard and `ContinuationResolver`'s
140
- * iteration limit are independent the lower one fires first. The default depth guard (500)
141
- * fires before the default iteration limit (1000) for loop-heavy pipelines.
216
+ * 500 therefore covers any realistic chart it bounds recursive
217
+ * COMPOSITION, not chain length or loop count. Loops are bounded by
218
+ * `ContinuationResolver`'s independent iteration limit (default 1000,
219
+ * configurable via `RunOptions.maxIterations`), which is now the binding
220
+ * constraint for loop-heavy pipelines.
142
221
  *
143
222
  * @remarks Not safe for concurrent `.execute()` calls on the same instance — concurrent
144
223
  * executions race on `_executeDepth`. Use a separate `FlowchartTraverser` per concurrent
@@ -197,19 +276,72 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
197
276
  /**
198
277
  * Build an O(1) ID→node map from the root graph.
199
278
  * Used by NodeResolver to avoid repeated DFS on every loopTo() call.
200
- * Depth-guarded at MAX_EXECUTE_DEPTH to prevent infinite recursion on cyclic graphs.
279
+ * Iterative worklist (no recursion) so arbitrarily long chains index fully;
280
+ * the `map.has` guard handles cyclic refs. First-visited node wins per ID —
281
+ * worklist order matches the old recursive pre-order (children, then next).
201
282
  * Dynamic subflows and lazy-resolved nodes are added to stageMap at runtime but not to this map —
202
283
  * those use the DFS fallback in NodeResolver.
203
284
  */
204
285
  private buildNodeIdMap;
205
286
  private getStageFn;
287
+ private getPatch;
288
+ private getOrCreatePatch;
289
+ /** Effective children: dynamic patch first, then the built node's children. */
290
+ private effChildren;
291
+ /** Effective output-based selector: dynamic patch first, then the built node's. */
292
+ private effSelector;
293
+ /** Effective subflow-root marker (true when a dynamic subflow was patched on). */
294
+ private effIsSubflowRoot;
295
+ /** Effective subflow id (patched verbatim by a dynamic subflow return). */
296
+ private effSubflowId;
297
+ /**
298
+ * Materialize the effective view of a node — field-identical to what the
299
+ * pre-overlay code produced by mutating the shared node. Used where a node
300
+ * is handed to a helper executor (NodeResolver / SubflowExecutor /
301
+ * ChildrenExecutor) so helpers never read stale built fields. Returns the
302
+ * node itself (no allocation) when it carries no patch.
303
+ */
304
+ private effNode;
206
305
  private executeStage;
207
306
  /**
208
- * Pre-order DFS traversal the core algorithm.
209
- * Each call processes one node through all 7 phases.
307
+ * Trampoline driver — pre-order DFS traversal entry point.
308
+ *
309
+ * Runs `executeNodeStep` (one node, all 7 phases) in a flat loop: every
310
+ * TAIL continuation (linear `next`, loop edge, dynamic next / dynamic
311
+ * re-entry, no-continuation decider dispatch) comes back as a
312
+ * `ContinuationHop` and is followed ITERATIVELY — neither the call stack
313
+ * nor the retained promise chain grows with chain length or loop count.
314
+ *
315
+ * Recursion remains ONLY for true tree nesting (each gets a nested driver
316
+ * call): fork children (`ChildrenExecutor`), selector branches (parallel
317
+ * fan-out), decider branch dispatch when the decider has its own `next`
318
+ * (the branch must complete BEFORE the decider's continuation runs), and
319
+ * subflow mounts (fresh traverser; the mount frame stays in the parent).
320
+ * `_executeDepth` therefore counts chart COMPOSITION depth only, guarded
321
+ * by `_maxDepth` (default `MAX_EXECUTE_DEPTH` = 500).
322
+ *
323
+ * PauseSignal: a flat decider dispatch records an `InvokerStamp`; if the
324
+ * continued chain later pauses, the driver stamps the signal during
325
+ * unwind — same invoker context the recursive dispatch's catch used to
326
+ * stamp, innermost (most recent dispatch) first.
210
327
  */
211
328
  private executeNode;
329
+ /** Build a flat continuation hop for the driver loop. */
330
+ private hop;
331
+ /**
332
+ * Execute ONE node through all 7 phases — the old recursive `executeNode`
333
+ * body; only the tail calls became `ContinuationHop` returns. Returns the
334
+ * node's result, or a hop for the driver loop to follow.
335
+ */
336
+ private executeNodeStep;
212
337
  private captureDynamicChildrenResult;
338
+ /**
339
+ * Parent-chain length of a StageContext — same value the pre-trampoline
340
+ * walk produced, memoized. The context tree deepens by one per executed
341
+ * stage along a chain, so the naive walk is O(chain length) per stage —
342
+ * O(n²) per run once chains reach trampoline scale. Contexts are visited
343
+ * parent-before-child, so the cached parent makes this O(1) amortized.
344
+ */
213
345
  private computeContextDepth;
214
346
  private prefixNodeTree;
215
347
  private autoRegisterSubflowDef;
@@ -322,12 +322,24 @@ export interface RunOptions {
322
322
  */
323
323
  env?: ExecutionEnv;
324
324
  /**
325
- * Override the maximum recursive `executeNode` depth for this run.
325
+ * Override the maximum nested `executeNode` depth for this run.
326
326
  * Defaults to `FlowchartTraverser.MAX_EXECUTE_DEPTH` (500).
327
- * Useful when deeply nested subflows or long chains need more headroom.
328
- * Must be >= 1.
327
+ *
328
+ * Depth counts TREE nesting only — fork children, decider/selector branch
329
+ * dispatch, recursive composition. Linear chains and loop iterations run
330
+ * on a flat trampoline and never consume depth, so this rarely needs
331
+ * raising. Must be >= 1.
329
332
  */
330
333
  maxDepth?: number;
334
+ /**
335
+ * Override the maximum loop iterations per node for this run (the
336
+ * `ContinuationResolver` infinite-loop guard). Defaults to 1000.
337
+ * This is the binding constraint for loop-heavy pipelines — raise it for
338
+ * legitimately long loops (`loopTo` chains run with a flat stack, so high
339
+ * values are safe; memory for state/narrative still grows per iteration).
340
+ * Propagates to subflows. Must be >= 1.
341
+ */
342
+ maxIterations?: number;
331
343
  }
332
344
  export type { FlowControlType, FlowMessage };
333
345
  export interface RuntimeStructureMetadata {
@@ -117,4 +117,7 @@ export declare class StageContext {
117
117
  }): void;
118
118
  getStageId(): string;
119
119
  getSnapshot(): StageSnapshot;
120
+ /** Snapshot of THIS context's own fields — `next`/`children` are filled
121
+ * in by the iterative walk in `getSnapshot`. */
122
+ private snapshotSelf;
120
123
  }
@@ -212,7 +212,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
212
212
  * const result = await executor.resume(restored, { approved: true });
213
213
  * ```
214
214
  */
215
- resume(checkpoint: FlowchartCheckpoint, resumeInput?: unknown, options?: Pick<RunOptions, 'signal' | 'env' | 'maxDepth'>): Promise<ExecutorResult>;
215
+ resume(checkpoint: FlowchartCheckpoint, resumeInput?: unknown, options?: Pick<RunOptions, 'signal' | 'env' | 'maxDepth' | 'maxIterations'>): Promise<ExecutorResult>;
216
216
  /**
217
217
  * Build a fully DETACHED checkpoint from a caught PauseSignal.
218
218
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "8.2.0",
3
+ "version": "9.0.0",
4
4
  "description": "Explainable backend flows — automatic causal traces, decision evidence, and MCP tool generation for AI agents",
5
5
  "license": "MIT",
6
6
  "author": "Sanjay Krishna Anbalagan",