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.
- package/CLAUDE.md +43 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/lib/builder/FlowChartBuilder.js +53 -1
- package/dist/esm/lib/builder/types.js +1 -1
- package/dist/esm/lib/engine/graph/StageNode.js +1 -1
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +14 -1
- package/dist/esm/lib/engine/handlers/DeciderHandler.js +7 -1
- package/dist/esm/lib/engine/handlers/SelectorHandler.js +7 -1
- package/dist/esm/lib/engine/handlers/StageRunner.js +13 -1
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +8 -1
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +39 -1
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +31 -1
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +10 -1
- package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
- package/dist/esm/lib/engine/narrative/narrativeTypes.js +1 -1
- package/dist/esm/lib/engine/narrative/types.js +1 -1
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +8 -1
- package/dist/esm/lib/engine/types.js +2 -1
- package/dist/esm/lib/pause/index.js +2 -0
- package/dist/esm/lib/pause/types.js +58 -0
- package/dist/esm/lib/reactive/types.js +2 -1
- package/dist/esm/lib/runner/ExecutionRuntime.js +11 -2
- package/dist/esm/lib/runner/FlowChartExecutor.js +232 -8
- package/dist/esm/lib/scope/ScopeFacade.js +21 -1
- package/dist/esm/lib/scope/recorders/DebugRecorder.js +19 -1
- package/dist/esm/lib/scope/recorders/MetricRecorder.js +10 -1
- package/dist/esm/lib/scope/types.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/builder/FlowChartBuilder.js +53 -1
- package/dist/lib/builder/types.js +1 -1
- package/dist/lib/engine/graph/StageNode.js +1 -1
- package/dist/lib/engine/handlers/ChildrenExecutor.js +14 -1
- package/dist/lib/engine/handlers/DeciderHandler.js +7 -1
- package/dist/lib/engine/handlers/SelectorHandler.js +7 -1
- package/dist/lib/engine/handlers/StageRunner.js +17 -5
- package/dist/lib/engine/handlers/SubflowExecutor.js +8 -1
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +39 -1
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +31 -1
- package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +10 -1
- package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +3 -1
- package/dist/lib/engine/narrative/narrativeTypes.js +1 -1
- package/dist/lib/engine/narrative/types.js +1 -1
- package/dist/lib/engine/traversal/FlowchartTraverser.js +8 -1
- package/dist/lib/engine/types.js +6 -2
- package/dist/lib/pause/index.js +8 -0
- package/dist/lib/pause/types.js +64 -0
- package/dist/lib/reactive/types.js +2 -1
- package/dist/lib/runner/ExecutionRuntime.js +11 -2
- package/dist/lib/runner/FlowChartExecutor.js +232 -8
- package/dist/lib/scope/ScopeFacade.js +21 -1
- package/dist/lib/scope/recorders/DebugRecorder.js +19 -1
- package/dist/lib/scope/recorders/MetricRecorder.js +10 -1
- package/dist/lib/scope/types.js +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +22 -0
- package/dist/types/lib/builder/types.d.ts +3 -1
- package/dist/types/lib/engine/graph/StageNode.d.ts +9 -0
- package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +9 -1
- package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +2 -0
- package/dist/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +3 -1
- package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +2 -0
- package/dist/types/lib/engine/narrative/narrativeTypes.d.ts +1 -1
- package/dist/types/lib/engine/narrative/types.d.ts +24 -0
- package/dist/types/lib/engine/types.d.ts +11 -0
- package/dist/types/lib/pause/index.d.ts +2 -0
- package/dist/types/lib/pause/types.d.ts +175 -0
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +4 -0
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +54 -2
- package/dist/types/lib/scope/ScopeFacade.d.ts +4 -0
- package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +4 -2
- package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +4 -1
- package/dist/types/lib/scope/types.d.ts +10 -0
- 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,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
|
|
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<
|
|
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