footprintjs 9.26.0 → 9.28.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.
Files changed (54) hide show
  1. package/AGENTS.md +3 -0
  2. package/CLAUDE.md +5 -4
  3. package/README.md +4 -0
  4. package/dist/advanced.js +3 -2
  5. package/dist/esm/advanced.d.ts +2 -2
  6. package/dist/esm/advanced.js +2 -2
  7. package/dist/esm/index.d.ts +1 -1
  8. package/dist/esm/index.js +1 -1
  9. package/dist/esm/lib/builder/FlowChartBuilder.d.ts +10 -0
  10. package/dist/esm/lib/builder/FlowChartBuilder.js +27 -13
  11. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +45 -12
  12. package/dist/esm/lib/engine/handlers/ResumeEntry.d.ts +195 -0
  13. package/dist/esm/lib/engine/handlers/ResumeEntry.js +471 -0
  14. package/dist/esm/lib/engine/handlers/SubflowExecutor.d.ts +6 -0
  15. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +43 -10
  16. package/dist/esm/lib/engine/handlers/index.d.ts +2 -0
  17. package/dist/esm/lib/engine/handlers/index.js +2 -1
  18. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +1 -1
  19. package/dist/esm/lib/engine/traversal/FlowchartTraverser.d.ts +49 -4
  20. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +64 -13
  21. package/dist/esm/lib/engine/types.d.ts +30 -11
  22. package/dist/esm/lib/engine/types.js +1 -1
  23. package/dist/esm/lib/pause/index.d.ts +1 -1
  24. package/dist/esm/lib/pause/index.js +1 -1
  25. package/dist/esm/lib/pause/types.d.ts +93 -5
  26. package/dist/esm/lib/pause/types.js +57 -3
  27. package/dist/esm/lib/runner/FlowChartExecutor.d.ts +8 -10
  28. package/dist/esm/lib/runner/FlowChartExecutor.js +153 -181
  29. package/dist/esm/lib/time-travel/bundles.js +11 -1
  30. package/dist/index.js +1 -1
  31. package/dist/lib/builder/FlowChartBuilder.js +27 -13
  32. package/dist/lib/engine/handlers/ChildrenExecutor.js +45 -12
  33. package/dist/lib/engine/handlers/ResumeEntry.js +478 -0
  34. package/dist/lib/engine/handlers/SubflowExecutor.js +43 -10
  35. package/dist/lib/engine/handlers/index.js +4 -2
  36. package/dist/lib/engine/traversal/FlowchartTraverser.js +64 -13
  37. package/dist/lib/engine/types.js +1 -1
  38. package/dist/lib/pause/index.js +1 -1
  39. package/dist/lib/pause/types.js +57 -3
  40. package/dist/lib/runner/FlowChartExecutor.js +153 -181
  41. package/dist/lib/time-travel/bundles.js +11 -1
  42. package/dist/types/advanced.d.ts +2 -2
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/lib/builder/FlowChartBuilder.d.ts +10 -0
  45. package/dist/types/lib/engine/handlers/ResumeEntry.d.ts +195 -0
  46. package/dist/types/lib/engine/handlers/SubflowExecutor.d.ts +6 -0
  47. package/dist/types/lib/engine/handlers/index.d.ts +2 -0
  48. package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +1 -1
  49. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +49 -4
  50. package/dist/types/lib/engine/types.d.ts +30 -11
  51. package/dist/types/lib/pause/index.d.ts +1 -1
  52. package/dist/types/lib/pause/types.d.ts +93 -5
  53. package/dist/types/lib/runner/FlowChartExecutor.d.ts +8 -10
  54. package/package.json +1 -1
@@ -14,7 +14,16 @@
14
14
  * Resume rebuilds the flowchart, restores scope, navigates to the paused stage,
15
15
  * injects resumeInput, and continues traversal.
16
16
  *
17
- * Supported topologies: linear, subflow, lazy subflow, loop, nested subflow in loop.
17
+ * Supported topologies: linear, subflow (at any depth — mounted linearly, as a
18
+ * decider or selector branch, or as a fork child), loop, nested subflow in
19
+ * loop, and several parallel siblings pausing in one fan-out (each asks in
20
+ * turn — `FlowchartCheckpoint.pendingPauses`).
21
+ *
22
+ * NOT resumable yet: a pause inside a LAZY subflow (`addLazySubFlowChart*`) or
23
+ * inside an `addParallelForEach` branch. Their graphs are resolved at run time
24
+ * and are not in the chart a resume walks, so `resume()` refuses the
25
+ * checkpoint ("not found") instead of guessing. Pinned in
26
+ * test/lib/pause/resume-known-limitations.test.ts.
18
27
  */
