footprintjs 4.2.0 → 4.3.1

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 (73) hide show
  1. package/CLAUDE.md +43 -1
  2. package/dist/esm/index.js +1 -1
  3. package/dist/esm/lib/builder/FlowChartBuilder.js +53 -1
  4. package/dist/esm/lib/builder/types.js +1 -1
  5. package/dist/esm/lib/engine/graph/StageNode.js +1 -1
  6. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +14 -1
  7. package/dist/esm/lib/engine/handlers/DeciderHandler.js +7 -1
  8. package/dist/esm/lib/engine/handlers/SelectorHandler.js +7 -1
  9. package/dist/esm/lib/engine/handlers/StageRunner.js +13 -1
  10. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +8 -1
  11. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +39 -1
  12. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +31 -1
  13. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +10 -1
  14. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
  15. package/dist/esm/lib/engine/narrative/narrativeTypes.js +1 -1
  16. package/dist/esm/lib/engine/narrative/types.js +1 -1
  17. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +8 -1
  18. package/dist/esm/lib/engine/types.js +2 -1
  19. package/dist/esm/lib/pause/index.js +2 -0
  20. package/dist/esm/lib/pause/types.js +58 -0
  21. package/dist/esm/lib/reactive/types.js +2 -1
  22. package/dist/esm/lib/runner/ExecutionRuntime.js +11 -2
  23. package/dist/esm/lib/runner/FlowChartExecutor.js +232 -8
  24. package/dist/esm/lib/scope/ScopeFacade.js +21 -1
  25. package/dist/esm/lib/scope/recorders/DebugRecorder.js +19 -1
  26. package/dist/esm/lib/scope/recorders/MetricRecorder.js +10 -1
  27. package/dist/esm/lib/scope/types.js +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/lib/builder/FlowChartBuilder.js +53 -1
  30. package/dist/lib/builder/types.js +1 -1
  31. package/dist/lib/engine/graph/StageNode.js +1 -1
  32. package/dist/lib/engine/handlers/ChildrenExecutor.js +14 -1
  33. package/dist/lib/engine/handlers/DeciderHandler.js +7 -1
  34. package/dist/lib/engine/handlers/SelectorHandler.js +7 -1
  35. package/dist/lib/engine/handlers/StageRunner.js +17 -5
  36. package/dist/lib/engine/handlers/SubflowExecutor.js +8 -1
  37. package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +39 -1
  38. package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +31 -1
  39. package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +10 -1
  40. package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
  41. package/dist/lib/engine/narrative/narrativeTypes.js +1 -1
  42. package/dist/lib/engine/narrative/types.js +1 -1
  43. package/dist/lib/engine/traversal/FlowchartTraverser.js +8 -1
  44. package/dist/lib/engine/types.js +6 -2
  45. package/dist/lib/pause/index.js +8 -0
  46. package/dist/lib/pause/types.js +64 -0
  47. package/dist/lib/reactive/types.js +2 -1
  48. package/dist/lib/runner/ExecutionRuntime.js +11 -2
  49. package/dist/lib/runner/FlowChartExecutor.js +232 -8
  50. package/dist/lib/scope/ScopeFacade.js +21 -1
  51. package/dist/lib/scope/recorders/DebugRecorder.js +19 -1
  52. package/dist/lib/scope/recorders/MetricRecorder.js +10 -1
  53. package/dist/lib/scope/types.js +1 -1
  54. package/dist/types/index.d.ts +4 -1
  55. package/dist/types/lib/builder/FlowChartBuilder.d.ts +22 -0
  56. package/dist/types/lib/builder/types.d.ts +3 -1
  57. package/dist/types/lib/engine/graph/StageNode.d.ts +9 -0
  58. package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +9 -1
  59. package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +2 -0
  60. package/dist/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +3 -1
  61. package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +2 -0
  62. package/dist/types/lib/engine/narrative/narrativeTypes.d.ts +1 -1
  63. package/dist/types/lib/engine/narrative/types.d.ts +24 -0
  64. package/dist/types/lib/engine/types.d.ts +11 -0
  65. package/dist/types/lib/pause/index.d.ts +2 -0
  66. package/dist/types/lib/pause/types.d.ts +175 -0
  67. package/dist/types/lib/runner/ExecutionRuntime.d.ts +4 -0
  68. package/dist/types/lib/runner/FlowChartExecutor.d.ts +54 -2
  69. package/dist/types/lib/scope/ScopeFacade.d.ts +4 -0
  70. package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +4 -2
  71. package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +4 -1
  72. package/dist/types/lib/scope/types.d.ts +10 -0
  73. package/package.json +1 -1
