footprintjs 5.0.0 → 6.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.
- package/CLAUDE.md +32 -8
- package/dist/advanced.js +2 -3
- package/dist/esm/advanced.js +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/lib/builder/FlowChartBuilder.js +364 -100
- package/dist/esm/lib/builder/index.js +1 -1
- package/dist/esm/lib/builder/structure/StructureRecorder.js +158 -0
- package/dist/esm/lib/builder/structure/StructureRecorderDispatcher.js +171 -0
- package/dist/esm/lib/builder/types.js +1 -1
- package/dist/esm/lib/engine/errors/errorInfo.js +4 -3
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +11 -2
- package/dist/esm/lib/engine/handlers/DeciderHandler.js +5 -7
- package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +2 -2
- package/dist/esm/lib/engine/handlers/SelectorHandler.js +9 -7
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +7 -1
- package/dist/esm/lib/engine/handlers/index.js +1 -3
- package/dist/esm/lib/engine/handlers/types.js +2 -2
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +3 -3
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
- package/dist/esm/lib/engine/narrative/types.js +1 -1
- package/dist/esm/lib/engine/runtimeStageId.js +64 -2
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +11 -54
- package/dist/esm/lib/engine/types.js +1 -1
- package/dist/esm/lib/engine/walkSubflowSpec.js +144 -0
- package/dist/esm/lib/runner/FlowChartExecutor.js +4 -17
- package/dist/esm/lib/runner/getSubtreeSnapshot.js +3 -2
- package/dist/esm/trace.js +3 -2
- package/dist/index.js +1 -1
- package/dist/lib/builder/FlowChartBuilder.js +364 -100
- package/dist/lib/builder/index.js +1 -1
- package/dist/lib/builder/structure/StructureRecorder.js +159 -0
- package/dist/lib/builder/structure/StructureRecorderDispatcher.js +175 -0
- package/dist/lib/builder/types.js +1 -1
- package/dist/lib/engine/errors/errorInfo.js +4 -3
- package/dist/lib/engine/handlers/ChildrenExecutor.js +11 -2
- package/dist/lib/engine/handlers/DeciderHandler.js +5 -7
- package/dist/lib/engine/handlers/RuntimeStructureManager.js +2 -2
- package/dist/lib/engine/handlers/SelectorHandler.js +9 -7
- package/dist/lib/engine/handlers/SubflowExecutor.js +7 -1
- package/dist/lib/engine/handlers/index.js +2 -5
- package/dist/lib/engine/handlers/types.js +2 -2
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +3 -3
- package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
- package/dist/lib/engine/narrative/types.js +1 -1
- package/dist/lib/engine/runtimeStageId.js +66 -3
- package/dist/lib/engine/traversal/FlowchartTraverser.js +11 -54
- package/dist/lib/engine/types.js +1 -1
- package/dist/lib/engine/walkSubflowSpec.js +148 -0
- package/dist/lib/runner/FlowChartExecutor.js +4 -17
- package/dist/lib/runner/getSubtreeSnapshot.js +3 -2
- package/dist/trace.js +5 -2
- package/dist/types/advanced.d.ts +4 -4
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +110 -14
- package/dist/types/lib/builder/index.d.ts +2 -1
- package/dist/types/lib/builder/structure/StructureRecorder.d.ts +349 -0
- package/dist/types/lib/builder/structure/StructureRecorderDispatcher.d.ts +77 -0
- package/dist/types/lib/builder/types.d.ts +20 -6
- package/dist/types/lib/engine/errors/errorInfo.d.ts +3 -2
- package/dist/types/lib/engine/handlers/DeciderHandler.d.ts +3 -3
- package/dist/types/lib/engine/handlers/RuntimeStructureManager.d.ts +1 -1
- package/dist/types/lib/engine/handlers/SelectorHandler.d.ts +2 -2
- package/dist/types/lib/engine/handlers/index.d.ts +1 -2
- package/dist/types/lib/engine/handlers/types.d.ts +2 -9
- package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +2 -2
- package/dist/types/lib/engine/narrative/types.d.ts +24 -2
- package/dist/types/lib/engine/runtimeStageId.d.ts +57 -1
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +1 -6
- package/dist/types/lib/engine/types.d.ts +0 -26
- package/dist/types/lib/engine/walkSubflowSpec.d.ts +95 -0
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +4 -16
- package/dist/types/trace.d.ts +3 -1
- package/package.json +1 -1
- package/dist/esm/lib/engine/handlers/ExtractorRunner.js +0 -122
- package/dist/lib/engine/handlers/ExtractorRunner.js +0 -126
- package/dist/types/lib/engine/handlers/ExtractorRunner.d.ts +0 -41
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StructureRecorderDispatcher — Fans build-time structure events out to N
|
|
3
|
+
* attached `StructureRecorder` instances.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `FlowRecorderDispatcher` (engine/narrative/) exactly:
|
|
6
|
+
*
|
|
7
|
+
* - `recorders: StructureRecorder[]` — attach order preserved
|
|
8
|
+
* - per-event fire methods early-return when no recorders attached
|
|
9
|
+
* (zero-allocation fast path)
|
|
10
|
+
* - per-recorder try/catch isolates errors; one bad recorder cannot
|
|
11
|
+
* cascade into the chain build or sibling recorders
|
|
12
|
+
* - errors route to BOTH the dev-mode console warning (matches
|
|
13
|
+
* FlowRecorderDispatcher) AND a structured `buildErrors`
|
|
14
|
+
* accumulator so consumers can inspect failures post-build
|
|
15
|
+
* - spec payloads are NOT frozen at dispatch time — handlers must
|
|
16
|
+
* respect the `readonly` markers on event payload types (the
|
|
17
|
+
* builder still needs to mutate `spec.next` after the immediate
|
|
18
|
+
* `onStageAdded` fires; see `fireStageAdded` for the full note,
|
|
19
|
+
* and `StructureRecorder.ts` header "Spec mutation" for the
|
|
20
|
+
* trust-model implications)
|
|
21
|
+
*
|
|
22
|
+
* The dispatcher itself owns NO chart state. The builder owns the
|
|
23
|
+
* dispatcher; events fire from the natural mutation points in
|
|
24
|
+
* FlowChartBuilder (L7.3).
|
|
25
|
+
*/
|
|
26
|
+
import type { FlowChartSpec } from '../types.js';
|
|
27
|
+
import type { StructureDeciderCompleteEvent, StructureEdgeAddedEvent, StructureLoopEdgeAddedEvent, StructureRecorder, StructureStageAddedEvent, StructureSubflowMountedEvent } from './StructureRecorder.js';
|
|
28
|
+
/** Structured error captured when a recorder throws. Read post-build
|
|
29
|
+
* via `builder.getStructureBuildErrors()` — call on the BUILDER
|
|
30
|
+
* reference (NOT the chart returned by `.build()`). Capture the
|
|
31
|
+
* builder reference before `.build()` if you need post-build access. */
|
|
32
|
+
export interface StructureBuildError {
|
|
33
|
+
/** Which recorder's handler threw. */
|
|
34
|
+
readonly recorderId: string;
|
|
35
|
+
/** Which event method (`'onStageAdded'`, `'onEdgeAdded'`, ...). */
|
|
36
|
+
readonly method: string;
|
|
37
|
+
/** Error message extracted from the thrown value. */
|
|
38
|
+
readonly message: string;
|
|
39
|
+
/** The original thrown value — `Error` instance or whatever the
|
|
40
|
+
* recorder threw. Useful when diagnosis needs a stack trace. */
|
|
41
|
+
readonly error: unknown;
|
|
42
|
+
}
|
|
43
|
+
export declare class StructureRecorderDispatcher {
|
|
44
|
+
private recorders;
|
|
45
|
+
private readonly errors;
|
|
46
|
+
private _truncated;
|
|
47
|
+
/** Attach a `StructureRecorder`. Multiple recorders with the same
|
|
48
|
+
* id are allowed; the convention is one id per logical concern. */
|
|
49
|
+
attach(recorder: StructureRecorder): void;
|
|
50
|
+
/** Detach every recorder with the given id. */
|
|
51
|
+
detach(id: string): void;
|
|
52
|
+
/** Defensive copy of the attached recorders — used in tests + by
|
|
53
|
+
* tooling that wants to inspect what's registered. */
|
|
54
|
+
getRecorders(): StructureRecorder[];
|
|
55
|
+
/** Find one recorder by id. */
|
|
56
|
+
getRecorderById<T extends StructureRecorder = StructureRecorder>(id: string): T | undefined;
|
|
57
|
+
/** Read accumulated errors from this build. Returns a defensive copy. */
|
|
58
|
+
getErrors(): StructureBuildError[];
|
|
59
|
+
fireStageAdded(event: StructureStageAddedEvent): void;
|
|
60
|
+
fireEdgeAdded(event: StructureEdgeAddedEvent): void;
|
|
61
|
+
fireLoopEdgeAdded(event: StructureLoopEdgeAddedEvent): void;
|
|
62
|
+
fireDeciderComplete(event: StructureDeciderCompleteEvent): void;
|
|
63
|
+
fireSubflowMounted(event: StructureSubflowMountedEvent): void;
|
|
64
|
+
/**
|
|
65
|
+
* Externally-callable error capture for events the builder fires
|
|
66
|
+
* OUTSIDE the normal fire* fan-out path — specifically the seed
|
|
67
|
+
* replay in `FlowChartBuilder.attachStructureRecorder()`, which
|
|
68
|
+
* targets one specific recorder rather than every attached recorder.
|
|
69
|
+
*
|
|
70
|
+
* Same observability contract as the internal `recordError`:
|
|
71
|
+
* accumulates on `getErrors()` AND logs in dev mode.
|
|
72
|
+
*/
|
|
73
|
+
recordErrorForReplay(recorderId: string, method: string, err: unknown): void;
|
|
74
|
+
private recordError;
|
|
75
|
+
}
|
|
76
|
+
/** Re-export the FlowChartSpec type for downstream type completeness. */
|
|
77
|
+
export type { FlowChartSpec };
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* The builder remains standalone at runtime.
|
|
10
10
|
*/
|
|
11
11
|
import type { StageNode } from '../engine/graph/StageNode.js';
|
|
12
|
-
import type { ILogger, ScopeFactory, StageFunction
|
|
12
|
+
import type { ILogger, ScopeFactory, StageFunction } from '../engine/types.js';
|
|
13
13
|
import type { ScopeProtectionMode } from '../scope/protection/types.js';
|
|
14
|
+
import type { StructureRecorder } from './structure/StructureRecorder.js';
|
|
14
15
|
export type { ResumeFn, StageNode } from '../engine/graph/StageNode.js';
|
|
15
16
|
export type { ILogger, StageFunction, StreamCallback, StreamHandlers, StreamLifecycleHandler, StreamTokenHandler, SubflowMountOptions, } from '../engine/types.js';
|
|
16
17
|
export { ArrayMergeMode } from '../engine/types.js';
|
|
@@ -77,14 +78,27 @@ export interface FlowChartSpec {
|
|
|
77
78
|
/** True when this node is a back-edge reference created by loopTo() — not an executable stage. */
|
|
78
79
|
isLoopReference?: boolean;
|
|
79
80
|
}
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
export
|
|
81
|
+
/**
|
|
82
|
+
* Options-bag argument shape for the `flowChart()` factory.
|
|
83
|
+
*/
|
|
84
|
+
export interface FlowChartOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Build-time recorders to attach BEFORE `start()` fires. Equivalent
|
|
87
|
+
* to chaining `.attachStructureRecorder(rec)` immediately after the
|
|
88
|
+
* factory returns — but registered EARLIER, so even the seed event
|
|
89
|
+
* fires through the dispatcher without needing the seed-replay path.
|
|
90
|
+
*
|
|
91
|
+
* Multiple recorders attach in array order (same as
|
|
92
|
+
* `.attachStructureRecorder` repeated). See `StructureRecorder` JSDoc
|
|
93
|
+
* for event semantics, ordering invariants, and the trust model.
|
|
94
|
+
*/
|
|
95
|
+
structureRecorders?: StructureRecorder[];
|
|
96
|
+
/** Free-form description shown on the root spec node. */
|
|
97
|
+
description?: string;
|
|
98
|
+
}
|
|
84
99
|
export type FlowChart<TOut = any, TScope = any> = {
|
|
85
100
|
root: StageNode<TOut, TScope>;
|
|
86
101
|
stageMap: Map<string, StageFunction<TOut, TScope>>;
|
|
87
|
-
extractor?: TraversalExtractor;
|
|
88
102
|
subflows?: Record<string, {
|
|
89
103
|
root: StageNode<TOut, TScope>;
|
|
90
104
|
}>;
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* a single extraction point that preserves structured details while still
|
|
7
7
|
* producing a human-readable message.
|
|
8
8
|
*
|
|
9
|
-
* Consumers (narrative recorders,
|
|
10
|
-
* StructuredErrorInfo instead of a flat string, and can
|
|
9
|
+
* Consumers (narrative recorders, FlowRecorder.onError handlers, diagnostic
|
|
10
|
+
* collectors) receive StructuredErrorInfo instead of a flat string, and can
|
|
11
|
+
* decide how to render it.
|
|
11
12
|
* String-ification happens only at the final rendering boundary.
|
|
12
13
|
*/
|
|
13
14
|
import type { ValidationIssue } from '../../schema/errors.js';
|
|
@@ -8,8 +8,8 @@ import type { StageContext } from '../../memory/StageContext.js';
|
|
|
8
8
|
import type { StageNode } from '../graph/StageNode.js';
|
|
9
9
|
import type { TraversalContext } from '../narrative/types.js';
|
|
10
10
|
import type { HandlerDeps, StageFunction } from '../types.js';
|
|
11
|
-
import type {
|
|
12
|
-
export type {
|
|
11
|
+
import type { ExecuteNodeFn, RunStageFn } from './types.js';
|
|
12
|
+
export type { ExecuteNodeFn, RunStageFn };
|
|
13
13
|
export declare class DeciderHandler<TOut = any, TScope = any> {
|
|
14
14
|
private readonly deps;
|
|
15
15
|
constructor(deps: HandlerDeps<TOut, TScope>);
|
|
@@ -20,5 +20,5 @@ export declare class DeciderHandler<TOut = any, TScope = any> {
|
|
|
20
20
|
*/
|
|
21
21
|
handleScopeBased(node: StageNode<TOut, TScope>, stageFunc: StageFunction<TOut, TScope>, context: StageContext, breakFlag: {
|
|
22
22
|
shouldBreak: boolean;
|
|
23
|
-
}, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, executeNode: ExecuteNodeFn<TOut, TScope>,
|
|
23
|
+
}, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, executeNode: ExecuteNodeFn<TOut, TScope>, traversalContext?: TraversalContext): Promise<any>;
|
|
24
24
|
}
|
|
@@ -11,7 +11,7 @@ import type { StageNode } from '../graph/StageNode.js';
|
|
|
11
11
|
import type { SerializedPipelineStructure } from '../types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Compute the node type from node properties.
|
|
14
|
-
*
|
|
14
|
+
* Used by RuntimeStructureManager for serialization.
|
|
15
15
|
*/
|
|
16
16
|
export declare function computeNodeType(node: StageNode): 'stage' | 'decider' | 'selector' | 'fork' | 'streaming' | 'subflow' | 'loop';
|
|
17
17
|
export declare class RuntimeStructureManager {
|
|
@@ -11,7 +11,7 @@ import type { StageNode } from '../graph/StageNode.js';
|
|
|
11
11
|
import type { TraversalContext } from '../narrative/types.js';
|
|
12
12
|
import type { HandlerDeps, NodeResultType, StageFunction } from '../types.js';
|
|
13
13
|
import type { ChildrenExecutor } from './ChildrenExecutor.js';
|
|
14
|
-
import type {
|
|
14
|
+
import type { ExecuteNodeFn, RunStageFn } from './types.js';
|
|
15
15
|
export declare class SelectorHandler<TOut = any, TScope = any> {
|
|
16
16
|
private readonly deps;
|
|
17
17
|
private readonly childrenExecutor;
|
|
@@ -23,5 +23,5 @@ export declare class SelectorHandler<TOut = any, TScope = any> {
|
|
|
23
23
|
*/
|
|
24
24
|
handleScopeBased(node: StageNode<TOut, TScope>, stageFunc: StageFunction<TOut, TScope>, context: StageContext, breakFlag: {
|
|
25
25
|
shouldBreak: boolean;
|
|
26
|
-
}, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, executeNode: ExecuteNodeFn<TOut, TScope>,
|
|
26
|
+
}, branchPath: string | undefined, runStage: RunStageFn<TOut, TScope>, executeNode: ExecuteNodeFn<TOut, TScope>, traversalContext?: TraversalContext): Promise<Record<string, NodeResultType>>;
|
|
27
27
|
}
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { StageRunner } from './StageRunner.js';
|
|
5
5
|
export { NodeResolver } from './NodeResolver.js';
|
|
6
|
-
export type {
|
|
6
|
+
export type { ExecuteNodeFn, RunStageFn } from './types.js';
|
|
7
7
|
export { ChildrenExecutor } from './ChildrenExecutor.js';
|
|
8
8
|
export { DeciderHandler } from './DeciderHandler.js';
|
|
9
9
|
export { SelectorHandler } from './SelectorHandler.js';
|
|
10
10
|
export { ContinuationResolver, DEFAULT_MAX_ITERATIONS } from './ContinuationResolver.js';
|
|
11
11
|
export { SubflowExecutor } from './SubflowExecutor.js';
|
|
12
12
|
export { applyOutputMapping, createSubflowHandlerDeps, extractParentScopeValues, getInitialScopeValues, seedSubflowGlobalStore, } from './SubflowInputMapper.js';
|
|
13
|
-
export { ExtractorRunner } from './ExtractorRunner.js';
|
|
14
13
|
export { computeNodeType, RuntimeStructureManager } from './RuntimeStructureManager.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* handlers/types.ts — Shared callback types for all handler modules.
|
|
3
3
|
*
|
|
4
|
-
* Avoids duplicate definitions of ExecuteNodeFn /
|
|
4
|
+
* Avoids duplicate definitions of ExecuteNodeFn / RunStageFn / etc.
|
|
5
5
|
* across ChildrenExecutor, DeciderHandler, ContinuationResolver, SubflowExecutor.
|
|
6
6
|
* All types are callbacks that break circular dependencies with FlowchartTraverser.
|
|
7
7
|
*/
|
|
@@ -28,12 +28,5 @@ export interface BreakFlag {
|
|
|
28
28
|
}
|
|
29
29
|
/** Recursive node execution — avoids circular dep with FlowchartTraverser. */
|
|
30
30
|
export type ExecuteNodeFn<TOut = any, TScope = any> = (node: StageNode<TOut, TScope>, context: StageContext, breakFlag: BreakFlag, branchPath?: string) => Promise<any>;
|
|
31
|
-
/** Run a stage function with commit
|
|
31
|
+
/** Run a stage function with commit. */
|
|
32
32
|
export type RunStageFn<TOut = any, TScope = any> = (node: StageNode<TOut, TScope>, stageFunc: StageFunction<TOut, TScope>, context: StageContext, breakFn: () => void) => Promise<TOut>;
|
|
33
|
-
/** Call the traversal extractor after stage execution. */
|
|
34
|
-
export type CallExtractorFn<TOut = any, TScope = any> = (node: StageNode<TOut, TScope>, context: StageContext, stagePath: string, stageOutput?: unknown, errorInfo?: {
|
|
35
|
-
type: string;
|
|
36
|
-
message: string;
|
|
37
|
-
}) => void;
|
|
38
|
-
/** Compute the stage path string for extractor and narrative. */
|
|
39
|
-
export type GetStagePathFn<TOut = any, TScope = any> = (node: StageNode<TOut, TScope>, branchPath?: string, contextStageName?: string) => string;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* When no recorders are attached, every method is a fast no-op (empty array check).
|
|
11
11
|
*/
|
|
12
12
|
import type { DecisionEvidence, SelectionEvidence } from '../../decide/types.js';
|
|
13
|
-
import type { FlowRecorder, IControlFlowNarrative, TraversalContext } from './types.js';
|
|
13
|
+
import type { FlowRecorder, IControlFlowNarrative, StageType, TraversalContext } from './types.js';
|
|
14
14
|
export declare class FlowRecorderDispatcher implements IControlFlowNarrative {
|
|
15
15
|
private recorders;
|
|
16
16
|
/** Attach a FlowRecorder. Duplicate IDs are allowed (same as scope ScopeRecorder). */
|
|
@@ -21,7 +21,7 @@ export declare class FlowRecorderDispatcher implements IControlFlowNarrative {
|
|
|
21
21
|
getScopeRecorders(): FlowRecorder[];
|
|
22
22
|
/** Find a recorder by ID. Useful for retrieving built-in recorders like NarrativeFlowRecorder. */
|
|
23
23
|
getRecorderById<T extends FlowRecorder = FlowRecorder>(id: string): T | undefined;
|
|
24
|
-
onStageExecuted(stageName: string, description
|
|
24
|
+
onStageExecuted(stageName: string, description: string | undefined, traversalContext: TraversalContext | undefined, stageType: StageType): void;
|
|
25
25
|
onNext(fromStage: string, toStage: string, description?: string, traversalContext?: TraversalContext): void;
|
|
26
26
|
onDecision(deciderName: string, chosenBranch: string, rationale?: string, deciderDescription?: string, traversalContext?: TraversalContext, evidence?: DecisionEvidence): void;
|
|
27
27
|
onFork(parentStage: string, childNames: string[], traversalContext?: TraversalContext): void;
|
|
@@ -13,9 +13,25 @@ import type { StructuredErrorInfo } from '../errors/errorInfo.js';
|
|
|
13
13
|
* Uses Null Object pattern: NullControlFlowNarrativeGenerator satisfies this
|
|
14
14
|
* interface with empty methods for zero-cost disabled path.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The kind of stage that completed. Lets consumers route uniform
|
|
18
|
+
* "did this stage execute" handling without a side-table lookup into
|
|
19
|
+
* the chart spec. Required on every `onStageExecuted` event since
|
|
20
|
+
* proposal #003 unified the event to fire for ALL stage kinds.
|
|
21
|
+
*/
|
|
22
|
+
export type StageType = 'linear' | 'decider' | 'fork' | 'selector' | 'subflow-mount';
|
|
16
23
|
export interface IControlFlowNarrative {
|
|
17
|
-
/**
|
|
18
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Called when a stage completes its main work. Fires for ALL stage
|
|
26
|
+
* kinds (`'linear'`, `'decider'`, `'fork'`, `'selector'`, `'subflow-mount'`)
|
|
27
|
+
* AFTER the corresponding specialized event (`onDecision`, `onFork`,
|
|
28
|
+
* `onSelected`, `onSubflowEntry`). For linear stages, fires after
|
|
29
|
+
* the stage function returns.
|
|
30
|
+
*
|
|
31
|
+
* Consumers tracking "did this stage run?" use this event uniformly
|
|
32
|
+
* and switch on `stageType` for kind-specific work.
|
|
33
|
+
*/
|
|
34
|
+
onStageExecuted(stageName: string, description: string | undefined, traversalContext: TraversalContext | undefined, stageType: StageType): void;
|
|
19
35
|
/** Called on linear continuation from one stage to the next. */
|
|
20
36
|
onNext(fromStage: string, toStage: string, description?: string, traversalContext?: TraversalContext): void;
|
|
21
37
|
/** Called when a decider selects a branch. Most valuable for LLM context. */
|
|
@@ -115,6 +131,12 @@ export interface FlowStageEvent {
|
|
|
115
131
|
description?: string;
|
|
116
132
|
/** Traversal context from the engine — read-only, set by traverser. */
|
|
117
133
|
traversalContext?: TraversalContext;
|
|
134
|
+
/**
|
|
135
|
+
* Which kind of stage completed. The engine fires `onStageExecuted`
|
|
136
|
+
* uniformly for every stage kind (proposal #003); consumers route by
|
|
137
|
+
* `stageType` without a chart-spec lookup.
|
|
138
|
+
*/
|
|
139
|
+
stageType: StageType;
|
|
118
140
|
}
|
|
119
141
|
/** Event passed to FlowRecorder.onNext. */
|
|
120
142
|
export interface FlowNextEvent {
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
* - Human-readable ('sf-tools/execute-tool-calls#8')
|
|
15
15
|
* - Parseable (split on '#' for stageId and index, split stageId on '/' for subflow path)
|
|
16
16
|
*
|
|
17
|
+
* Naming-collision warning
|
|
18
|
+
* ────────────────────────
|
|
19
|
+
* The parsed-output `.stageId` field below is the LOCAL form (segment
|
|
20
|
+
* after the last '/'). This is NOT the same as `spec.id` / `node.id`
|
|
21
|
+
* for subflow-nested stages, which carry the FULL prefixed form
|
|
22
|
+
* (`'sf-tools/execute-tool-calls'`). To compare safely, use
|
|
23
|
+
* `splitStageId(spec.id)` to decompose the prefixed form the same
|
|
24
|
+
* way `parseRuntimeStageId` decomposes a runtimeStageId.
|
|
25
|
+
*
|
|
17
26
|
* @example
|
|
18
27
|
* ```
|
|
19
28
|
* buildRuntimeStageId('call-llm', 5) // 'call-llm#5'
|
|
@@ -29,12 +38,59 @@
|
|
|
29
38
|
* consumers constructing IDs from parsed components (round-trip via parseRuntimeStageId).
|
|
30
39
|
*/
|
|
31
40
|
export declare function buildRuntimeStageId(stageId: string, executionIndex: number, subflowPath?: string): string;
|
|
32
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Parse a runtimeStageId into its components.
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT — naming collision: the returned `stageId` is the LOCAL
|
|
45
|
+
* form (the segment between the last '/' and the '#'). This is NOT
|
|
46
|
+
* the same as `spec.id` or `node.id` for subflow-nested stages,
|
|
47
|
+
* which contain the FULL prefixed form.
|
|
48
|
+
*
|
|
49
|
+
* parseRuntimeStageId('sf-tools/execute-tool-calls#8').stageId
|
|
50
|
+
* // → 'execute-tool-calls' (LOCAL)
|
|
51
|
+
*
|
|
52
|
+
* node.id // (post-mount, in a spec that contains subflows)
|
|
53
|
+
* // → 'sf-tools/execute-tool-calls' (FULL prefixed)
|
|
54
|
+
*
|
|
55
|
+
* To compare these two safely, use `splitStageId(node.id)` to get
|
|
56
|
+
* the local form, OR reconstruct the full form via
|
|
57
|
+
* `(subflowPath ? subflowPath + '/' : '') + stageId`.
|
|
58
|
+
*/
|
|
33
59
|
export declare function parseRuntimeStageId(runtimeStageId: string): {
|
|
34
60
|
stageId: string;
|
|
35
61
|
executionIndex: number;
|
|
36
62
|
subflowPath: string | undefined;
|
|
37
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Decompose a (possibly prefixed) stage id into its components.
|
|
66
|
+
*
|
|
67
|
+
* Use this when you have an id WITHOUT the `#N` execution suffix and
|
|
68
|
+
* need the local stage name and/or the subflow path. Common sources
|
|
69
|
+
* of such ids:
|
|
70
|
+
* - `spec.id` (post-mount the id includes any subflow prefix)
|
|
71
|
+
* - `CommitBundle.stageId` (post-mount id)
|
|
72
|
+
* - `node.id` from xyflow nodes built off the spec
|
|
73
|
+
* - the segment of `runtimeStageId` BEFORE the `#` (use
|
|
74
|
+
* `parseRuntimeStageId` directly for full runtimeStageId strings)
|
|
75
|
+
*
|
|
76
|
+
* Mirrors the decomposition `parseRuntimeStageId` performs on the
|
|
77
|
+
* stageId portion of a runtimeStageId, so the two helpers stay in
|
|
78
|
+
* lockstep on naming and behavior.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* splitStageId('sf-tools/execute-tool-calls')
|
|
82
|
+
* // → { localStageId: 'execute-tool-calls', subflowPath: 'sf-tools' }
|
|
83
|
+
*
|
|
84
|
+
* splitStageId('execute-tool-calls')
|
|
85
|
+
* // → { localStageId: 'execute-tool-calls', subflowPath: undefined }
|
|
86
|
+
*
|
|
87
|
+
* splitStageId('sf-outer/sf-inner/validate')
|
|
88
|
+
* // → { localStageId: 'validate', subflowPath: 'sf-outer/sf-inner' }
|
|
89
|
+
*/
|
|
90
|
+
export declare function splitStageId(prefixedStageId: string): {
|
|
91
|
+
localStageId: string;
|
|
92
|
+
subflowPath: string | undefined;
|
|
93
|
+
};
|
|
38
94
|
/**
|
|
39
95
|
* Shared mutable counter for execution index.
|
|
40
96
|
* Passed by reference to child traversers (subflows) so they
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import type { ScopeProtectionMode } from '../../scope/protection/types.js';
|
|
22
22
|
import { FlowRecorderDispatcher } from '../narrative/FlowRecorderDispatcher.js';
|
|
23
23
|
import type { FlowRecorder, IControlFlowNarrative } from '../narrative/types.js';
|
|
24
|
-
import type {
|
|
24
|
+
import type { IExecutionRuntime, ILogger, ScopeFactory, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowResult, TraversalResult } from '../types.js';
|
|
25
25
|
export interface TraverserOptions<TOut = any, TScope = any> {
|
|
26
26
|
root: StageNode<TOut, TScope>;
|
|
27
27
|
stageMap: Map<string, StageFunction<TOut, TScope>>;
|
|
@@ -32,12 +32,10 @@ export interface TraverserOptions<TOut = any, TScope = any> {
|
|
|
32
32
|
executionEnv?: import('../../engine/types').ExecutionEnv;
|
|
33
33
|
throttlingErrorChecker?: (error: unknown) => boolean;
|
|
34
34
|
streamHandlers?: StreamHandlers;
|
|
35
|
-
extractor?: TraversalExtractor;
|
|
36
35
|
scopeProtectionMode?: ScopeProtectionMode;
|
|
37
36
|
subflows?: Record<string, {
|
|
38
37
|
root: StageNode<TOut, TScope>;
|
|
39
38
|
}>;
|
|
40
|
-
enrichSnapshots?: boolean;
|
|
41
39
|
narrativeEnabled?: boolean;
|
|
42
40
|
buildTimeStructure?: SerializedPipelineStructure;
|
|
43
41
|
logger: ILogger;
|
|
@@ -103,7 +101,6 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
103
101
|
private readonly deciderHandler;
|
|
104
102
|
private readonly selectorHandler;
|
|
105
103
|
private readonly structureManager;
|
|
106
|
-
private readonly extractorRunner;
|
|
107
104
|
private readonly narrativeGenerator;
|
|
108
105
|
private readonly flowRecorderDispatcher;
|
|
109
106
|
private subflowResults;
|
|
@@ -194,8 +191,6 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
194
191
|
getBranchIds(): string[];
|
|
195
192
|
getRuntimeRoot(): StageNode;
|
|
196
193
|
getSubflowResults(): Map<string, SubflowResult>;
|
|
197
|
-
getExtractedResults<TResult = unknown>(): Map<string, TResult>;
|
|
198
|
-
getExtractorErrors(): ExtractorError[];
|
|
199
194
|
getNarrative(): string[];
|
|
200
195
|
/** Returns the FlowRecorderDispatcher, or undefined if narrative is disabled. */
|
|
201
196
|
getFlowRecorderDispatcher(): FlowRecorderDispatcher | undefined;
|
|
@@ -323,32 +323,6 @@ export interface RuntimeStructureMetadata {
|
|
|
323
323
|
isLoopReference?: boolean;
|
|
324
324
|
streamId?: string;
|
|
325
325
|
}
|
|
326
|
-
export interface StageSnapshot<TOut = any, TScope = any> {
|
|
327
|
-
node: StageNode<TOut, TScope>;
|
|
328
|
-
context: StageContext;
|
|
329
|
-
stepNumber: number;
|
|
330
|
-
structureMetadata: RuntimeStructureMetadata;
|
|
331
|
-
scopeState?: Record<string, unknown>;
|
|
332
|
-
debugInfo?: {
|
|
333
|
-
logs: Record<string, unknown>;
|
|
334
|
-
errors: Record<string, unknown>;
|
|
335
|
-
metrics: Record<string, unknown>;
|
|
336
|
-
evals: Record<string, unknown>;
|
|
337
|
-
flowMessages?: FlowMessage[];
|
|
338
|
-
};
|
|
339
|
-
stageOutput?: unknown;
|
|
340
|
-
errorInfo?: {
|
|
341
|
-
type: string;
|
|
342
|
-
message: string;
|
|
343
|
-
};
|
|
344
|
-
historyIndex?: number;
|
|
345
|
-
}
|
|
346
|
-
export type TraversalExtractor<TResult = unknown> = (snapshot: StageSnapshot) => TResult | undefined | null;
|
|
347
|
-
export interface ExtractorError {
|
|
348
|
-
stagePath: string;
|
|
349
|
-
message: string;
|
|
350
|
-
error: unknown;
|
|
351
|
-
}
|
|
352
326
|
export type NodeResultType = {
|
|
353
327
|
id: string;
|
|
354
328
|
result: unknown;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* walkSubflowSpec — yield the structural shape of a subflow spec as a
|
|
3
|
+
* flat ordered stream of items, with `subflowPath` already composed
|
|
4
|
+
* for nested subflows.
|
|
5
|
+
*
|
|
6
|
+
* This is the public contract for traversing the structure delivered
|
|
7
|
+
* via `StructureSubflowMountedEvent.subflowSpec`. Item shapes mirror
|
|
8
|
+
* the corresponding Structure event payloads so consumers can route
|
|
9
|
+
* walker items through the same handlers they use for live events.
|
|
10
|
+
*
|
|
11
|
+
* Walker contract (LOCKED):
|
|
12
|
+
* 1. AUTO-RECURSE by default into nested subflows, with composed
|
|
13
|
+
* paths (`parent/child/...`). Pass `{ recurse: false }` to walk
|
|
14
|
+
* only one level.
|
|
15
|
+
* 2. ENTRY-STAGE MARKER FIRST: for each subflow (top-level and
|
|
16
|
+
* nested), yields a `{ kind: 'subflow-start', ... }` item BEFORE
|
|
17
|
+
* any stage/edge items from that subflow. Lets consumers draw the
|
|
18
|
+
* boundary edge from the mount node to the entry stage.
|
|
19
|
+
* 3. COMPOSED PATHS: nested subflows get `parentPath + '/' + localId`.
|
|
20
|
+
* Top-level mount paths are local-only (`'auth'`, NOT `'__root__/auth'`).
|
|
21
|
+
* 4. SHAPE MIRRORING: stage/edge/loop items have the same payload
|
|
22
|
+
* shape as Structure events, with `subflowPath` added.
|
|
23
|
+
* 5. SOURCE DISCRIMINATOR: every walker item carries `source: 'walker'`
|
|
24
|
+
* (Structure events do NOT). Lets consumers distinguish event vs
|
|
25
|
+
* walker in logs/debuggers while still sharing handler code paths.
|
|
26
|
+
* 6. STAGE-ID PREFIXING: stage IDs in nested subflows are already
|
|
27
|
+
* prefixed by the spec (e.g. `'auth/verify/check'`). Walker
|
|
28
|
+
* preserves this; `subflowPath` field is redundant-but-explicit.
|
|
29
|
+
*/
|
|
30
|
+
import type { SerializedPipelineStructure } from '../builder/types.js';
|
|
31
|
+
export interface WalkerOptions {
|
|
32
|
+
/** Auto-recurse into nested subflows (default: true). When false,
|
|
33
|
+
* nested subflow items are yielded but their internals are not
|
|
34
|
+
* traversed. */
|
|
35
|
+
recurse?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export type WalkerItem = {
|
|
38
|
+
kind: 'subflow-start';
|
|
39
|
+
stageId: string;
|
|
40
|
+
subflowPath: string;
|
|
41
|
+
source: 'walker';
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'stage';
|
|
44
|
+
stageId: string;
|
|
45
|
+
name: string;
|
|
46
|
+
type: NonNullable<SerializedPipelineStructure['type']>;
|
|
47
|
+
isPausable?: boolean;
|
|
48
|
+
spec: SerializedPipelineStructure;
|
|
49
|
+
subflowPath: string;
|
|
50
|
+
source: 'walker';
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'edge';
|
|
53
|
+
from: string;
|
|
54
|
+
to: string;
|
|
55
|
+
edgeKind: 'next' | 'fork-branch' | 'decision-branch';
|
|
56
|
+
label?: string;
|
|
57
|
+
subflowPath: string;
|
|
58
|
+
source: 'walker';
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'loop';
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
subflowPath: string;
|
|
64
|
+
source: 'walker';
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'subflow';
|
|
67
|
+
mountStageId: string;
|
|
68
|
+
subflowId: string;
|
|
69
|
+
subflowName: string;
|
|
70
|
+
subflowSpec: SerializedPipelineStructure;
|
|
71
|
+
subflowPath: string;
|
|
72
|
+
source: 'walker';
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Walk a subflow spec, yielding its structure as flat ordered items.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { walkSubflowSpec } from 'footprintjs/trace';
|
|
80
|
+
*
|
|
81
|
+
* onSubflowMounted(event) {
|
|
82
|
+
* if (!event.subflowSpec) return; // lazy mount — no spec yet
|
|
83
|
+
* for (const item of walkSubflowSpec(event.subflowSpec, event.subflowPath)) {
|
|
84
|
+
* switch (item.kind) {
|
|
85
|
+
* case 'subflow-start': break; // entry boundary
|
|
86
|
+
* case 'stage': break; // inner stage
|
|
87
|
+
* case 'edge': break; // inner edge
|
|
88
|
+
* case 'loop': break; // inner loop back-edge
|
|
89
|
+
* case 'subflow': break; // nested mount marker
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function walkSubflowSpec(spec: SerializedPipelineStructure, subflowPath: string, options?: WalkerOptions): Generator<WalkerItem, void, void>;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* const executor = new FlowChartExecutor(chart);
|
|
10
10
|
*
|
|
11
11
|
* // Options-object form (preferred when you need to customize behavior):
|
|
12
|
-
* const executor = new FlowChartExecutor(chart, { scopeFactory: myFactory
|
|
12
|
+
* const executor = new FlowChartExecutor(chart, { scopeFactory: myFactory });
|
|
13
13
|
*
|
|
14
14
|
* // 2-param form (accepts a ScopeFactory directly, for backward compatibility):
|
|
15
15
|
* const executor = new FlowChartExecutor(chart, myFactory);
|
|
@@ -21,7 +21,7 @@ import type { CombinedNarrativeRecorderOptions } from '../engine/narrative/Combi
|
|
|
21
21
|
import type { CombinedNarrativeEntry } from '../engine/narrative/narrativeTypes.js';
|
|
22
22
|
import type { ManifestEntry } from '../engine/narrative/recorders/ManifestFlowRecorder.js';
|
|
23
23
|
import type { FlowRecorder } from '../engine/narrative/types.js';
|
|
24
|
-
import { type ExecutorResult, type
|
|
24
|
+
import { type ExecutorResult, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult } from '../engine/types.js';
|
|
25
25
|
import type { FlowchartCheckpoint } from '../pause/types.js';
|
|
26
26
|
import type { CombinedRecorder } from '../recorder/CombinedRecorder.js';
|
|
27
27
|
import type { EmitRecorder } from '../recorder/EmitRecorder.js';
|
|
@@ -34,7 +34,7 @@ import { type RuntimeSnapshot } from './ExecutionRuntime.js';
|
|
|
34
34
|
* ```typescript
|
|
35
35
|
* const ex = new FlowChartExecutor(chart, {
|
|
36
36
|
* scopeFactory: myFactory,
|
|
37
|
-
*
|
|
37
|
+
* defaultValuesForContext: { ... },
|
|
38
38
|
* });
|
|
39
39
|
* ```
|
|
40
40
|
*
|
|
@@ -50,14 +50,6 @@ import { type RuntimeSnapshot } from './ExecutionRuntime.js';
|
|
|
50
50
|
export interface FlowChartExecutorOptions<TScope = any> {
|
|
51
51
|
/** Custom scope factory. Defaults to TypedScope or ScopeFacade auto-detection. */
|
|
52
52
|
scopeFactory?: ScopeFactory<TScope>;
|
|
53
|
-
/**
|
|
54
|
-
* Attach a per-stage scope snapshot to each extractor result. When `true`, the
|
|
55
|
-
* extraction callback receives the full shared state at the point that stage
|
|
56
|
-
* committed — useful for debugging multi-stage state transitions. Defaults to
|
|
57
|
-
* `false` (no scope snapshot attached). Can also be set on the chart via
|
|
58
|
-
* `flowChart(...).enrichSnapshots(true)`.
|
|
59
|
-
*/
|
|
60
|
-
enrichSnapshots?: boolean;
|
|
61
53
|
/**
|
|
62
54
|
* Default values pre-populated into the shared context before **each** stage
|
|
63
55
|
* (re-applied every stage, acting as baseline defaults).
|
|
@@ -121,7 +113,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
121
113
|
*
|
|
122
114
|
* **Options object form** (preferred):
|
|
123
115
|
* ```typescript
|
|
124
|
-
* new FlowChartExecutor(chart, { scopeFactory,
|
|
116
|
+
* new FlowChartExecutor(chart, { scopeFactory, defaultValuesForContext })
|
|
125
117
|
* ```
|
|
126
118
|
*
|
|
127
119
|
* **2-param form** (also supported):
|
|
@@ -419,10 +411,6 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
419
411
|
getRuntimeStructure(): SerializedPipelineStructure | undefined;
|
|
420
412
|
/** @internal */
|
|
421
413
|
getSubflowResults(): Map<string, SubflowResult>;
|
|
422
|
-
/** @internal */
|
|
423
|
-
getExtractedResults<TResult = unknown>(): Map<string, TResult>;
|
|
424
|
-
/** @internal */
|
|
425
|
-
getExtractorErrors(): ExtractorError[];
|
|
426
414
|
/**
|
|
427
415
|
* Returns the subflow manifest from an attached ManifestFlowRecorder.
|
|
428
416
|
* Returns empty array if no ManifestFlowRecorder is attached.
|
package/dist/types/trace.d.ts
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export type { ExecutionCounter } from './lib/engine/runtimeStageId.js';
|
|
24
|
-
export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId } from './lib/engine/runtimeStageId.js';
|
|
24
|
+
export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId, splitStageId, } from './lib/engine/runtimeStageId.js';
|
|
25
|
+
export type { WalkerItem, WalkerOptions } from './lib/engine/walkSubflowSpec.js';
|
|
26
|
+
export { walkSubflowSpec } from './lib/engine/walkSubflowSpec.js';
|
|
25
27
|
export { findCommit, findCommits, findLastWriter } from './lib/memory/commitLogUtils.js';
|
|
26
28
|
export type { CausalChainOptions, CausalNode, KeysReadLookup } from './lib/memory/backtrack.js';
|
|
27
29
|
export { causalChain, flattenCausalDAG, formatCausalChain } from './lib/memory/backtrack.js';
|
package/package.json
CHANGED