19
28
  /**
20
29
  * Thrown by `scope.$pause()` to signal that execution should stop
@@ -65,10 +74,35 @@ export declare class PauseSignal extends Error {
65
74
  * crash with "X is not iterable" / undefined.
66
75
  */
67
76
  private _subflowStates;
77
+ /**
78
+ * Pauses that PARALLEL SIBLINGS raised in the same fan-out as this one,
79
+ * waiting their turn (9.28.0). Recorded by `ChildrenExecutor` when more
80
+ * than one child of a fork (or more than one selected branch) pauses: the
81
+ * first pause is the one that is asked, the others ride here instead of
82
+ * being dropped. Each path is relative to where the signal currently is
83
+ * and grows with it — `prependSubflow` prepends to every entry too.
84
+ */
85
+ private _pendingPauses;
68
86
  constructor(data: unknown, stageId: string, pausedBy?: 'interrupt');
69
87
  get subflowPath(): readonly string[];
70
- /** Prepend a subflow ID to the path (called during bubble-up). */
88
+ /**
89
+ * Prepend a subflow ID to the path (called during bubble-up). The pending
90
+ * sibling pauses ride the same boundary, so their paths grow with it.
91
+ */
71
92
  prependSubflow(subflowId: string): void;
93
+ /**
94
+ * Queue a parallel sibling's pause behind this one (its path relative to
95
+ * this signal's current level). Copied in: the entry is owned by the
96
+ * signal from here on.
97
+ */
98
+ addPendingPause(pause: PendingPause): void;
99
+ /** Parallel siblings' pauses waiting behind this one, in the order they will be asked. */
100
+ get pendingPauses(): readonly PendingPause[];
101
+ /**
102
+ * This pause as a {@link PendingPause} — what `ChildrenExecutor` queues
103
+ * behind a sibling's pause when both paused in one fan-out.
104
+ */
105
+ toPendingPause(): PendingPause;
72
106
  /** The stage that invoked the paused child (decider, selector, fork). */
73
107
  get invokerStageId(): string | undefined;
74
108
  /** Where execution should continue after resume (invoker's next node). */
@@ -93,6 +127,42 @@ export declare class PauseSignal extends Error {
93
127
  /** Captured subflow scopes (read-only view). */
94
128
  get subflowStates(): Readonly<Record<string, Record<string, unknown>>>;
95
129
  }
130
+ /**
131
+ * A pause a PARALLEL SIBLING of the paused stage raised in the same fan-out —
132
+ * two children of one fork (or two selected branches of one selector) that
133
+ * both paused (9.28.0). Only one pause is asked at a time: the checkpoint's
134
+ * own (`pausedStageId`), then each of these in turn, with the fan-out's join
135
+ * running only once every child is done.
136
+ *
137
+ * Everything needed to raise the sibling's pause again later WITHOUT re-running
138
+ * the sibling: its stage, its path, its captured subflow state, its question.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // A fork whose two children both ask: the first checkpoint asks c1 and
143
+ * // carries c2's pause; resuming it finishes c1, then pauses with c2's
144
+ * // question; resuming THAT finishes c2 and runs the join.
145
+ * const cp = executor.getCheckpoint()!;
146
+ * cp.pausedStageId; // 'c1/ask'
147
+ * cp.pendingPauses?.[0].pausedStageId; // 'c2/ask'
148
+ * ```
149
+ */
150
+ export interface PendingPause {
151
+ /** The sibling's paused stage (path-prefixed id, as `pausedStageId`). */
152
+ readonly pausedStageId: string;
153
+ /** Path through subflows to the sibling's paused stage (as `subflowPath`). */
154
+ readonly subflowPath: readonly string[];
155
+ /**
156
+ * The sibling's own subflow captures — the subflows on ITS path below the
157
+ * fan-out. The subflows above the fan-out are shared with the checkpoint's
158
+ * own pause and are captured again when this pause is raised.
159
+ */
160
+ readonly subflowStates: Record<string, Record<string, unknown>>;
161
+ /** The sibling's question (as `pauseData`). */
162
+ readonly pauseData?: unknown;
163
+ /** How the sibling paused (as `pausedBy`). */
164
+ readonly pausedBy?: 'interrupt';
165
+ }
96
166
  /**
97
167
  * Returned by a pausable stage's execute/resume function to signal pause.
98
168
  *
@@ -183,8 +253,10 @@ export interface FlowchartCheckpoint {
183
253
  *
184
254
  * Always present (empty `{}` for root-level pauses where no subflows
185
255
  * were entered). On resume, `SubflowExecutor` seeds each nested runtime
186
- * from this map (and skips the inputMapper) so resume handlers see
187
- * pre-pause scope across same-executor AND cross-executor restarts.
256
+ * on the path from this map — ONCE, on its first entry, in place of the
257
+ * inputMapper's values (the mapper still runs, for the stages' read-only
258
+ * args) — so resume handlers see pre-pause scope across same-executor AND
259
+ * cross-executor restarts. Only the subflows ON `subflowPath` read it.
188
260
  */