@@ -36,6 +36,10 @@ export interface IControlFlowNarrative {
36
36
  onBreak(stageName: string, traversalContext?: TraversalContext): void;
37
37
  /** Called when a stage throws an error. Raw error is extracted into structured details. */
38
38
  onError(stageName: string, errorMessage: string, error: unknown, traversalContext?: TraversalContext): void;
39
+ /** Called when a pausable stage pauses execution. */
40
+ onPause(stageName: string, stageId: string, pauseData: unknown, subflowPath: readonly string[], traversalContext?: TraversalContext): void;
41
+ /** Called when a paused stage is resumed. */
42
+ onResume(stageName: string, stageId: string, hasInput: boolean, traversalContext?: TraversalContext): void;
39
43
  /** Returns accumulated narrative sentences in execution order. */
40
44
  getSentences(): string[];
41
45
  }
@@ -145,6 +149,24 @@ export interface FlowErrorEvent {
145
149
  structuredError: StructuredErrorInfo;
146
150
  traversalContext?: TraversalContext;
147
151
  }
152
+ /** Event passed to FlowRecorder.onPause. */
153
+ export interface FlowPauseEvent {
154
+ stageName: string;
155
+ stageId: string;
156
+ /** Data from the pause signal (question, reason, metadata). */
157
+ pauseData?: unknown;
158
+ /** Path through subflows to the paused stage. Empty at root level. */
159
+ subflowPath: readonly string[];
160
+ traversalContext?: TraversalContext;
161
+ }
162
+ /** Event passed to FlowRecorder.onResume. */
163
+ export interface FlowResumeEvent {
164
+ stageName: string;
165
+ stageId: string;
166
+ /** Whether resume input was provided. */
167
+ hasInput: boolean;
168
+ traversalContext?: TraversalContext;
169
+ }
148
170
  /**
149
171
  * FlowRecorder — Pluggable observer for control flow events.
150
172
  *
@@ -177,6 +199,8 @@ export interface FlowRecorder {
177
199
  onLoop?(event: FlowLoopEvent): void;
178
200
  onBreak?(event: FlowBreakEvent): void;
179
201
  onError?(event: FlowErrorEvent): void;
202
+ onPause?(event: FlowPauseEvent): void;
203
+ onResume?(event: FlowResumeEvent): void;
180
204
  /** Called before each run to reset per-run state. Implement for stateful recorders. */
181
205
  clear?(): void;
182
206
  /** Optional: expose collected data for inclusion in snapshots. */
@@ -11,6 +11,8 @@ import type { ScopeProtectionMode } from '../scope/protection/types.js';
11
11
  import type { StageNode } from './graph/StageNode.js';
12
12
  import type { IControlFlowNarrative } from './narrative/types.js';
13
13
  export type { Decider, Selector, StageNode } from './graph/StageNode.js';
14
+ export type { FlowchartCheckpoint, PausableHandler, PauseResult } from '../pause/index.js';
15
+ export { isPauseResult, isPauseSignal, PauseSignal } from '../pause/index.js';
14
16
  /** Minimal logging contract. Mirrors Console API subset. */
