footprintjs 9.2.0 → 9.4.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/AGENTS.md +2 -1
- package/CLAUDE.md +2 -1
- package/dist/advanced.js +1 -1
- package/dist/esm/advanced.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/lib/capture/index.js +16 -0
- package/dist/esm/lib/capture/policies.js +29 -0
- package/dist/esm/lib/capture/summarize.js +58 -0
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +9 -1
- package/dist/esm/lib/memory/StageContext.js +98 -44
- package/dist/esm/lib/memory/index.js +2 -2
- package/dist/esm/lib/memory/types.js +7 -4
- package/dist/esm/lib/runner/ExecutionRuntime.js +13 -1
- package/dist/esm/lib/runner/FlowChartExecutor.js +20 -1
- package/dist/index.js +1 -1
- package/dist/lib/capture/index.js +23 -0
- package/dist/lib/capture/policies.js +30 -0
- package/dist/lib/capture/summarize.js +63 -0
- package/dist/lib/engine/handlers/SubflowExecutor.js +9 -1
- package/dist/lib/memory/StageContext.js +99 -45
- package/dist/lib/memory/index.js +3 -2
- package/dist/lib/memory/types.js +10 -5
- package/dist/lib/runner/ExecutionRuntime.js +13 -1
- package/dist/lib/runner/FlowChartExecutor.js +20 -1
- package/dist/types/advanced.d.ts +1 -1
- package/dist/types/index.d.ts +13 -0
- package/dist/types/lib/capture/index.d.ts +16 -0
- package/dist/types/lib/capture/policies.d.ts +42 -0
- package/dist/types/lib/capture/summarize.d.ts +65 -0
- package/dist/types/lib/memory/StageContext.d.ts +67 -1
- package/dist/types/lib/memory/index.d.ts +2 -2
- package/dist/types/lib/memory/types.d.ts +33 -22
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +11 -1
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +43 -1
- package/package.json +3 -1
package/dist/types/advanced.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Import via: import { ... } from 'footprint/advanced'
|
|
21
21
|
*/
|
|
22
|
-
export type { CommitBundle, FlowControlType, FlowMessage, MemoryPatch, ReadSummaryMarker, ReadTrackingMode, StageSnapshot, TraceEntry, } from './lib/memory/index.js';
|
|
22
|
+
export type { CommitBundle, FlowControlType, FlowMessage, MemoryPatch, ReadSummaryMarker, ReadTrackingMode, RetentionPolicy, StageSnapshot, TraceEntry, WriteSummaryMarker, WriteTrackingMode, } from './lib/memory/index.js';
|
|
23
23
|
export { SharedMemory } from './lib/memory/index.js';
|
|
24
24
|
export { StageContext } from './lib/memory/index.js';
|
|
25
25
|
export { EventLog } from './lib/memory/index.js';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -123,6 +123,19 @@ export type { ScopeFactory } from './lib/engine/index.js';
|
|
|
123
123
|
* or call `executor.setReadTracking(mode)` before `run()`.
|
|
124
124
|
*/
|
|
125
125
|
export type { ReadSummaryMarker, ReadTrackingMode } from './lib/memory/index.js';
|
|
126
|
+
/**
|
|
127
|
+
* @category Configuration
|
|
128
|
+
*
|
|
129
|
+
* Write-tracking policy for `StageSnapshot.stageWrites` (#13c-A) — the
|
|
130
|
+
* sibling of `ReadTrackingMode`; both alias the shared `RetentionPolicy`
|
|
131
|
+
* family from `lib/capture`. `'full'` (default — per-write value clone,
|
|
132
|
+
* historical behavior) / `'summary'` (cheap `WriteSummaryMarker` per write)
|
|
133
|
+
* / `'off'` (no tracking; `stageWrites` absent and the `onCommit` mutations
|
|
134
|
+
* payload is empty — writes themselves still commit, and the commit log is
|
|
135
|
+
* unaffected). Pass as `new FlowChartExecutor(chart, { writeTracking })` or
|
|
136
|
+
* call `executor.setWriteTracking(mode)` before `run()`.
|
|
137
|
+
*/
|
|
138
|
+
export type { RetentionPolicy, WriteSummaryMarker, WriteTrackingMode } from './lib/memory/index.js';
|
|
126
139
|
/** @category Contract & Validation */
|
|
127
140
|
export type { SchemaKind, ValidationIssue, ValidationResult } from './lib/schema/index.js';
|
|
128
141
|
/** @category Contract & Validation */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capture/ — Shared value-capture / retention primitives (zero deps, leaf
|
|
3
|
+
* module — `memory/` imports it, never the reverse).
|
|
4
|
+
*
|
|
5
|
+
* One home for "what do we keep about a tracked operation's value?":
|
|
6
|
+
* - `RetentionPolicy` — the `'full' | 'summary' | 'off'` family behind the
|
|
7
|
+
* #14 `readTracking` and #13c-A `writeTracking` dials.
|
|
8
|
+
* - `summarizeReadValue` / `summarizeWriteValue` — the parameterized
|
|
9
|
+
* summary-marker builders sharing one classification path.
|
|
10
|
+
*
|
|
11
|
+
* RFC-001 (deferred observer delivery) builds its capture tier on this
|
|
12
|
+
* module — see the mapping notes in `policies.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export type { RetentionPolicy } from './policies.js';
|
|
15
|
+
export type { ReadSummaryMarker, SummaryValueType, WriteSummaryMarker } from './summarize.js';
|
|
16
|
+
export { READ_PREVIEW_LENGTH, SUMMARY_PREVIEW_LENGTH, summarizeReadValue, summarizeWriteValue } from './summarize.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* policies.ts — The retention policy family shared by every snapshot-tracking
|
|
3
|
+
* dial (#14 `readTracking`, #13c-A `writeTracking`).
|
|
4
|
+
*
|
|
5
|
+
* RETENTION answers "what does the engine KEEP in its own snapshot state
|
|
6
|
+
* (`StageSnapshot.stageReads` / `stageWrites`, and therefore the commit
|
|
7
|
+
* observer's mutations payload) after the moment of the operation?" It is
|
|
8
|
+
* distinct from DELIVERY — what an observer receives at event time
|
|
9
|
+
* (`ScopeRecorder.onRead`/`onWrite` always deliver the live value, in every
|
|
10
|
+
* retention mode).
|
|
11
|
+
*
|
|
12
|
+
* ── RFC-001 (deferred observer delivery) mapping ──────────────────────────
|
|
13
|
+
* RFC-001's capture tier uses the vocabulary `'clone' | 'summary' | 'ref'`.
|
|
14
|
+
* When its Block 1 lands it builds on THIS module:
|
|
15
|
+
*
|
|
16
|
+
* - RFC capture `'clone'` ≈ retention `'full'` (alias at the module
|
|
17
|
+
* boundary — same semantics: structuredClone at capture time).
|
|
18
|
+
* - RFC capture `'summary'` ≈ retention `'summary'` (same marker shapes —
|
|
19
|
+
* see `summarize.ts`).
|
|
20
|
+
* - RFC capture `'ref'` is DELIVERY-tier only and is NOT implemented here
|
|
21
|
+
* — reserved. Retention must never hold live references into engine
|
|
22
|
+
* state: retained entries outlive the stage (the execution tree keeps
|
|
23
|
+
* them for the whole run), while a `'ref'` is only safe within the
|
|
24
|
+
* immutability window of the captured value. Holding one in retention
|
|
25
|
+
* would either pin state generations (the #18 leak) or expose
|
|
26
|
+
* later-mutated values as if they were point-in-time captures.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* How a tracked operation's value is retained in the per-stage snapshot view.
|
|
30
|
+
*
|
|
31
|
+
* - `'full'` — `structuredClone` the value at the moment of the operation
|
|
32
|
+
* (the historical default for both dials; point-in-time, detached copy).
|
|
33
|
+
* - `'summary'` — retain a cheap marker (type + size proxy + short preview)
|
|
34
|
+
* instead of the value. O(1)-ish per operation; no value clone.
|
|
35
|
+
* - `'off'` — retain nothing. The operation itself is unaffected (reads
|
|
36
|
+
* still return values, writes still commit); only the snapshot bookkeeping
|
|
37
|
+
* is skipped.
|
|
38
|
+
*
|
|
39
|
+
* `ReadTrackingMode` (#14) and `WriteTrackingMode` (#13c-A) are public
|
|
40
|
+
* aliases of this type — see `memory/types.ts`.
|
|
41
|
+
*/
|
|
42
|
+
export type RetentionPolicy = 'full' | 'summary' | 'off';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summarize.ts — Cheap value summarization for `'summary'` retention.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `StageContext` (#14's `summarizeReadValue`) into a shared,
|
|
5
|
+
* brand-parameterized helper so both snapshot-tracking dials (#14
|
|
6
|
+
* `readTracking`, #13c-A `writeTracking`) — and later RFC-001's deferred
|
|
7
|
+
* observer capture tier — produce identical summaries from ONE code path.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately avoids every O(value) operation: no clone, no serialization.
|
|
10
|
+
* See {@link ReadSummaryMarker} for the honest-cost contract.
|
|
11
|
+
*/
|
|
12
|
+
/** Max characters captured in a summary marker's `preview`. */
|
|
13
|
+
export declare const SUMMARY_PREVIEW_LENGTH = 80;
|
|
14
|
+
/**
|
|
15
|
+
* Compat alias for {@link SUMMARY_PREVIEW_LENGTH} — the name shipped with
|
|
16
|
+
* #14, kept so existing import paths (`memory/types`, the memory barrel)
|
|
17
|
+
* stay valid. Same constant; reads and writes share one preview cap.
|
|
18
|
+
*/
|
|
19
|
+
export declare const READ_PREVIEW_LENGTH = 80;
|
|
20
|
+
/** `typeof` result, refined to 'array' / 'null' for objects. */
|
|
21
|
+
export type SummaryValueType = 'string' | 'number' | 'boolean' | 'bigint' | 'symbol' | 'function' | 'object' | 'array' | 'null';
|
|
22
|
+
/** Brand-free summary fields shared by both marker shapes. */
|
|
23
|
+
interface ValueSummary {
|
|
24
|
+
/** `typeof` result, refined to 'array' / 'null' for objects. */
|
|
25
|
+
type: SummaryValueType;
|
|
26
|
+
/** Size proxy: string length, array length, or object key count. */
|
|
27
|
+
size?: number;
|
|
28
|
+
/** First {@link SUMMARY_PREVIEW_LENGTH} chars — primitives and strings only. */
|
|
29
|
+
preview?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Marker recorded in `StageSnapshot.stageReads` under `readTracking: 'summary'`.
|
|
33
|
+
*
|
|
34
|
+
* Honest cost note: `size` is a cheap proxy (string length / array length /
|
|
35
|
+
* object key count), NOT a serialized byte count — computing real byte size
|
|
36
|
+
* would require an O(value) serialization, which is exactly the cost the
|
|
37
|
+
* summary mode removes. `preview` is only produced for primitives and strings
|
|
38
|
+
* (first {@link SUMMARY_PREVIEW_LENGTH} characters); objects and arrays carry
|
|
39
|
+
* no preview for the same reason.
|
|
40
|
+
*/
|
|
41
|
+
export interface ReadSummaryMarker extends ValueSummary {
|
|
42
|
+
/** Discriminant — lets snapshot consumers detect marker entries. */
|
|
43
|
+
__readSummary: true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Marker recorded in `StageSnapshot.stageWrites` (and the commit observer's
|
|
47
|
+
* mutations payload) under `writeTracking: 'summary'` (#13c-A). Same fields
|
|
48
|
+
* and cost contract as {@link ReadSummaryMarker}; distinct brand so consumers
|
|
49
|
+
* can tell which dial produced an entry.
|
|
50
|
+
*/
|
|
51
|
+
export interface WriteSummaryMarker extends ValueSummary {
|
|
52
|
+
/** Discriminant — lets snapshot consumers detect marker entries. */
|
|
53
|
+
__writeSummary: true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Summarize a tracked READ for `readTracking: 'summary'` (#14). Byte-identical
|
|
57
|
+
* output to the pre-extraction `StageContext`-local implementation.
|
|
58
|
+
*/
|
|
59
|
+
export declare function summarizeReadValue(value: unknown): ReadSummaryMarker;
|
|
60
|
+
/**
|
|
61
|
+
* Summarize a tracked WRITE for `writeTracking: 'summary'` (#13c-A). Sibling
|
|
62
|
+
* of {@link summarizeReadValue} — same classification, distinct brand.
|
|
63
|
+
*/
|
|
64
|
+
export declare function summarizeWriteValue(value: unknown): WriteSummaryMarker;
|
|
65
|
+
export {};
|
|
@@ -11,7 +11,7 @@ import { DiagnosticCollector } from './DiagnosticCollector.js';
|
|
|
11
11
|
import { EventLog } from './EventLog.js';
|
|
12
12
|
import { SharedMemory } from './SharedMemory.js';
|
|
13
13
|
import { TransactionBuffer } from './TransactionBuffer.js';
|
|
14
|
-
import type { FlowControlType, ReadTrackingMode, StageSnapshot } from './types.js';
|
|
14
|
+
import type { FlowControlType, ReadTrackingMode, StageSnapshot, WriteTrackingMode } from './types.js';
|
|
15
15
|
export declare class StageContext {
|
|
16
16
|
private sharedMemory;
|
|
17
17
|
/**
|
|
@@ -68,6 +68,19 @@ export declare class StageContext {
|
|
|
68
68
|
* every mode.
|
|
69
69
|
*/
|
|
70
70
|
private readTracking;
|
|
71
|
+
/**
|
|
72
|
+
* How tracked writes are recorded into `_stageWrites` (#13c-A) — the
|
|
73
|
+
* sibling of {@link readTracking}, with the same propagation pattern
|
|
74
|
+
* (inherited via {@link createNext}/{@link createChild}, pushed into
|
|
75
|
+
* subflow root contexts by `SubflowExecutor`). Governs the per-write
|
|
76
|
+
* `structuredClone` in {@link setObject}/{@link updateObject}. Affects the
|
|
77
|
+
* snapshot's `stageWrites` payload AND the commit observer's mutations
|
|
78
|
+
* payload (which is a spread of `_stageWrites`) — but NOT the write
|
|
79
|
+
* itself: the transaction buffer, the commit log, and shared state are
|
|
80
|
+
* identical in every mode, and `ScopeRecorder.onWrite` always fires with
|
|
81
|
+
* the live value.
|
|
82
|
+
*/
|
|
83
|
+
private writeTracking;
|
|
71
84
|
/** Observer called after commit() — used by ScopeFacade to fire ScopeRecorder.onCommit. */
|
|
72
85
|
private _commitObserver?;
|
|
73
86
|
constructor(runId: string, name: string, stageId: string, sharedMemory: SharedMemory, branchId?: string, eventLog?: EventLog, isDecider?: boolean);
|
|
@@ -94,6 +107,30 @@ export declare class StageContext {
|
|
|
94
107
|
useReadTracking(mode: ReadTrackingMode): void;
|
|
95
108
|
/** Returns the active read-tracking policy (used for subflow propagation). */
|
|
96
109
|
getReadTracking(): ReadTrackingMode;
|
|
110
|
+
/**
|
|
111
|
+
* Set the write-tracking policy for this context (#13c-A). Same plumbing
|
|
112
|
+
* as {@link useReadTracking}: called at the root by
|
|
113
|
+
* `ExecutionRuntime.useWriteTracking()` (plumbed from `FlowChartExecutor`);
|
|
114
|
+
* descendants inherit via `createNext`/`createChild`, and `SubflowExecutor`
|
|
115
|
+
* pushes the parent context's mode into each subflow root.
|
|
116
|
+
*/
|
|
117
|
+
useWriteTracking(mode: WriteTrackingMode): void;
|
|
118
|
+
/** Returns the active write-tracking policy (used for subflow propagation). */
|
|
119
|
+
getWriteTracking(): WriteTrackingMode;
|
|
120
|
+
/**
|
|
121
|
+
* Record a tracked user-level write into `_stageWrites`, policy-gated
|
|
122
|
+
* (#13c-A) — the single bookkeeping path for {@link setObject} and
|
|
123
|
+
* {@link updateObject}.
|
|
124
|
+
*
|
|
125
|
+
* Redaction takes precedence over the dial in EVERY mode: a redacted
|
|
126
|
+
* write stores the `'[REDACTED]'` placeholder under `'full'` AND
|
|
127
|
+
* `'summary'` (a summary marker would leak the value's preview/size),
|
|
128
|
+
* and stores nothing under `'off'` (entry skipped entirely — nothing to
|
|
129
|
+
* leak). The staged write itself is unaffected — redaction of the
|
|
130
|
+
* committed payload is handled by the transaction buffer's
|
|
131
|
+
* `redactedPaths`.
|
|
132
|
+
*/
|
|
133
|
+
private trackWrite;
|
|
97
134
|
/**
|
|
98
135
|
* ── The first-touch state view (#13) ────────────────────────────────────
|
|
99
136
|
*
|
|
@@ -194,6 +231,35 @@ export declare class StageContext {
|
|
|
194
231
|
value: unknown;
|
|
195
232
|
operation: 'set' | 'update' | 'delete';
|
|
196
233
|
}>) => void): void;
|
|
234
|
+
/**
|
|
235
|
+
* Flush staged writes to shared memory and RELEASE the per-stage staging
|
|
236
|
+
* state (#13b).
|
|
237
|
+
*
|
|
238
|
+
* Commit is the stage's lifecycle end: `buffer` (2 full-state clones) and
|
|
239
|
+
* `stateView` (a reference that pins one full committed-state GENERATION —
|
|
240
|
+
* `applySmartMerge` clones + swaps the whole state per commit, so every
|
|
241
|
+
* stage's view is a distinct object) are only needed DURING execution, as
|
|
242
|
+
* the read snapshot + net-change diff base. The execution tree retains
|
|
243
|
+
* every StageContext for the lifetime of the run, so WITHOUT the release
|
|
244
|
+
* a long loop retains one state generation + two clones per executed
|
|
245
|
+
* stage — measured O(N²): 563.8MB at N=200 on an agent-style chart; a
|
|
246
|
+
* 500-iteration agent OOMed a default Node heap (backlog #18).
|
|
247
|
+
*
|
|
248
|
+
* RE-USE AFTER COMMIT stays correct because both fields re-create lazily:
|
|
249
|
+
* - a later READ re-anchors via {@link firstTouchState} on the CURRENT
|
|
250
|
+
* committed state (which includes this stage's own flushed writes);
|
|
251
|
+
* - a later WRITE constructs a fresh buffer on that re-anchored view, so a
|
|
252
|
+
* second commit diffs against post-first-commit state. The pre-release
|
|
253
|
+
* buffer behaved the same for VALUES (its `workingCopy` was reset on
|
|
254
|
+
* commit, falling reads through to live state) but kept the ORIGINAL
|
|
255
|
+
* `baseSnapshot` as diff base — unreachable in practice: every engine
|
|
256
|
+
* re-commit path (fork double-commit, subflow outputMapper double-commit)
|
|
257
|
+
* stages nothing in between, and the two real "write after commit" sites
|
|
258
|
+
* (SubflowExecutor seed → replaces the context; resume → fresh context
|
|
259
|
+
* via `leaf.createNext`) never re-use a committed context's buffer.
|
|
260
|
+
* - `_stageWrites` / `_stageReads` are NOT released — `snapshotSelf()`
|
|
261
|
+
* reads them post-run for the execution-tree snapshot.
|
|
262
|
+
*/
|
|
197
263
|
commit(): void;
|
|
198
264
|
/**
|
|
199
265
|
* Create (or return) this context's linked successor.
|
|
@@ -9,6 +9,6 @@ export { EventLog } from './EventLog.js';
|
|
|
9
9
|
export { SharedMemory } from './SharedMemory.js';
|
|
10
10
|
export { StageContext } from './StageContext.js';
|
|
11
11
|
export { TransactionBuffer } from './TransactionBuffer.js';
|
|
12
|
-
export type { CommitBundle, FlowControlType, FlowMessage, MemoryPatch, ReadSummaryMarker, ReadTrackingMode, ScopeFactory, StageSnapshot, TraceEntry, } from './types.js';
|
|
13
|
-
export { READ_PREVIEW_LENGTH } from './types.js';
|
|
12
|
+
export type { CommitBundle, FlowControlType, FlowMessage, MemoryPatch, ReadSummaryMarker, ReadTrackingMode, RetentionPolicy, ScopeFactory, StageSnapshot, TraceEntry, WriteSummaryMarker, WriteTrackingMode, } from './types.js';
|
|
13
|
+
export { READ_PREVIEW_LENGTH, SUMMARY_PREVIEW_LENGTH } from './types.js';
|
|
14
14
|
export { applySmartMerge, deepSmartMerge, DELIM, getNestedValue, getRunAndGlobalPaths, normalisePath, redactPatch, setNestedValue, updateNestedValue, updateValue, } from './utils.js';
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* types.ts — Core type definitions for the memory library
|
|
3
3
|
*
|
|
4
|
-
* Zero dependencies on old code or other libraries in this package
|
|
4
|
+
* Zero dependencies on old code or other libraries in this package, with one
|
|
5
|
+
* deliberate exception: `capture/` — the standalone leaf module holding the
|
|
6
|
+
* shared retention-policy family and summary-marker builders (extracted from
|
|
7
|
+
* here in #13c-A so the read and write dials, and later RFC-001, share one
|
|
8
|
+
* implementation).
|
|
5
9
|
*/
|
|
10
|
+
import type { RetentionPolicy } from '../capture/policies.js';
|
|
6
11
|
/** A flat key-value bag representing a state patch (overwrite or merge). */
|
|
7
12
|
export interface MemoryPatch {
|
|
8
13
|
[key: string]: any;
|
|
@@ -45,6 +50,9 @@ export interface FlowMessage {
|
|
|
45
50
|
iteration?: number;
|
|
46
51
|
timestamp?: number;
|
|
47
52
|
}
|
|
53
|
+
export type { RetentionPolicy } from '../capture/policies.js';
|
|
54
|
+
export type { ReadSummaryMarker, WriteSummaryMarker } from '../capture/summarize.js';
|
|
55
|
+
export { READ_PREVIEW_LENGTH, SUMMARY_PREVIEW_LENGTH } from '../capture/summarize.js';
|
|
48
56
|
/**
|
|
49
57
|
* Policy for how tracked reads are recorded into `StageSnapshot.stageReads`.
|
|
50
58
|
*
|
|
@@ -62,30 +70,30 @@ export interface FlowMessage {
|
|
|
62
70
|
*
|
|
63
71
|
* Set via `new FlowChartExecutor(chart, { readTracking })` or
|
|
64
72
|
* `executor.setReadTracking(mode)` (before `run()`).
|
|
73
|
+
*
|
|
74
|
+
* Alias of the shared {@link RetentionPolicy} family (#13c-A) — kept as the
|
|
75
|
+
* shipped public name for the read dial.
|
|
65
76
|
*/
|
|
66
|
-
export type ReadTrackingMode =
|
|
77
|
+
export type ReadTrackingMode = RetentionPolicy;
|
|
67
78
|
/**
|
|
68
|
-
*
|
|
79
|
+
* Policy for how tracked writes are recorded into `StageSnapshot.stageWrites`
|
|
80
|
+
* (#13c-A) — the sibling of {@link ReadTrackingMode}.
|
|
81
|
+
*
|
|
82
|
+
* - `'full'` (default) — every tracked write `structuredClone`s the value into
|
|
83
|
+
* the stage's write view. Byte-identical to the historical behavior.
|
|
84
|
+
* - `'summary'` — writes record a cheap {@link WriteSummaryMarker} instead of
|
|
85
|
+
* the cloned value.
|
|
86
|
+
* - `'off'` — writes are not recorded at all; `stageWrites` is absent from the
|
|
87
|
+
* snapshot. The writes themselves still commit to shared state and still
|
|
88
|
+
* appear in the commit log — only the per-stage snapshot bookkeeping (and
|
|
89
|
+
* therefore the commit observer's mutations payload) is affected.
|
|
69
90
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* (first {@link READ_PREVIEW_LENGTH} characters); objects and arrays carry no
|
|
75
|
-
* preview for the same reason.
|
|
91
|
+
* Set via `new FlowChartExecutor(chart, { writeTracking })` or
|
|
92
|
+
* `executor.setWriteTracking(mode)` (before `run()`). See
|
|
93
|
+
* `FlowChartExecutorOptions.writeTracking` for the full observable-consequence
|
|
94
|
+
* contract (onCommit payload, redaction precedence, what is OUT of scope).
|
|
76
95
|
*/
|
|
77
|
-
export
|
|
78
|
-
/** Discriminant — lets snapshot consumers detect marker entries. */
|
|
79
|
-
__readSummary: true;
|
|
80
|
-
/** `typeof` result, refined to 'array' / 'null' for objects. */
|
|
81
|
-
type: 'string' | 'number' | 'boolean' | 'bigint' | 'symbol' | 'function' | 'object' | 'array' | 'null';
|
|
82
|
-
/** Size proxy: string length, array length, or object key count. */
|
|
83
|
-
size?: number;
|
|
84
|
-
/** First {@link READ_PREVIEW_LENGTH} chars — primitives and strings only. */
|
|
85
|
-
preview?: string;
|
|
86
|
-
}
|
|
87
|
-
/** Max characters captured in {@link ReadSummaryMarker.preview}. */
|
|
88
|
-
export declare const READ_PREVIEW_LENGTH = 80;
|
|
96
|
+
export type WriteTrackingMode = RetentionPolicy;
|
|
89
97
|
/** Serialisable representation of a stage's state (for debugging / visualisation). */
|
|
90
98
|
export type StageSnapshot = {
|
|
91
99
|
id: string;
|
|
@@ -98,7 +106,10 @@ export type StageSnapshot = {
|
|
|
98
106
|
subflowId?: string;
|
|
99
107
|
isDecider?: boolean;
|
|
100
108
|
isFork?: boolean;
|
|
101
|
-
/** User-level writes made by this stage (pre-namespace keys → values).
|
|
109
|
+
/** User-level writes made by this stage (pre-namespace keys → values).
|
|
110
|
+
* Shape depends on {@link WriteTrackingMode}: cloned values under `'full'`
|
|
111
|
+
* (default), {@link WriteSummaryMarker}s under `'summary'`, absent under
|
|
112
|
+
* `'off'`. Redacted writes show `'[REDACTED]'` regardless of mode. */
|
|
102
113
|
stageWrites?: Record<string, unknown>;
|
|
103
114
|
/** User-level reads made by this stage (pre-namespace keys → values at read
|
|
104
115
|
* time). Shape depends on {@link ReadTrackingMode}: cloned values under
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { EventLog } from '../memory/EventLog.js';
|
|
13
13
|
import { SharedMemory } from '../memory/SharedMemory.js';
|
|
14
14
|
import { StageContext } from '../memory/StageContext.js';
|
|
15
|
-
import type { CommitBundle, ReadTrackingMode, StageSnapshot } from '../memory/types.js';
|
|
15
|
+
import type { CommitBundle, ReadTrackingMode, StageSnapshot, WriteTrackingMode } from '../memory/types.js';
|
|
16
16
|
/** Snapshot of a single recorder's collected data. */
|
|
17
17
|
export interface RecorderSnapshot {
|
|
18
18
|
id: string;
|
|
@@ -72,6 +72,16 @@ export declare class ExecutionRuntime {
|
|
|
72
72
|
* the freshly-created continuation root.
|
|
73
73
|
*/
|
|
74
74
|
useReadTracking(mode: ReadTrackingMode): void;
|
|
75
|
+
/**
|
|
76
|
+
* Set the write-tracking policy (#13c-A) on the root stage context — the
|
|
77
|
+
* sibling of {@link useReadTracking}, with identical plumbing: descendant
|
|
78
|
+
* contexts inherit via `createNext`/`createChild`; subflow root contexts
|
|
79
|
+
* inherit from their parent-mount context via `SubflowExecutor`. Called by
|
|
80
|
+
* `FlowChartExecutor.createTraverser()` when the executor's policy is not
|
|
81
|
+
* the default `'full'` — including the resume path, where it is applied to
|
|
82
|
+
* the freshly-created continuation root.
|
|
83
|
+
*/
|
|
84
|
+
useWriteTracking(mode: WriteTrackingMode): void;
|
|
75
85
|
/** Preserve the current rootStageContext for snapshots before changing it for resume. */
|
|
76
86
|
preserveSnapshotRoot(): void;
|
|
77
87
|
getPipelines(): string[];
|
|
@@ -22,7 +22,7 @@ import type { CombinedNarrativeEntry } from '../engine/narrative/narrativeTypes.
|
|
|
22
22
|
import type { ManifestEntry } from '../engine/narrative/recorders/ManifestFlowRecorder.js';
|
|
23
23
|
import type { FlowRecorder } from '../engine/narrative/types.js';
|
|
24
24
|
import { type ExecutorResult, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult } from '../engine/types.js';
|
|
25
|
-
import type { ReadTrackingMode } from '../memory/types.js';
|
|
25
|
+
import type { ReadTrackingMode, WriteTrackingMode } from '../memory/types.js';
|
|
26
26
|
import type { FlowchartCheckpoint } from '../pause/types.js';
|
|
27
27
|
import type { CombinedRecorder } from '../recorder/CombinedRecorder.js';
|
|
28
28
|
import type { EmitRecorder } from '../recorder/EmitRecorder.js';
|
|
@@ -77,6 +77,40 @@ export interface FlowChartExecutorOptions<TScope = any> {
|
|
|
77
77
|
* Equivalent to calling `executor.setReadTracking(mode)` before `run()`.
|
|
78
78
|
*/
|
|
79
79
|
readTracking?: ReadTrackingMode;
|
|
80
|
+
/**
|
|
81
|
+
* Policy for `StageSnapshot.stageWrites` (#13c-A) — the sibling of
|
|
82
|
+
* {@link readTracking}; the two dials are independent. Default `'full'` —
|
|
83
|
+
* every tracked write `structuredClone`s the value into the stage's write
|
|
84
|
+
* view (the historical behavior). `'summary'` records a cheap
|
|
85
|
+
* `WriteSummaryMarker` (type/size/preview) per write; `'off'` records
|
|
86
|
+
* nothing — `stageWrites` is absent from the snapshot.
|
|
87
|
+
*
|
|
88
|
+
* Observable consequences — what the policy DOES govern:
|
|
89
|
+
* - `StageSnapshot.stageWrites` (markers under `'summary'`, absent under
|
|
90
|
+
* `'off'`).
|
|
91
|
+
* - The commit observer payload: `ScopeRecorder.onCommit(mutations)`
|
|
92
|
+
* receives the retained `_stageWrites` entries, so it carries the same
|
|
93
|
+
* markers under `'summary'` and an empty mutations bag under `'off'` —
|
|
94
|
+
* deferred/observer consumers see exactly what retention stored.
|
|
95
|
+
*
|
|
96
|
+
* What it does NOT govern:
|
|
97
|
+
* - The writes themselves: shared state, the transaction buffer, and the
|
|
98
|
+
* COMMIT LOG are identical in every mode (commitLog values keep their
|
|
99
|
+
* full payloads — the lossless linear-cost fix for those is #13c-B's
|
|
100
|
+
* delta verb, out of scope here).
|
|
101
|
+
* - Per-op `ScopeRecorder.onWrite` events — they fire with live values
|
|
102
|
+
* regardless (delivery tier, RFC-001's concern), so narrative output is
|
|
103
|
+
* identical in every mode.
|
|
104
|
+
* - Redaction: a policy/per-call-redacted write stores `'[REDACTED]'`
|
|
105
|
+
* under `'full'` AND `'summary'` (redaction takes precedence over the
|
|
106
|
+
* dial; a marker would leak size/preview), and nothing under `'off'`.
|
|
107
|
+
*
|
|
108
|
+
* Caveat: under `'off'` a stage's SNAPSHOT is indistinguishable from one
|
|
109
|
+
* that wrote nothing — but unlike `readTracking: 'off'`, the commit log
|
|
110
|
+
* still records every net change, so "did it write?" stays answerable.
|
|
111
|
+
* Equivalent to calling `executor.setWriteTracking(mode)` before `run()`.
|
|
112
|
+
*/
|
|
113
|
+
writeTracking?: WriteTrackingMode;
|
|
80
114
|
/**
|
|
81
115
|
* Custom error classifier for throttling detection. Return `true` if the
|
|
82
116
|
* error represents a rate-limit or backpressure condition (the executor will
|
|
@@ -163,6 +197,14 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
163
197
|
* for the mode semantics ('full' default / 'summary' / 'off').
|
|
164
198
|
*/
|
|
165
199
|
setReadTracking(mode: ReadTrackingMode): void;
|
|
200
|
+
/**
|
|
201
|
+
* Set the write-tracking policy for `StageSnapshot.stageWrites` (#13c-A).
|
|
202
|
+
* Must be called before run(). Equivalent to the `writeTracking`
|
|
203
|
+
* constructor option — see {@link FlowChartExecutorOptions.writeTracking}
|
|
204
|
+
* for the mode semantics ('full' default / 'summary' / 'off'), the
|
|
205
|
+
* onCommit-payload consequence, and the redaction-precedence rule.
|
|
206
|
+
*/
|
|
207
|
+
setWriteTracking(mode: WriteTrackingMode): void;
|
|
166
208
|
/**
|
|
167
209
|
* Returns a compliance-friendly report of all redaction activity from the
|
|
168
210
|
* most recent run. Never includes actual values.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "footprintjs",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0",
|
|
4
4
|
"description": "Explainable backend flows — automatic causal traces, decision evidence, and MCP tool generation for AI agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sanjay Krishna Anbalagan",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"bench:micro": "npx tsx bench/run.ts",
|
|
58
58
|
"bench:baseline": "npx tsx bench/baseline.ts",
|
|
59
59
|
"bench:depth": "npx tsx bench/depth-probe.ts",
|
|
60
|
+
"bench:heap": "NODE_OPTIONS=--expose-gc npx tsx bench/retained-heap.ts",
|
|
61
|
+
"bench:compare": "npx tsx bench/compare.ts",
|
|
60
62
|
"bench:typecheck": "tsc -p bench/tsconfig.json",
|
|
61
63
|
"docs": "typedoc",
|
|
62
64
|
"docs:serve": "typedoc && npx serve docs",
|