footprintjs 4.17.2 → 5.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 +39 -1
- package/dist/advanced.js +1 -1
- package/dist/esm/advanced.js +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/lib/builder/FlowChartBuilder.js +32 -7
- package/dist/esm/lib/decide/decide.js +9 -9
- package/dist/esm/lib/decide/evidence.js +2 -2
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +3 -3
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +3 -3
- package/dist/esm/lib/engine/narrative/types.js +1 -1
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +20 -3
- package/dist/esm/lib/memory/StageContext.js +3 -3
- package/dist/esm/lib/memory/backtrack.js +1 -1
- package/dist/esm/lib/reactive/createTypedScope.js +5 -5
- package/dist/esm/lib/reactive/types.js +7 -7
- package/dist/esm/lib/recorder/BoundaryStateStore.js +167 -0
- package/dist/esm/lib/recorder/BoundaryStateTracker.js +4 -4
- package/dist/esm/lib/recorder/CombinedRecorder.js +9 -9
- package/dist/esm/lib/recorder/CommitRangeIndex.js +207 -0
- package/dist/esm/lib/recorder/CompositeRecorder.js +6 -6
- package/dist/esm/lib/recorder/EmitRecorder.js +3 -3
- package/dist/esm/lib/recorder/InOutRecorder.js +1 -1
- package/dist/esm/lib/recorder/KeyedStore.js +113 -0
- package/dist/esm/lib/recorder/QualityRecorder.js +2 -2
- package/dist/esm/lib/recorder/SequenceRecorder.js +2 -2
- package/dist/esm/lib/recorder/SequenceStore.js +195 -0
- package/dist/esm/lib/recorder/TopologyRecorder.js +1 -1
- package/dist/esm/lib/recorder/index.js +13 -3
- package/dist/esm/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/esm/lib/runner/FlowChartExecutor.js +72 -28
- package/dist/esm/lib/runner/RunContext.js +2 -2
- package/dist/esm/lib/runner/RunnableChart.js +1 -1
- package/dist/esm/lib/runner/runId.js +65 -0
- package/dist/esm/lib/scope/ScopeFacade.js +6 -6
- package/dist/esm/lib/scope/index.js +1 -1
- package/dist/esm/lib/scope/recorders/DebugRecorder.js +4 -4
- package/dist/esm/lib/scope/recorders/MetricRecorder.js +2 -2
- package/dist/esm/lib/scope/recorders/index.js +1 -1
- package/dist/esm/lib/scope/types.js +1 -1
- package/dist/esm/recorders.js +1 -1
- package/dist/esm/trace.js +13 -1
- package/dist/index.js +3 -3
- package/dist/lib/builder/FlowChartBuilder.js +32 -7
- package/dist/lib/decide/decide.js +9 -9
- package/dist/lib/decide/evidence.js +2 -2
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +3 -3
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +3 -3
- package/dist/lib/engine/narrative/types.js +1 -1
- package/dist/lib/engine/traversal/FlowchartTraverser.js +20 -3
- package/dist/lib/memory/StageContext.js +3 -3
- package/dist/lib/memory/backtrack.js +1 -1
- package/dist/lib/reactive/createTypedScope.js +5 -5
- package/dist/lib/reactive/types.js +7 -7
- package/dist/lib/recorder/BoundaryStateStore.js +171 -0
- package/dist/lib/recorder/BoundaryStateTracker.js +4 -4
- package/dist/lib/recorder/CombinedRecorder.js +9 -9
- package/dist/lib/recorder/CommitRangeIndex.js +211 -0
- package/dist/lib/recorder/CompositeRecorder.js +6 -6
- package/dist/lib/recorder/EmitRecorder.js +3 -3
- package/dist/lib/recorder/InOutRecorder.js +1 -1
- package/dist/lib/recorder/KeyedStore.js +117 -0
- package/dist/lib/recorder/QualityRecorder.js +2 -2
- package/dist/lib/recorder/SequenceRecorder.js +2 -2
- package/dist/lib/recorder/SequenceStore.js +199 -0
- package/dist/lib/recorder/TopologyRecorder.js +1 -1
- package/dist/lib/recorder/index.js +19 -6
- package/dist/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/lib/runner/FlowChartExecutor.js +72 -28
- package/dist/lib/runner/RunContext.js +2 -2
- package/dist/lib/runner/RunnableChart.js +1 -1
- package/dist/lib/runner/runId.js +70 -0
- package/dist/lib/scope/ScopeFacade.js +6 -6
- package/dist/lib/scope/index.js +1 -1
- package/dist/lib/scope/recorders/DebugRecorder.js +4 -4
- package/dist/lib/scope/recorders/MetricRecorder.js +2 -2
- package/dist/lib/scope/recorders/index.js +1 -1
- package/dist/lib/scope/types.js +1 -1
- package/dist/recorders.js +1 -1
- package/dist/trace.js +18 -2
- package/dist/types/index.d.ts +4 -4
- package/dist/types/lib/decide/evidence.d.ts +3 -3
- package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +1 -1
- package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +2 -2
- package/dist/types/lib/engine/narrative/types.d.ts +19 -2
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +11 -0
- package/dist/types/lib/memory/StageContext.d.ts +2 -2
- package/dist/types/lib/memory/backtrack.d.ts +1 -1
- package/dist/types/lib/reactive/types.d.ts +7 -7
- package/dist/types/lib/recorder/BoundaryStateStore.d.ts +115 -0
- package/dist/types/lib/recorder/BoundaryStateTracker.d.ts +3 -3
- package/dist/types/lib/recorder/CombinedRecorder.d.ts +11 -11
- package/dist/types/lib/recorder/CommitRangeIndex.d.ts +147 -0
- package/dist/types/lib/recorder/CompositeRecorder.d.ts +8 -8
- package/dist/types/lib/recorder/EmitRecorder.d.ts +2 -2
- package/dist/types/lib/recorder/InOutRecorder.d.ts +1 -1
- package/dist/types/lib/recorder/KeyedStore.d.ts +70 -0
- package/dist/types/lib/recorder/QualityRecorder.d.ts +4 -4
- package/dist/types/lib/recorder/SequenceRecorder.d.ts +1 -1
- package/dist/types/lib/recorder/SequenceStore.d.ts +120 -0
- package/dist/types/lib/recorder/TopologyRecorder.d.ts +1 -1
- package/dist/types/lib/recorder/index.d.ts +5 -2
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +1 -1
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +36 -14
- package/dist/types/lib/runner/RunContext.d.ts +2 -2
- package/dist/types/lib/runner/RunnableChart.d.ts +2 -2
- package/dist/types/lib/runner/runId.d.ts +45 -0
- package/dist/types/lib/scope/ScopeFacade.d.ts +4 -4
- package/dist/types/lib/scope/index.d.ts +1 -1
- package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +4 -4
- package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +4 -4
- package/dist/types/lib/scope/recorders/index.d.ts +1 -1
- package/dist/types/lib/scope/types.d.ts +1 -1
- package/dist/types/recorders.d.ts +1 -1
- package/dist/types/trace.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SequenceStore<T> — concrete, composable storage for ordered sequence data.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: COMPOSITION primitive. Concrete class — instantiate with
|
|
5
|
+
* `new SequenceStore<T>()` and own it as a field on your
|
|
6
|
+
* recorder. Replaces the abstract `SequenceRecorder<T>` base
|
|
7
|
+
* class for the v5 "one purpose per recorder" rule:
|
|
8
|
+
* stores ARE storage; recorders ARE event handlers; consumers
|
|
9
|
+
* COMPOSE.
|
|
10
|
+
* Role: Dual-indexed append-only sequence: a flat array preserving
|
|
11
|
+
* insertion order plus a `Map<runtimeStageId, T[]>` for O(1)
|
|
12
|
+
* per-step lookup. Plus a precomputed range index for time-
|
|
13
|
+
* travel scrubbing.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { SequenceStore } from 'footprintjs/trace';
|
|
18
|
+
* import type { ScopeRecorder } from 'footprintjs';
|
|
19
|
+
*
|
|
20
|
+
* interface AuditEntry {
|
|
21
|
+
* runtimeStageId?: string;
|
|
22
|
+
* type: 'read' | 'write' | 'decision';
|
|
23
|
+
* detail: string;
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // ONE PURPOSE: scope-event handler. Storage is composed in.
|
|
27
|
+
* class AuditRecorder implements ScopeRecorder {
|
|
28
|
+
* readonly id = 'audit';
|
|
29
|
+
* private readonly store = new SequenceStore<AuditEntry>();
|
|
30
|
+
*
|
|
31
|
+
* onRead(event: ReadEvent) {
|
|
32
|
+
* this.store.push({
|
|
33
|
+
* runtimeStageId: event.runtimeStageId,
|
|
34
|
+
* type: 'read',
|
|
35
|
+
* detail: event.key,
|
|
36
|
+
* });
|
|
37
|
+
* }
|
|
38
|
+
* onWrite(event: WriteEvent) {
|
|
39
|
+
* this.store.push({
|
|
40
|
+
* runtimeStageId: event.runtimeStageId,
|
|
41
|
+
* type: 'write',
|
|
42
|
+
* detail: event.key,
|
|
43
|
+
* });
|
|
44
|
+
* }
|
|
45
|
+
*
|
|
46
|
+
* getAudit() { return this.store.getAll(); }
|
|
47
|
+
* getAuditUpTo(ids: ReadonlySet<string>) {
|
|
48
|
+
* return this.store.getEntriesUpTo(ids);
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* clear() { this.store.clear(); }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* **Contrast with `KeyedStore<T>`:** SequenceStore stores 1:N entries
|
|
56
|
+
* per runtimeStageId in insertion order. Use KeyedStore for 1:1
|
|
57
|
+
* (one record per step — token counts, metric snapshots).
|
|
58
|
+
*/
|
|
59
|
+
export declare class SequenceStore<T extends {
|
|
60
|
+
runtimeStageId?: string;
|
|
61
|
+
}> {
|
|
62
|
+
/** Ordered sequence of all entries (insertion order). */
|
|
63
|
+
private readonly entries;
|
|
64
|
+
/** Per-step index: runtimeStageId → entries for that step. Same objects as `entries[]`. */
|
|
65
|
+
private readonly byRuntimeStageId;
|
|
66
|
+
/** Per-step range index: runtimeStageId → [firstIdx, endIdx) in entries array.
|
|
67
|
+
* endIdx includes trailing keyless entries (structural markers). Maintained during push(). */
|
|
68
|
+
private readonly entryRanges;
|
|
69
|
+
/** The runtimeStageId of the most recently emitted keyed entry. Used to extend
|
|
70
|
+
* ranges for trailing markers (entries without runtimeStageId attached after a step). */
|
|
71
|
+
private lastEmittedId;
|
|
72
|
+
/**
|
|
73
|
+
* Append an entry to both the ordered sequence, keyed index, and range index.
|
|
74
|
+
* All three reference the SAME entry object — no duplication.
|
|
75
|
+
*/
|
|
76
|
+
push(entry: T): void;
|
|
77
|
+
/** All entries in insertion order. Returns a shallow copy — entry objects are shared. */
|
|
78
|
+
getAll(): T[];
|
|
79
|
+
/** Number of entries in the sequence. */
|
|
80
|
+
get size(): number;
|
|
81
|
+
/** Zero-copy iteration. Avoids the `getAll()` spread when the caller just needs
|
|
82
|
+
* to walk the entries (e.g., aggregating, rendering). */
|
|
83
|
+
forEach(fn: (entry: T) => void): void;
|
|
84
|
+
/** O(1) lookup: all entries for a specific execution step. Returns a copy. */
|
|
85
|
+
getByKey(runtimeStageId: string): T[];
|
|
86
|
+
/** Number of distinct execution steps that have at least one entry. */
|
|
87
|
+
get keyCount(): number;
|
|
88
|
+
/**
|
|
89
|
+
* Pre-built range index: runtimeStageId → half-open `[firstIdx, endIdx)`
|
|
90
|
+
* range in the entries array. Maintained during `push()` — no rebuild
|
|
91
|
+
* needed. Use for O(1) per-step lookups during time-travel scrubbing.
|
|
92
|
+
* `endIdx` includes trailing keyless entries (structural markers
|
|
93
|
+
* following a step).
|
|
94
|
+
*/
|
|
95
|
+
getEntryRanges(): ReadonlyMap<string, {
|
|
96
|
+
readonly firstIdx: number;
|
|
97
|
+
readonly endIdx: number;
|
|
98
|
+
}>;
|
|
99
|
+
/** Reduce ALL entries to a single value. For dashboards, totals, summaries. */
|
|
100
|
+
aggregate<R>(fn: (acc: R, entry: T) => R, initial: R): R;
|
|
101
|
+
/**
|
|
102
|
+
* Reduce entries, optionally filtered by a set of `runtimeStageIds`.
|
|
103
|
+
* For time-travel progressive view: pass the runtimeStageIds visible
|
|
104
|
+
* at the current slider position. Entries without `runtimeStageId`
|
|
105
|
+
* (structural markers) are excluded when keys are provided. Without
|
|
106
|
+
* keys, reduces all entries (same as `aggregate`).
|
|
107
|
+
*/
|
|
108
|
+
accumulate<R>(fn: (acc: R, entry: T) => R, initial: R, keys?: ReadonlySet<string>): R;
|
|
109
|
+
/**
|
|
110
|
+
* Progressive reveal: entries whose `runtimeStageId` is in the visible
|
|
111
|
+
* set. Preserves insertion order. Entries without `runtimeStageId`
|
|
112
|
+
* (structural markers) are buffered and included only when surrounded
|
|
113
|
+
* by visible steps on both sides — trailing markers after the last
|
|
114
|
+
* visible step are discarded.
|
|
115
|
+
*/
|
|
116
|
+
getEntriesUpTo(visibleIds: ReadonlySet<string>): T[];
|
|
117
|
+
/** Clear all stored data. Recorders typically call this from their own
|
|
118
|
+
* `clear()` method, which the executor invokes before each run. */
|
|
119
|
+
clear(): void;
|
|
120
|
+
}
|
|
@@ -97,7 +97,7 @@ export interface Topology {
|
|
|
97
97
|
readonly rootId: string | null;
|
|
98
98
|
}
|
|
99
99
|
export interface TopologyRecorderOptions {
|
|
100
|
-
/**
|
|
100
|
+
/** ScopeRecorder id. Defaults to `topology-N` (auto-incremented). */
|
|
101
101
|
id?: string;
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
export { BoundaryStateStore } from './BoundaryStateStore.js';
|
|
2
|
+
export { KeyedStore } from './KeyedStore.js';
|
|
3
|
+
export { SequenceStore } from './SequenceStore.js';
|
|
1
4
|
export { BoundaryStateTracker } from './BoundaryStateTracker.js';
|
|
5
|
+
export { KeyedRecorder } from './KeyedRecorder.js';
|
|
6
|
+
export { SequenceRecorder } from './SequenceRecorder.js';
|
|
2
7
|
export type { CombinedRecorder } from './CombinedRecorder.js';
|
|
3
8
|
export { hasEmitRecorderMethods, hasFlowRecorderMethods, hasRecorderMethods, isFlowEvent } from './CombinedRecorder.js';
|
|
4
9
|
export type { CompositeSnapshot } from './CompositeRecorder.js';
|
|
5
10
|
export { CompositeRecorder } from './CompositeRecorder.js';
|
|
6
11
|
export type { EmitEvent, EmitRecorder } from './EmitRecorder.js';
|
|
7
|
-
export { KeyedRecorder } from './KeyedRecorder.js';
|
|
8
12
|
export { RecorderOperation } from './RecorderOperation.js';
|
|
9
|
-
export { SequenceRecorder } from './SequenceRecorder.js';
|
|
@@ -17,7 +17,7 @@ import type { CommitBundle, StageSnapshot } from '../memory/types.js';
|
|
|
17
17
|
export interface RecorderSnapshot {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
-
/**
|
|
20
|
+
/** ScopeRecorder type and pattern description (e.g., "Translator (KeyedRecorder) — per-step token usage"). */
|
|
21
21
|
description?: string;
|
|
22
22
|
/** Preferred read-time operation — hints the UI about which view to show prominently. */
|
|
23
23
|
preferredOperation?: 'translate' | 'accumulate' | 'aggregate';
|
|
@@ -26,7 +26,7 @@ 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';
|
|
28
28
|
import type { ScopeProtectionMode } from '../scope/protection/types.js';
|
|
29
|
-
import type {
|
|
29
|
+
import type { RedactionPolicy, RedactionReport, ScopeRecorder } from '../scope/types.js';
|
|
30
30
|
import { type RuntimeSnapshot } from './ExecutionRuntime.js';
|
|
31
31
|
/**
|
|
32
32
|
* Options object for `FlowChartExecutor` — preferred over positional params.
|
|
@@ -86,6 +86,10 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
86
86
|
private traverser;
|
|
87
87
|
/** Shared execution counter — survives pause/resume. Reset on fresh run(). */
|
|
88
88
|
private _executionCounter;
|
|
89
|
+
/** Per-`run()` identifier — generated fresh per run + per resume. Threaded
|
|
90
|
+
* through every TraversalContext so recorders can scope state to a single
|
|
91
|
+
* run. See `runId.ts`. */
|
|
92
|
+
private _currentRunId;
|
|
89
93
|
private narrativeEnabled;
|
|
90
94
|
private narrativeOptions?;
|
|
91
95
|
private combinedRecorder;
|
|
@@ -159,6 +163,24 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
159
163
|
getCheckpoint(): FlowchartCheckpoint | undefined;
|
|
160
164
|
/** Returns `true` if the most recent run() was paused (checkpoint available). */
|
|
161
165
|
isPaused(): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Number of commits in the run's commit log. O(1) — direct length
|
|
168
|
+
* read, no snapshot materialization. Use this to stamp commit
|
|
169
|
+
* indices on observer events (e.g., `BoundaryRecorder` storing
|
|
170
|
+
* `commitIdxBefore` / `commitIdxAfter` per domain event for
|
|
171
|
+
* `CommitRangeIndex` queries — see `footprintjs/trace`).
|
|
172
|
+
*
|
|
173
|
+
* Returns 0 before any run; after, returns the cumulative commit
|
|
174
|
+
* count across the executor's lifetime (including resumes).
|
|
175
|
+
*
|
|
176
|
+
* IMPLEMENTATION NOTE: this returns `runtime.executionHistory.length`,
|
|
177
|
+
* which is the same value as `getSnapshot().commitLog.length`. The
|
|
178
|
+
* naming asymmetry is historical — the underlying `EventLog` field
|
|
179
|
+
* is named `executionHistory` but stores the `CommitBundle[]` that
|
|
180
|
+
* `commitLog` exposes. They are the SAME array (verified by the
|
|
181
|
+
* "matches commitLog.length" integration test).
|
|
182
|
+
*/
|
|
183
|
+
getCommitCount(): number;
|
|
162
184
|
/**
|
|
163
185
|
* Resume a paused flowchart from a checkpoint.
|
|
164
186
|
*
|
|
@@ -201,7 +223,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
201
223
|
/** DFS search for a node by ID in the StageNode graph. Cycle-safe via visited set. */
|
|
202
224
|
private dfsFind;
|
|
203
225
|
/**
|
|
204
|
-
* Attach a scope
|
|
226
|
+
* Attach a scope ScopeRecorder to observe data operations (reads, writes, commits).
|
|
205
227
|
* Automatically attached to every ScopeFacade created during traversal.
|
|
206
228
|
* Must be called before run().
|
|
207
229
|
*
|
|
@@ -216,18 +238,18 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
216
238
|
* @example
|
|
217
239
|
* ```typescript
|
|
218
240
|
* // Multiple recorders with different configs — each gets a unique ID
|
|
219
|
-
* executor.
|
|
220
|
-
* executor.
|
|
241
|
+
* executor.attachScopeRecorder(new MetricRecorder());
|
|
242
|
+
* executor.attachScopeRecorder(new DebugRecorder({ verbosity: 'minimal' }));
|
|
221
243
|
*
|
|
222
244
|
* // Override a framework-attached recorder by passing its well-known ID
|
|
223
|
-
* executor.
|
|
245
|
+
* executor.attachScopeRecorder(new MetricRecorder('metrics'));
|
|
224
246
|
*
|
|
225
247
|
* // Attaching twice with same ID replaces (no double-counting)
|
|
226
|
-
* executor.
|
|
227
|
-
* executor.
|
|
248
|
+
* executor.attachScopeRecorder(new MetricRecorder('my-metrics'));
|
|
249
|
+
* executor.attachScopeRecorder(new MetricRecorder('my-metrics')); // replaces previous
|
|
228
250
|
* ```
|
|
229
251
|
*/
|
|
230
|
-
|
|
252
|
+
attachScopeRecorder(recorder: ScopeRecorder): void;
|
|
231
253
|
/**
|
|
232
254
|
* Detach a child flowchart on the given driver and return a `DetachHandle`
|
|
233
255
|
* the caller can `wait()` on (Promise) or read `.status` from (sync).
|
|
@@ -257,9 +279,9 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
257
279
|
*/
|
|
258
280
|
detachAndForget(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): void;
|
|
259
281
|
/** Detach all scope Recorders with the given ID. */
|
|
260
|
-
|
|
282
|
+
detachScopeRecorder(id: string): void;
|
|
261
283
|
/** Returns a defensive copy of attached scope Recorders. */
|
|
262
|
-
|
|
284
|
+
getScopeRecorders(): ScopeRecorder[];
|
|
263
285
|
/**
|
|
264
286
|
* Attach a FlowRecorder to observe control flow events.
|
|
265
287
|
* Automatically enables narrative if not already enabled.
|
|
@@ -277,7 +299,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
277
299
|
* data-flow, control-flow, or both). Detects at runtime which streams the
|
|
278
300
|
* recorder has methods for and routes it to the correct internal channels.
|
|
279
301
|
*
|
|
280
|
-
* Preferred over calling `
|
|
302
|
+
* Preferred over calling `attachScopeRecorder` and `attachFlowRecorder`
|
|
281
303
|
* separately, because forgetting one of the two is a silent foot-gun —
|
|
282
304
|
* half your events never fire and there is no runtime warning. With
|
|
283
305
|
* `attachCombinedRecorder` the library guarantees the recorder's declared
|
|
@@ -287,7 +309,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
287
309
|
*
|
|
288
310
|
* Idempotent by `id` across ALL channels — re-attaching with the same `id`
|
|
289
311
|
* replaces the previous instance everywhere it was registered. Mixing
|
|
290
|
-
* `attachCombinedRecorder(x)` with a prior `
|
|
312
|
+
* `attachCombinedRecorder(x)` with a prior `attachScopeRecorder(y)` or
|
|
291
313
|
* `attachFlowRecorder(y)` that share `x.id === y.id` is also safe: the
|
|
292
314
|
* combined attach replaces the single-channel registration on whichever
|
|
293
315
|
* channel(s) `x` has methods for. No duplicate firings occur.
|
|
@@ -330,8 +352,8 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
330
352
|
* Internally, emit recorders share the scope-recorder channel because
|
|
331
353
|
* emit events fire from inside `ScopeFacade` during stage execution,
|
|
332
354
|
* same timing as `onRead`/`onWrite`. This method is a convenience that
|
|
333
|
-
* delegates to `
|
|
334
|
-
* `
|
|
355
|
+
* delegates to `attachScopeRecorder` — consumers can also use
|
|
356
|
+
* `attachScopeRecorder` directly for a recorder that implements BOTH
|
|
335
357
|
* `onWrite` and `onEmit`. Either approach places the recorder on the
|
|
336
358
|
* same underlying list, so `onEmit` fires exactly once per event.
|
|
337
359
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type { FlowChart } from '../builder/types.js';
|
|
11
11
|
import type { FlowRecorder } from '../engine/narrative/types.js';
|
|
12
12
|
import type { RunOptions } from '../engine/types.js';
|
|
13
|
-
import type {
|
|
13
|
+
import type { RedactionPolicy, ScopeRecorder } from '../scope/types.js';
|
|
14
14
|
/** Result from RunContext.run() — owns state and output. */
|
|
15
15
|
export interface RunResult {
|
|
16
16
|
/** Raw scope state after execution. */
|
|
@@ -29,7 +29,7 @@ export declare class RunContext<TOut = any, TScope = any> {
|
|
|
29
29
|
private redactionPolicy?;
|
|
30
30
|
constructor(chart: FlowChart<TOut, TScope>);
|
|
31
31
|
/** Attach a recorder. Auto-detects scope vs flow recorder. Chainable. */
|
|
32
|
-
recorder(r:
|
|
32
|
+
recorder(r: ScopeRecorder | FlowRecorder): RunContext<TOut, TScope>;
|
|
33
33
|
/** Set redaction policy for this run. Chainable. */
|
|
34
34
|
redact(policy: RedactionPolicy): RunContext<TOut, TScope>;
|
|
35
35
|
/** Execute the chart with accumulated config. Returns RunResult. */
|
|
@@ -9,7 +9,7 @@ import type { FlowChart } from '../builder/types.js';
|
|
|
9
9
|
import type { JsonSchema } from '../contract/types.js';
|
|
10
10
|
import type { FlowRecorder } from '../engine/narrative/types.js';
|
|
11
11
|
import type { RunOptions } from '../engine/types.js';
|
|
12
|
-
import type {
|
|
12
|
+
import type { RedactionPolicy, ScopeRecorder } from '../scope/types.js';
|
|
13
13
|
import { type RunResult, RunContext } from './RunContext.js';
|
|
14
14
|
/** OpenAPI generation options. */
|
|
15
15
|
export interface ChartOpenAPIOptions {
|
|
@@ -38,7 +38,7 @@ export interface MCPToolDescription {
|
|
|
38
38
|
*/
|
|
39
39
|
export interface RunnableFlowChart<TOut = any, TScope = any> extends FlowChart<TOut, TScope> {
|
|
40
40
|
/** Attach a recorder for the next run. Returns a chainable RunContext. */
|
|
41
|
-
recorder(r:
|
|
41
|
+
recorder(r: ScopeRecorder | FlowRecorder): RunContext<TOut, TScope>;
|
|
42
42
|
/** Set redaction policy for the next run. Returns a chainable RunContext. */
|
|
43
43
|
redact(policy: RedactionPolicy): RunContext<TOut, TScope>;
|
|
44
44
|
/** Execute the chart directly (bare run, no recorders). */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runId — per-`executor.run()` identifier generator.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: monotonic counter + clock-guarded timestamp. One id per
|
|
5
|
+
* call to `executor.run()` (or `executor.resume()`). Stable
|
|
6
|
+
* for the duration of that run; unique across consecutive
|
|
7
|
+
* runs.
|
|
8
|
+
* Role: primitive that solves the "two consecutive runs of the
|
|
9
|
+
* same executor produce identical runtimeStageIds" class of
|
|
10
|
+
* bugs. Recorders that accumulate state across runs detect
|
|
11
|
+
* "new run" via `runId` change and reset transient
|
|
12
|
+
* bookkeeping.
|
|
13
|
+
*
|
|
14
|
+
* Format: `${timestamp}-${counter}`.
|
|
15
|
+
* - `timestamp` is `Date.now()` clamped to a monotonic-clock guard
|
|
16
|
+
* (never decreases — protects against NTP / system-clock
|
|
17
|
+
* adjustments).
|
|
18
|
+
* - `counter` is a process-local incrementing integer, ZERO-PADDED
|
|
19
|
+
* to 10 digits so lexicographic sort matches numeric order
|
|
20
|
+
* (`"...001"` < `"...010"` < `"...100"`). 10 digits = 10 billion
|
|
21
|
+
* runs in a single process — sufficient for any real workload.
|
|
22
|
+
*
|
|
23
|
+
* Lexicographic ordering of `runId` strings matches chronological
|
|
24
|
+
* ordering for runs that are at least 1ms apart, AND for runs that
|
|
25
|
+
* happen within the same millisecond (because the padded counter
|
|
26
|
+
* tie-breaks). The counter NEVER resets — it is process-global.
|
|
27
|
+
*
|
|
28
|
+
* Process-local only. Cross-process correlation uses
|
|
29
|
+
* `getEnv().traceId` (consumer-supplied), not `runId`. Documented
|
|
30
|
+
* in `docs/design/v5-recorder-redesign.md` Section 8.1.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Generate a fresh runId. Called once per `executor.run()` and once
|
|
34
|
+
* per `executor.resume()`. Pure (deterministic for a given clock +
|
|
35
|
+
* counter state); no side effects beyond advancing the counter and
|
|
36
|
+
* monotonic-clock guard.
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateRunId(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Reset the runId state. Test-only. NEVER call from production code —
|
|
41
|
+
* runIds must be process-globally monotonic.
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare function _resetRunIdStateForTesting(): void;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ExecutionEnv } from '../engine/types.js';
|
|
17
17
|
import { StageContext } from '../memory/StageContext.js';
|
|
18
|
-
import type { CommitEvent,
|
|
18
|
+
import type { CommitEvent, RedactionPolicy, RedactionReport, ScopeRecorder } from './types.js';
|
|
19
19
|
export declare class ScopeFacade {
|
|
20
20
|
static readonly BRAND: unique symbol;
|
|
21
21
|
/**
|
|
@@ -60,9 +60,9 @@ export declare class ScopeFacade {
|
|
|
60
60
|
* Never includes actual values — only key names, field names, and patterns.
|
|
61
61
|
*/
|
|
62
62
|
getRedactionReport(): RedactionReport;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
attachScopeRecorder(recorder: ScopeRecorder): void;
|
|
64
|
+
detachScopeRecorder(recorderId: string): void;
|
|
65
|
+
getScopeRecorders(): ScopeRecorder[];
|
|
66
66
|
/** @internal */
|
|
67
67
|
notifyStageStart(): void;
|
|
68
68
|
/** @internal */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* providers, protection, and Zod-based scope definitions.
|
|
6
6
|
*/
|
|
7
7
|
export { ScopeFacade } from './ScopeFacade.js';
|
|
8
|
-
export type { CommitEvent, ErrorEvent, ReadEvent,
|
|
8
|
+
export type { CommitEvent, ErrorEvent, ReadEvent, RecorderContext, RedactionPolicy, RedactionReport, ScopeRecorder, StageEvent, WriteEvent, } from './types.js';
|
|
9
9
|
export type { DebugEntry, DebugRecorderOptions, DebugVerbosity } from './recorders/DebugRecorder.js';
|
|
10
10
|
export { DebugRecorder } from './recorders/DebugRecorder.js';
|
|
11
11
|
export type { AggregatedMetrics, StageMetrics } from './recorders/MetricRecorder.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and stage lifecycle events for troubleshooting.
|
|
6
6
|
*/
|
|
7
7
|
import type { RecorderOperation } from '../../recorder/RecorderOperation.js';
|
|
8
|
-
import type { ErrorEvent, PauseEvent, ReadEvent,
|
|
8
|
+
import type { ErrorEvent, PauseEvent, ReadEvent, ResumeEvent, ScopeRecorder, StageEvent, WriteEvent } from '../types.js';
|
|
9
9
|
export type DebugVerbosity = 'minimal' | 'verbose';
|
|
10
10
|
export interface DebugEntry {
|
|
11
11
|
type: 'read' | 'write' | 'error' | 'stageStart' | 'stageEnd' | 'pause' | 'resume';
|
|
@@ -26,15 +26,15 @@ export interface DebugRecorderOptions {
|
|
|
26
26
|
* @example
|
|
27
27
|
* ```typescript
|
|
28
28
|
* // Verbose debug for development
|
|
29
|
-
* executor.
|
|
29
|
+
* executor.attachScopeRecorder(new DebugRecorder({ verbosity: 'verbose' }));
|
|
30
30
|
*
|
|
31
31
|
* // Minimal debug for production (errors only)
|
|
32
|
-
* executor.
|
|
32
|
+
* executor.attachScopeRecorder(new DebugRecorder({ verbosity: 'minimal' }));
|
|
33
33
|
*
|
|
34
34
|
* // Both coexist — different auto IDs
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export declare class DebugRecorder implements
|
|
37
|
+
export declare class DebugRecorder implements ScopeRecorder {
|
|
38
38
|
private static _counter;
|
|
39
39
|
readonly id: string;
|
|
40
40
|
readonly preferredOperation: RecorderOperation;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @example
|
|
8
8
|
* ```typescript
|
|
9
9
|
* const metric = new MetricRecorder();
|
|
10
|
-
* executor.
|
|
10
|
+
* executor.attachScopeRecorder(metric);
|
|
11
11
|
* await executor.run();
|
|
12
12
|
*
|
|
13
13
|
* // Per-step (time-travel):
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { KeyedRecorder } from '../../recorder/KeyedRecorder.js';
|
|
24
24
|
import type { RecorderOperation } from '../../recorder/RecorderOperation.js';
|
|
25
|
-
import type { CommitEvent, PauseEvent, ReadEvent,
|
|
25
|
+
import type { CommitEvent, PauseEvent, ReadEvent, ScopeRecorder, StageEvent, WriteEvent } from '../types.js';
|
|
26
26
|
/** Per-invocation metrics for a single execution step. */
|
|
27
27
|
export interface StepMetrics {
|
|
28
28
|
/** Human-readable stage name. */
|
|
@@ -60,14 +60,14 @@ export interface StageMetrics {
|
|
|
60
60
|
}
|
|
61
61
|
/** Options for MetricRecorder. */
|
|
62
62
|
export interface MetricRecorderOptions {
|
|
63
|
-
/**
|
|
63
|
+
/** ScopeRecorder ID. Defaults to auto-increment (`metrics-1`, `metrics-2`, ...). */
|
|
64
64
|
id?: string;
|
|
65
65
|
/** Filter which stages are recorded. Return `true` to record, `false` to skip. */
|
|
66
66
|
stageFilter?: (stageName: string) => boolean;
|
|
67
67
|
/** Preferred UI operation. Defaults to 'aggregate' (dashboard totals). */
|
|
68
68
|
preferredOperation?: RecorderOperation;
|
|
69
69
|
}
|
|
70
|
-
export declare class MetricRecorder extends KeyedRecorder<StepMetrics> implements
|
|
70
|
+
export declare class MetricRecorder extends KeyedRecorder<StepMetrics> implements ScopeRecorder {
|
|
71
71
|
private static _counter;
|
|
72
72
|
readonly id: string;
|
|
73
73
|
readonly preferredOperation: RecorderOperation;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CommitEvent, ErrorEvent, ReadEvent,
|
|
1
|
+
export type { CommitEvent, ErrorEvent, ReadEvent, RecorderContext, ScopeRecorder, StageEvent, WriteEvent, } from '../types.js';
|
|
2
2
|
export type { DebugEntry, DebugRecorderOptions, DebugVerbosity } from './DebugRecorder.js';
|
|
3
3
|
export { DebugRecorder } from './DebugRecorder.js';
|
|
4
4
|
export type { AggregatedMetrics, StageMetrics, StepMetrics } from './MetricRecorder.js';
|
|
@@ -100,7 +100,7 @@ export interface RedactionReport {
|
|
|
100
100
|
* If a recorder throws, the error is caught and passed to onError
|
|
101
101
|
* hooks of other recorders; the scope operation continues normally.
|
|
102
102
|
*/
|
|
103
|
-
export interface
|
|
103
|
+
export interface ScopeRecorder {
|
|
104
104
|
readonly id: string;
|
|
105
105
|
onRead?(event: ReadEvent): void;
|
|
106
106
|
onWrite?(event: WriteEvent): void;
|
|
@@ -37,7 +37,7 @@ import { DebugRecorder } from './lib/scope/recorders/DebugRecorder.js';
|
|
|
37
37
|
import type { AggregatedMetrics, MetricRecorderOptions, StageMetrics } from './lib/scope/recorders/MetricRecorder.js';
|
|
38
38
|
import { MetricRecorder } from './lib/scope/recorders/MetricRecorder.js';
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* ScopeRecorder factory for combined flow+data narrative.
|
|
41
41
|
*
|
|
42
42
|
* The returned recorder is a `CombinedNarrativeRecorder` — attach it to a
|
|
43
43
|
* chart/executor, then read structured entries via `.getEntries()` after
|
package/dist/types/trace.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId } from
|
|
|
25
25
|
export { findCommit, findCommits, findLastWriter } from './lib/memory/commitLogUtils.js';
|
|
26
26
|
export type { CausalChainOptions, CausalNode, KeysReadLookup } from './lib/memory/backtrack.js';
|
|
27
27
|
export { causalChain, flattenCausalDAG, formatCausalChain } from './lib/memory/backtrack.js';
|
|
28
|
+
export { BoundaryStateStore } from './lib/recorder/BoundaryStateStore.js';
|
|
29
|
+
export { KeyedStore } from './lib/recorder/KeyedStore.js';
|
|
30
|
+
export { SequenceStore } from './lib/recorder/SequenceStore.js';
|
|
31
|
+
export type { RangeEntry, RangeToken } from './lib/recorder/CommitRangeIndex.js';
|
|
32
|
+
export { CommitRangeIndex } from './lib/recorder/CommitRangeIndex.js';
|
|
28
33
|
export { KeyedRecorder } from './lib/recorder/KeyedRecorder.js';
|
|
29
34
|
export { SequenceRecorder } from './lib/recorder/SequenceRecorder.js';
|
|
30
35
|
export { BoundaryStateTracker } from './lib/recorder/BoundaryStateTracker.js';
|
package/package.json
CHANGED