15
17
  export interface ILogger {
16
18
  info(message?: any, ...optionalParams: any[]): void;
@@ -255,6 +257,13 @@ export type BranchResults = {
255
257
  [branchId: string]: BranchResult;
256
258
  };
257
259
  export type TraversalResult = BranchResults | string | Error;
260
+ /** Returned by run()/resume() when execution pauses. */
261
+ export type PausedResult = {
262
+ readonly paused: true;
263
+ readonly checkpoint: import('../pause/types.js').FlowchartCheckpoint;
264
+ };
265
+ /** Full return type of FlowChartExecutor.run() and resume(). */
266
+ export type ExecutorResult = TraversalResult | PausedResult;
258
267
  export interface SerializedPipelineNode {
259
268
  name: string;
260
269
  id: string;
@@ -276,6 +285,8 @@ export interface SerializedPipelineNode {
276
285
  isParallelChild?: boolean;
277
286
  parallelGroupId?: string;
278
287
  isDynamic?: boolean;
288
+ /** When true, this stage can pause execution (PausableHandler pattern). */
289
+ isPausable?: boolean;
279
290
  }
280
291
  export type FlowChart<TOut = any, TScope = any> = {
281
292
  root: StageNode<TOut, TScope>;
@@ -0,0 +1,2 @@
1
+ export type { FlowchartCheckpoint, PausableHandler, PauseResult } from './types.js';
2
+ export { isPauseResult, isPauseSignal, PauseSignal } from './types.js';
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Pause/Resume — serializable checkpoint for long-running or human-in-the-loop flows.
3
+ *
4
+ * A stage signals pause by calling `scope.$pause(data)` which throws a PauseSignal.
5
+ * The signal bubbles up through SubflowExecutor → FlowchartTraverser → FlowChartExecutor,
6
+ * each level adding its subflow ID to the path.
7
+ *
8
+ * The checkpoint captures:
9
+ * - pausedPath: full path to the paused stage (e.g., ['sf-payment', 'approve'])
10
+ * - sharedState: scope at the pause point
11
+ * - executionTree: completed stages for BTS/narrative
12
+ * - pauseData: question, reason, or metadata from $pause()
13
+ *
14
+ * Resume rebuilds the flowchart, restores scope, navigates to the paused stage,
15
+ * injects resumeInput, and continues traversal.
16
+ *
17
+ * Supported topologies: linear, subflow, lazy subflow, loop, nested subflow in loop.
18
+ */
19
+ /**
20
+ * Thrown by `scope.$pause()` to signal that execution should stop
21
+ * and create a serializable checkpoint.
22
+ *
23
+ * Bubbles up through SubflowExecutor (which prepends subflow ID to path)
24
+ * and is caught by FlowchartTraverser/FlowChartExecutor.
25
+ */
26
+ export declare class PauseSignal extends Error {
27
+ /** Data from $pause() — question, reason, metadata. */
28
+ readonly pauseData: unknown;
29
+ /** ID of the stage that called $pause(). */
30
+ readonly stageId: string;
31
+ /** Path through subflows to the paused stage. Built during bubble-up. */
32
+ private _subflowPath;
33
+ constructor(data: unknown, stageId: string);
34
+ get subflowPath(): readonly string[];
35
+ /** Prepend a subflow ID to the path (called during bubble-up). */
36
+ prependSubflow(subflowId: string): void;
37
+ }
38
+ /**
39
+ * Returned by a pausable stage's execute/resume function to signal pause.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * execute: async (scope) => {
44
+ * scope.orderId = '123';
45
+ * return { pause: true, data: { question: 'Approve order 123?' } };
46
+ * }
47
+ * ```
48
+ */
49
+ export interface PauseResult {
50
+ readonly pause: true;
51
+ /** Data to include in the checkpoint — question, reason, metadata. */
52
+ readonly data?: unknown;
53
+ }
54
+ /**
55
+ * Serializable checkpoint — everything needed to resume a paused flowchart.
56
+ *
57
+ * JSON-safe: no functions, no class instances, no SDK clients.
58
+ * Store anywhere: Redis, Postgres, localStorage, a file.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * // Save
63
+ * const checkpoint = executor.getCheckpoint(); // after pause
64
+ * await redis.set(`session:${id}`, JSON.stringify(checkpoint));
65
+ *
66
+ * // Resume (hours later, possibly different server)
67
+ * const checkpoint = JSON.parse(await redis.get(`session:${id}`));
68
+ * const executor = new FlowChartExecutor(chart);
69
+ * await executor.resume(checkpoint, { approved: true });
70
+ * ```
71
+ */
72
+ /**
73
+ * Serializable checkpoint — everything needed to resume a paused flowchart.
74
+ *
75
+ * The execution tree IS the traversed path. The leaf node with status 'paused'
76
+ * IS the cursor. No separate path array needed — the tree structure captures
77
+ * the full nesting (including subflows).
78
+ *
79
+ * JSON-safe: no functions, no class instances, no SDK clients.
80
+ * Store anywhere: Redis, Postgres, localStorage, a file.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const checkpoint = executor.getCheckpoint(); // after pause
85
+ * await redis.set(`session:${id}`, JSON.stringify(checkpoint));
86
+ *
87
+ * // Resume (hours later, possibly different server)
88
+ * const checkpoint = JSON.parse(await redis.get(`session:${id}`));
89
+ * const executor = new FlowChartExecutor(chart);
90
+ * await executor.resume(checkpoint, { approved: true });
91
+ * ```
92
+ */
93
+ export interface FlowchartCheckpoint {
94
+ /** Scope state at the pause point — all shared memory key/values. */
95
+ readonly sharedState: Record<string, unknown>;
96
+ /** Execution tree — the traversed path. The leaf with status 'paused' is the cursor.
97
+ * Contains subflow nesting. Used for BTS visualization and to find the resume point. */
98
+ readonly executionTree: unknown;
99
+ /** ID of the stage that paused. Used by resume() to find the node in the graph. */
100
+ readonly pausedStageId: string;
101
+ /** Path through subflows to the paused stage (e.g., ['sf-payment', 'sf-validation']).
102
+ * Empty array when paused at the top level. */
103
+ readonly subflowPath: readonly string[];
104
+ /** Data from $pause() — question, reason, metadata. */
105
+ readonly pauseData?: unknown;
106
+ /** Subflow results collected before the pause. */
107
+ readonly subflowResults?: Record<string, unknown>;
108
+ /** Timestamp of when the pause occurred. */
109
+ readonly pausedAt: number;
110
+ }
111
+ /**
112
+ * Handler for a pausable stage — has two phases: execute and resume.
113
+ *
114
+ * `execute` runs the first time. It can return `{ pause: true }` to pause.
115
+ * `resume` runs when the flowchart is resumed. It receives the resume input.
116
+ *
117
+ * Both phases receive the same scope. After execute pauses, the scope state
118
+ * is preserved in the checkpoint. On resume, the scope is restored before
119
+ * calling resume.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * .addPausableFunction('ApproveOrder', {
124
+ * execute: async (scope) => {
125
+ * scope.orderId = '123';
126
+ * scope.amount = 299;
127
+ * return { pause: true, data: { question: `Approve $${scope.amount} refund?` } };
128
+ * },
129
+ * resume: async (scope, input) => {
130
+ * scope.approved = input.approved;
131
+ * scope.approver = input.approver;
132
+ * },
133
+ * }, 'approve-order', 'Manager approval gate')
134
+ *
135
+ * // Later — resume with human's answer
136
+ * await executor.resume(checkpoint, { approved: true, approver: 'Jane' });
137
+ * ```
138
+ */
139
+ export interface PausableHandler<TScope = any, TInput = unknown> {
140
+ /**
141
+ * First-run phase. Return data to pause, or void/undefined to continue normally.
142
+ *
143
+ * Any non-void return value becomes the `pauseData` in the checkpoint.
144
+ * The library detects the return and pauses automatically — no need to
145
+ * call `pause()` or construct `{ pause: true }`.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * execute: async (scope) => {
150
+ * scope.orderId = '123';
151
+ * return { question: `Approve order ${scope.orderId}?` }; // ← pauses
152
+ * }
153
+ *
154
+ * // Conditional pause
155
+ * execute: async (scope) => {
156
+ * if (scope.amount > 500) {
157
+ * return { reason: 'High-value order needs approval' }; // ← pauses
158
+ * }
159
+ * // void return → no pause, continues normally
160
+ * }
161
+ * ```
162
+ */
163
+ execute: (scope: TScope) => Promise<unknown> | unknown;
164
+ /**
165
+ * Resume phase. Called with the resume input when execution continues.
166
+ *
167
+ * The scope is restored from the checkpoint's `sharedState`. Writes during
168
+ * `resume` are committed and visible to subsequent stages.
169
+ */
170
+ resume: (scope: TScope, input: TInput) => Promise<void> | void;
171
+ }
172
+ /** Check if a value is a PauseResult (stage wants to pause). */
173
+ export declare function isPauseResult(value: unknown): value is PauseResult;
174
+ /** Check if an error is a PauseSignal. Uses instanceof + name brand fallback for cross-realm safety. */
175
+ export declare function isPauseSignal(error: unknown): error is PauseSignal;
@@ -32,7 +32,11 @@ export declare class ExecutionRuntime {
32
32
  globalStore: SharedMemory;
33
33
  rootStageContext: StageContext;
34
34
  executionHistory: EventLog;
35
+ /** Original root for getSnapshot() — set before resume changes rootStageContext. */
36
+ private _snapshotRoot?;
35
37
  constructor(rootName: string, rootId: string, defaultValues?: unknown, initialState?: unknown);
38
+ /** Preserve the current rootStageContext for snapshots before changing it for resume. */
39
+ preserveSnapshotRoot(): void;
36
40
  getPipelines(): string[];
37
41
  setRootObject(path: string[], key: string, value: unknown): void;
38
42
  getSnapshot(): RuntimeSnapshot;
@@ -20,7 +20,8 @@ import type { CombinedNarrativeRecorderOptions } from '../engine/narrative/Combi
20
20
  import type { CombinedNarrativeEntry } from '../engine/narrative/narrativeTypes.js';
21
21
  import type { ManifestEntry } from '../engine/narrative/recorders/ManifestFlowRecorder.js';
22
22
  import type { FlowRecorder } from '../engine/narrative/types.js';
23
- import { type ExtractorError, type FlowChart, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult, type TraversalResult } from '../engine/types.js';
23
+ import { type ExecutorResult, type ExtractorError, type FlowChart, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult } from '../engine/types.js';
24
+ import type { FlowchartCheckpoint } from '../pause/types.js';
24
25
  import type { ScopeProtectionMode } from '../scope/protection/types.js';
25
26
  import type { Recorder, RedactionPolicy, RedactionReport } from '../scope/types.js';
26
27
  import { type RuntimeSnapshot } from './ExecutionRuntime.js';
@@ -88,6 +89,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
88
89
  private redactionPolicy;
89
90
  private sharedRedactedKeys;
90
91
  private sharedRedactedFieldsByKey;
92
+ private lastCheckpoint;
91
93
  private readonly flowChartArgs;
92
94
  /**
93
95
  * Create a FlowChartExecutor.
@@ -118,6 +120,56 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
118
120
  * most recent run. Never includes actual values.
119
121
  */
120
122
  getRedactionReport(): RedactionReport;
123
+ /**
124
+ * Returns the checkpoint from the most recent paused execution, or `undefined`
125
+ * if the last run completed without pausing.
126
+ *
127
+ * The checkpoint is JSON-serializable — store it in Redis, Postgres, localStorage, etc.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const result = await executor.run({ input });
132
+ * if (executor.isPaused()) {
133
+ * const checkpoint = executor.getCheckpoint()!;
134
+ * await redis.set(`session:${id}`, JSON.stringify(checkpoint));
135
+ * }
136
+ * ```
137
+ */
138
+ getCheckpoint(): FlowchartCheckpoint | undefined;
139
+ /** Returns `true` if the most recent run() was paused (checkpoint available). */
140
+ isPaused(): boolean;
141
+ /**
142
+ * Resume a paused flowchart from a checkpoint.
143
+ *
144
+ * Restores the scope state, calls the paused stage's `resumeFn` with the
145
+ * provided input, then continues traversal from the next stage.
146
+ *
147
+ * The checkpoint can come from `getCheckpoint()` on a previous run, or from
148
+ * a serialized checkpoint stored in Redis/Postgres/localStorage.
149
+ *
150
+ * **Narrative/recorder state is reset on resume.** To keep a unified narrative
151
+ * across pause/resume cycles, collect it before calling resume.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // After a pause...
156
+ * const checkpoint = executor.getCheckpoint()!;
157
+ * await redis.set(`session:${id}`, JSON.stringify(checkpoint));
158
+ *
159
+ * // Later (possibly different server, same chart)
160
+ * const checkpoint = JSON.parse(await redis.get(`session:${id}`));
161
+ * const executor = new FlowChartExecutor(chart);
162
+ * const result = await executor.resume(checkpoint, { approved: true });
163
+ * ```
164
+ */
165
+ resume(checkpoint: FlowchartCheckpoint, resumeInput?: unknown, options?: Pick<RunOptions, 'signal' | 'env' | 'maxDepth'>): Promise<ExecutorResult>;
166
+ /**
167
+ * Find a StageNode in the compiled graph by ID.
168
+ * Handles subflow paths by drilling into registered subflows.
169
+ */
170
+ private findNodeInGraph;
171
+ /** DFS search for a node by ID in the StageNode graph. Cycle-safe via visited set. */
172
+ private dfsFind;
121
173
  /**
122
174
  * Attach a scope Recorder to observe data operations (reads, writes, commits).
123
175
  * Automatically attached to every ScopeFacade created during traversal.
@@ -193,7 +245,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
193
245
  * chart with N stages you will typically get more entries here than from `getNarrative()`.
194
246
  */
195
247
  getFlowNarrative(): string[];
196
- run(options?: RunOptions): Promise<TraversalResult>;
248
+ run(options?: RunOptions): Promise<ExecutorResult>;
197
249
  getSnapshot(): RuntimeSnapshot;
198
250
  /** @internal */
199
251
  getRuntime(): import("../engine/types.js").IExecutionRuntime;
@@ -62,6 +62,10 @@ export declare class ScopeFacade {
62
62
  /** @internal */
63
63
  notifyStageEnd(duration?: number): void;
64
64
  /** @internal */
65
+ notifyPause(stageId: string, pauseData?: unknown): void;
66
+ /** @internal */
67
+ notifyResume(stageId: string, hasInput: boolean): void;
68
+ /** @internal */
65
69
  notifyCommit(mutations: CommitEvent['mutations']): void;
66
70
  /** Called by StageContext.commit() observer. Converts tracked writes to CommitEvent format.
67
71
  * Errors are caught to prevent recorder issues from aborting the traversal. */
@@ -4,10 +4,10 @@
4
4
  * Captures errors (always), mutations and reads (in verbose mode),
5
5
  * and stage lifecycle events for troubleshooting.
6
6
  */
7
- import type { ErrorEvent, ReadEvent, Recorder, StageEvent, WriteEvent } from '../types.js';
7
+ import type { ErrorEvent, PauseEvent, ReadEvent, Recorder, ResumeEvent, StageEvent, WriteEvent } from '../types.js';
8
8
  export type DebugVerbosity = 'minimal' | 'verbose';
9
9
  export interface DebugEntry {
10
- type: 'read' | 'write' | 'error' | 'stageStart' | 'stageEnd';
10
+ type: 'read' | 'write' | 'error' | 'stageStart' | 'stageEnd' | 'pause' | 'resume';
11
11
  stageName: string;
12
12
  timestamp: number;
13
13
  data: unknown;
@@ -42,6 +42,8 @@ export declare class DebugRecorder implements Recorder {
42
42
  onError(event: ErrorEvent): void;
43
43
  onStageStart(event: StageEvent): void;
44
44
  onStageEnd(event: StageEvent): void;
45
+ onPause(event: PauseEvent): void;
46
+ onResume(event: ResumeEvent): void;
45
47
  getEntries(): DebugEntry[];
46
48
  getErrors(): DebugEntry[];
47
49
  getEntriesForStage(stageName: string): DebugEntry[];
@@ -29,12 +29,13 @@
29
29
  * executor.attachRecorder(new MetricRecorder({ id: 'metrics' }));
30
30
  * ```
31
31
  */
32
- import type { CommitEvent, ReadEvent, Recorder, StageEvent, WriteEvent } from '../types.js';
32
+ import type { CommitEvent, PauseEvent, ReadEvent, Recorder, StageEvent, WriteEvent } from '../types.js';
33
33
  export interface StageMetrics {
34
34
  stageName: string;
35
35
  readCount: number;
36
36
  writeCount: number;
37
37
  commitCount: number;
38
+ pauseCount: number;
38
39
  totalDuration: number;
39
40
  invocationCount: number;
40
41
  }
@@ -43,6 +44,7 @@ export interface AggregatedMetrics {
43
44
  totalReads: number;
44
45
  totalWrites: number;
45
46
  totalCommits: number;
47
+ totalPauses: number;
46
48
  stageMetrics: Map<string, StageMetrics>;
47
49
  }
48
50
  /** Options for MetricRecorder. All fields are optional. */
@@ -72,6 +74,7 @@ export declare class MetricRecorder implements Recorder {
72
74
  onRead(event: ReadEvent): void;
73
75
  onWrite(event: WriteEvent): void;
74
76
  onCommit(event: CommitEvent): void;
77
+ onPause(event: PauseEvent): void;
75
78
  onStageStart(event: StageEvent): void;
76
79
  onStageEnd(event: StageEvent): void;
77
80
  getMetrics(): AggregatedMetrics;
@@ -38,6 +38,14 @@ export interface ErrorEvent extends RecorderContext {
38
38
  export interface StageEvent extends RecorderContext {
39
39
  duration?: number;
40
40
  }
41
+ export interface PauseEvent extends RecorderContext {
42
+ stageId: string;
43
+ pauseData?: unknown;
44
+ }
45
+ export interface ResumeEvent extends RecorderContext {
46
+ stageId: string;
47
+ hasInput: boolean;
48
+ }
41
49
  /**
42
50
  * Declarative redaction configuration — define once, applied everywhere.
43
51
  *
@@ -86,6 +94,8 @@ export interface Recorder {
86
94
  onError?(event: ErrorEvent): void;
87
95
  onStageStart?(event: StageEvent): void;
88
96
  onStageEnd?(event: StageEvent): void;
97
+ onPause?(event: PauseEvent): void;
98
+ onResume?(event: ResumeEvent): void;
89
99
  /** Reset state before each executor.run() — prevents cross-run accumulation. */
90
100
  clear?(): void;
91
101
  /** Expose collected data for inclusion in executor.getSnapshot().recorders. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
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",