189
261
  readonly subflowStates: Record<string, Record<string, unknown>>;
190
262
  /**
@@ -209,8 +281,24 @@ export interface FlowchartCheckpoint {
209
281
  readonly visitCounts?: Readonly<Record<string, number>>;
210
282
  /** Stage that invoked the paused child (decider, selector, fork). Absent for linear pauses. */
211
283
  readonly invokerStageId?: string;
212
- /** Where to continue after resume — the invoker's next node ID. Absent for linear pauses. */
284
+ /**
285
+ * The invoker's next node ID. Absent for linear pauses.
286
+ *
287
+ * A RECORD, not an instruction: since 9.28.0 `resume()` derives what runs
288
+ * after the paused stage from the chart itself (the stage's own `next`, else
289
+ * the continuation of the dispatcher that ran it — at every level of the
290
+ * pause path), so an edited value cannot redirect a resumed run.
291
+ */
213
292
  readonly continuationStageId?: string;
293
+ /**
294
+ * Pauses raised by PARALLEL SIBLINGS of the paused stage in the same
295
+ * fan-out, waiting their turn (9.28.0) — see {@link PendingPause}. Absent
296
+ * when only one child paused (every checkpoint before 9.28.0 omits it, and
297
+ * resumes exactly as before). Resuming this checkpoint finishes the paused
298
+ * stage's child, then pauses again with the first of these; the fan-out's
299
+ * join runs once every child is done.
300
+ */
301
+ readonly pendingPauses?: readonly PendingPause[];
214
302
  /** Timestamp of when the pause occurred. */
215
303
  readonly pausedAt: number;
216
304
  }
@@ -320,7 +320,14 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
320
320
  * Resume a paused flowchart from a checkpoint.
321
321
  *
322
322
  * Restores the scope state, calls the paused stage's `resumeFn` with the
323
- * provided input, then continues traversal from the next stage.
323
+ * provided input (or, for an `interrupt()` pause, re-runs the stage with the
324
+ * answer), then continues with whatever ran after that stage on the run —
325
+ * its own `next`, or the continuation of the decider, selector or fork that
326
+ * dispatched it, at every level of the pause path — and from there walks the
327
+ * chart as built. When parallel siblings paused in the same fan-out
328
+ * (`checkpoint.pendingPauses`), the run pauses again with the next sibling's
329
+ * question once this one's branch is done; the fan-out's join runs after the
330
+ * last answer. See `ResumeEntry` (`footprintjs/advanced`).
324
331
  *
325
332
  * The checkpoint can come from `getCheckpoint()` on a previous run, or from
326
333
  * a serialized checkpoint stored in Redis/Postgres/localStorage.
@@ -392,15 +399,6 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
392
399
  * Handles subflow paths by drilling into registered subflows.
393
400
  */
394
401
  private findNodeInGraph;
395
- /**
396
- * Find the mount node (the node that mounts a subflow boundary)
397
- * for a given subflowId, by DFS from `start`. Used by `resume()` to
398
- * locate the OUTER node we have to enter through so the subflow's
399
- * outputMapper and parent continuation execute.
400
- *
401
- * Cycle-safe via visited set. Returns the first match (DFS order).
402
- */
403
- private findMountInGraph;
404
402
  /** DFS search for a node by ID in the StageNode graph. Cycle-safe via visited set. */
405
403
  private dfsFind;
406
404
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "9.26.0",
3
+ "version": "9.28.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",