agentfootprint 2.14.4 → 3.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/dist/cache/CacheDecisionSubflow.js +3 -1
- package/dist/cache/CacheDecisionSubflow.js.map +1 -1
- package/dist/conventions.js +3 -2
- package/dist/conventions.js.map +1 -1
- package/dist/core/Agent.js +69 -23
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/LLMCall.js +44 -5
- package/dist/core/LLMCall.js.map +1 -1
- package/dist/core/RunnerBase.js +173 -0
- package/dist/core/RunnerBase.js.map +1 -1
- package/dist/core/agent/buildAgentChart.js +12 -2
- package/dist/core/agent/buildAgentChart.js.map +1 -1
- package/dist/core/runner.js +4 -3
- package/dist/core/runner.js.map +1 -1
- package/dist/core/slots/buildMessagesSlot.js +1 -1
- package/dist/core/slots/buildMessagesSlot.js.map +1 -1
- package/dist/core/slots/buildSystemPromptSlot.js +1 -1
- package/dist/core/slots/buildSystemPromptSlot.js.map +1 -1
- package/dist/core/slots/buildThinkingSubflow.js +1 -1
- package/dist/core/slots/buildThinkingSubflow.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +3 -1
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/core/translator.js +32 -0
- package/dist/core/translator.js.map +1 -0
- package/dist/core-flow/Conditional.js +73 -10
- package/dist/core-flow/Conditional.js.map +1 -1
- package/dist/core-flow/Loop.js +60 -16
- package/dist/core-flow/Loop.js.map +1 -1
- package/dist/core-flow/Parallel.js +241 -92
- package/dist/core-flow/Parallel.js.map +1 -1
- package/dist/core-flow/Sequence.js +51 -8
- package/dist/core-flow/Sequence.js.map +1 -1
- package/dist/esm/cache/CacheDecisionSubflow.js +3 -1
- package/dist/esm/cache/CacheDecisionSubflow.js.map +1 -1
- package/dist/esm/conventions.js +3 -2
- package/dist/esm/conventions.js.map +1 -1
- package/dist/esm/core/Agent.js +69 -23
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/LLMCall.js +44 -5
- package/dist/esm/core/LLMCall.js.map +1 -1
- package/dist/esm/core/RunnerBase.js +173 -0
- package/dist/esm/core/RunnerBase.js.map +1 -1
- package/dist/esm/core/agent/buildAgentChart.js +12 -2
- package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
- package/dist/esm/core/runner.js +4 -3
- package/dist/esm/core/runner.js.map +1 -1
- package/dist/esm/core/slots/buildMessagesSlot.js +1 -1
- package/dist/esm/core/slots/buildMessagesSlot.js.map +1 -1
- package/dist/esm/core/slots/buildSystemPromptSlot.js +1 -1
- package/dist/esm/core/slots/buildSystemPromptSlot.js.map +1 -1
- package/dist/esm/core/slots/buildThinkingSubflow.js +1 -1
- package/dist/esm/core/slots/buildThinkingSubflow.js.map +1 -1
- package/dist/esm/core/slots/buildToolsSlot.js +3 -1
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/translator.js +31 -0
- package/dist/esm/core/translator.js.map +1 -0
- package/dist/esm/core-flow/Conditional.js +73 -10
- package/dist/esm/core-flow/Conditional.js.map +1 -1
- package/dist/esm/core-flow/Loop.js +60 -16
- package/dist/esm/core-flow/Loop.js.map +1 -1
- package/dist/esm/core-flow/Parallel.js +242 -93
- package/dist/esm/core-flow/Parallel.js.map +1 -1
- package/dist/esm/core-flow/Sequence.js +51 -8
- package/dist/esm/core-flow/Sequence.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +1 -1
- package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/esm/memory/causal/snapshotPipeline.js +6 -2
- package/dist/esm/memory/causal/snapshotPipeline.js.map +1 -1
- package/dist/esm/memory/pipeline/auto.js +2 -2
- package/dist/esm/memory/pipeline/auto.js.map +1 -1
- package/dist/esm/memory/pipeline/default.js +4 -2
- package/dist/esm/memory/pipeline/default.js.map +1 -1
- package/dist/esm/memory/pipeline/ephemeral.js +3 -1
- package/dist/esm/memory/pipeline/ephemeral.js.map +1 -1
- package/dist/esm/memory/pipeline/fact.js +4 -2
- package/dist/esm/memory/pipeline/fact.js.map +1 -1
- package/dist/esm/memory/pipeline/narrative.js +4 -2
- package/dist/esm/memory/pipeline/narrative.js.map +1 -1
- package/dist/esm/memory/pipeline/semantic.js +2 -2
- package/dist/esm/memory/pipeline/semantic.js.map +1 -1
- package/dist/esm/observe.js +1 -1
- package/dist/esm/observe.js.map +1 -1
- package/dist/esm/patterns/MapReduce.js +5 -5
- package/dist/esm/patterns/MapReduce.js.map +1 -1
- package/dist/esm/patterns/Swarm.js +1 -1
- package/dist/esm/patterns/Swarm.js.map +1 -1
- package/dist/esm/recorders/observability/BoundaryRecorder.js +315 -36
- package/dist/esm/recorders/observability/BoundaryRecorder.js.map +1 -1
- package/dist/esm/recorders/observability/FlowchartRecorder.js +10 -0
- package/dist/esm/recorders/observability/FlowchartRecorder.js.map +1 -1
- package/dist/esm/recorders/observability/LiveStateRecorder.js +112 -21
- package/dist/esm/recorders/observability/LiveStateRecorder.js.map +1 -1
- package/dist/esm/recorders/observability/RunStepRecorder.js +652 -0
- package/dist/esm/recorders/observability/RunStepRecorder.js.map +1 -0
- package/dist/esm/recorders/observability/commentary/commentaryTemplates.js +3 -4
- package/dist/esm/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
- package/dist/esm/recorders/observability/internal/ActorArrowClassifier.js +34 -0
- package/dist/esm/recorders/observability/internal/ActorArrowClassifier.js.map +1 -0
- package/dist/esm/recorders/observability/internal/CandidateAnswerBuffer.js +32 -0
- package/dist/esm/recorders/observability/internal/CandidateAnswerBuffer.js.map +1 -0
- package/dist/esm/recorders/observability/internal/ForkTracker.js +84 -0
- package/dist/esm/recorders/observability/internal/ForkTracker.js.map +1 -0
- package/dist/esm/recorders/observability/internal/RootInferrer.js +114 -0
- package/dist/esm/recorders/observability/internal/RootInferrer.js.map +1 -0
- package/dist/esm/recorders/observability/internal/SequenceSiblingTracker.js +31 -0
- package/dist/esm/recorders/observability/internal/SequenceSiblingTracker.js.map +1 -0
- package/dist/esm/recorders/observability/observeRunId.js +21 -0
- package/dist/esm/recorders/observability/observeRunId.js.map +1 -0
- package/dist/esm/reliability/buildReliabilityGateChart.js +1 -1
- package/dist/esm/reliability/buildReliabilityGateChart.js.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +1 -1
- package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
- package/dist/memory/causal/snapshotPipeline.js +6 -2
- package/dist/memory/causal/snapshotPipeline.js.map +1 -1
- package/dist/memory/pipeline/auto.js +2 -2
- package/dist/memory/pipeline/auto.js.map +1 -1
- package/dist/memory/pipeline/default.js +4 -2
- package/dist/memory/pipeline/default.js.map +1 -1
- package/dist/memory/pipeline/ephemeral.js +3 -1
- package/dist/memory/pipeline/ephemeral.js.map +1 -1
- package/dist/memory/pipeline/fact.js +4 -2
- package/dist/memory/pipeline/fact.js.map +1 -1
- package/dist/memory/pipeline/narrative.js +4 -2
- package/dist/memory/pipeline/narrative.js.map +1 -1
- package/dist/memory/pipeline/semantic.js +2 -2
- package/dist/memory/pipeline/semantic.js.map +1 -1
- package/dist/observe.js +1 -1
- package/dist/observe.js.map +1 -1
- package/dist/patterns/MapReduce.js +5 -5
- package/dist/patterns/MapReduce.js.map +1 -1
- package/dist/patterns/Swarm.js +1 -1
- package/dist/patterns/Swarm.js.map +1 -1
- package/dist/recorders/observability/BoundaryRecorder.js +314 -35
- package/dist/recorders/observability/BoundaryRecorder.js.map +1 -1
- package/dist/recorders/observability/FlowchartRecorder.js +10 -0
- package/dist/recorders/observability/FlowchartRecorder.js.map +1 -1
- package/dist/recorders/observability/LiveStateRecorder.js +111 -20
- package/dist/recorders/observability/LiveStateRecorder.js.map +1 -1
- package/dist/recorders/observability/RunStepRecorder.js +658 -0
- package/dist/recorders/observability/RunStepRecorder.js.map +1 -0
- package/dist/recorders/observability/commentary/commentaryTemplates.js +3 -4
- package/dist/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
- package/dist/recorders/observability/internal/ActorArrowClassifier.js +38 -0
- package/dist/recorders/observability/internal/ActorArrowClassifier.js.map +1 -0
- package/dist/recorders/observability/internal/CandidateAnswerBuffer.js +36 -0
- package/dist/recorders/observability/internal/CandidateAnswerBuffer.js.map +1 -0
- package/dist/recorders/observability/internal/ForkTracker.js +88 -0
- package/dist/recorders/observability/internal/ForkTracker.js.map +1 -0
- package/dist/recorders/observability/internal/RootInferrer.js +118 -0
- package/dist/recorders/observability/internal/RootInferrer.js.map +1 -0
- package/dist/recorders/observability/internal/SequenceSiblingTracker.js +35 -0
- package/dist/recorders/observability/internal/SequenceSiblingTracker.js.map +1 -0
- package/dist/recorders/observability/observeRunId.js +25 -0
- package/dist/recorders/observability/observeRunId.js.map +1 -0
- package/dist/reliability/buildReliabilityGateChart.js +1 -1
- package/dist/reliability/buildReliabilityGateChart.js.map +1 -1
- package/dist/types/cache/CacheDecisionSubflow.d.ts.map +1 -1
- package/dist/types/conventions.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts +20 -18
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/LLMCall.d.ts +28 -2
- package/dist/types/core/LLMCall.d.ts.map +1 -1
- package/dist/types/core/RunnerBase.d.ts +124 -4
- package/dist/types/core/RunnerBase.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentChart.d.ts +7 -1
- package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +29 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/runner.d.ts +51 -5
- package/dist/types/core/runner.d.ts.map +1 -1
- package/dist/types/core/slots/buildMessagesSlot.d.ts.map +1 -1
- package/dist/types/core/slots/buildSystemPromptSlot.d.ts.map +1 -1
- package/dist/types/core/slots/buildThinkingSubflow.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/core/translator.d.ts +95 -0
- package/dist/types/core/translator.d.ts.map +1 -0
- package/dist/types/core-flow/Conditional.d.ts +48 -4
- package/dist/types/core-flow/Conditional.d.ts.map +1 -1
- package/dist/types/core-flow/Loop.d.ts +42 -3
- package/dist/types/core-flow/Loop.d.ts.map +1 -1
- package/dist/types/core-flow/Parallel.d.ts +99 -4
- package/dist/types/core-flow/Parallel.d.ts.map +1 -1
- package/dist/types/core-flow/Sequence.d.ts +49 -3
- package/dist/types/core-flow/Sequence.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +22 -1
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
- package/dist/types/memory/causal/snapshotPipeline.d.ts.map +1 -1
- package/dist/types/memory/pipeline/auto.d.ts.map +1 -1
- package/dist/types/memory/pipeline/default.d.ts.map +1 -1
- package/dist/types/memory/pipeline/ephemeral.d.ts.map +1 -1
- package/dist/types/memory/pipeline/fact.d.ts.map +1 -1
- package/dist/types/memory/pipeline/narrative.d.ts.map +1 -1
- package/dist/types/memory/pipeline/semantic.d.ts.map +1 -1
- package/dist/types/observe.d.ts +2 -2
- package/dist/types/observe.d.ts.map +1 -1
- package/dist/types/recorders/observability/BoundaryRecorder.d.ts +160 -6
- package/dist/types/recorders/observability/BoundaryRecorder.d.ts.map +1 -1
- package/dist/types/recorders/observability/FlowchartRecorder.d.ts.map +1 -1
- package/dist/types/recorders/observability/LiveStateRecorder.d.ts +42 -6
- package/dist/types/recorders/observability/LiveStateRecorder.d.ts.map +1 -1
- package/dist/types/recorders/observability/RunStepRecorder.d.ts +232 -0
- package/dist/types/recorders/observability/RunStepRecorder.d.ts.map +1 -0
- package/dist/types/recorders/observability/commentary/commentaryTemplates.d.ts.map +1 -1
- package/dist/types/recorders/observability/internal/ActorArrowClassifier.d.ts +26 -0
- package/dist/types/recorders/observability/internal/ActorArrowClassifier.d.ts.map +1 -0
- package/dist/types/recorders/observability/internal/CandidateAnswerBuffer.d.ts +29 -0
- package/dist/types/recorders/observability/internal/CandidateAnswerBuffer.d.ts.map +1 -0
- package/dist/types/recorders/observability/internal/ForkTracker.d.ts +61 -0
- package/dist/types/recorders/observability/internal/ForkTracker.d.ts.map +1 -0
- package/dist/types/recorders/observability/internal/RootInferrer.d.ts +52 -0
- package/dist/types/recorders/observability/internal/RootInferrer.d.ts.map +1 -0
- package/dist/types/recorders/observability/internal/SequenceSiblingTracker.d.ts +25 -0
- package/dist/types/recorders/observability/internal/SequenceSiblingTracker.d.ts.map +1 -0
- package/dist/types/recorders/observability/observeRunId.d.ts +37 -0
- package/dist/types/recorders/observability/observeRunId.d.ts.map +1 -0
- package/dist/types/reliability/buildReliabilityGateChart.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -75,8 +75,9 @@
|
|
|
75
75
|
* }
|
|
76
76
|
* ```
|
|
77
77
|
*/
|
|
78
|
-
import { ROOT_RUNTIME_STAGE_ID, ROOT_SUBFLOW_ID,
|
|
78
|
+
import { ROOT_RUNTIME_STAGE_ID, ROOT_SUBFLOW_ID, SequenceStore, CommitRangeIndex, } from 'footprintjs/trace';
|
|
79
79
|
import { SUBFLOW_IDS, STAGE_IDS, slotFromSubflowId } from '../../conventions.js';
|
|
80
|
+
import { createRunIdObserver } from './observeRunId.js';
|
|
80
81
|
/** Closed set of routing/wrapper subflow IDs that are pure plumbing.
|
|
81
82
|
* Slot subflows (`sf-system-prompt` / `sf-messages` / `sf-tools`) are
|
|
82
83
|
* NOT in this set — they're real context-engineering moments.
|
|
@@ -124,6 +125,53 @@ function isAgentInternalId(localId) {
|
|
|
124
125
|
}
|
|
125
126
|
return false;
|
|
126
127
|
}
|
|
128
|
+
function toBoundaryLabel(e) {
|
|
129
|
+
if (e.type === 'subflow.entry') {
|
|
130
|
+
return {
|
|
131
|
+
type: 'subflow.entry',
|
|
132
|
+
runtimeStageId: e.runtimeStageId,
|
|
133
|
+
subflowPath: e.subflowPath,
|
|
134
|
+
depth: e.depth,
|
|
135
|
+
ts: e.ts,
|
|
136
|
+
subflowId: e.subflowId,
|
|
137
|
+
localSubflowId: e.localSubflowId,
|
|
138
|
+
subflowName: e.subflowName,
|
|
139
|
+
...(e.description !== undefined ? { description: e.description } : {}),
|
|
140
|
+
...(e.primitiveKind !== undefined ? { primitiveKind: e.primitiveKind } : {}),
|
|
141
|
+
...(e.slotKind !== undefined ? { slotKind: e.slotKind } : {}),
|
|
142
|
+
isAgentInternal: e.isAgentInternal,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
type: 'run.entry',
|
|
147
|
+
runtimeStageId: e.runtimeStageId,
|
|
148
|
+
subflowPath: e.subflowPath,
|
|
149
|
+
depth: e.depth,
|
|
150
|
+
ts: e.ts,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Build a BoundaryRangeLabel for the open side of a composition pair. */
|
|
154
|
+
function toCompositionBoundaryLabel(e) {
|
|
155
|
+
return {
|
|
156
|
+
type: 'composition.start',
|
|
157
|
+
runtimeStageId: e.runtimeStageId,
|
|
158
|
+
subflowPath: e.subflowPath,
|
|
159
|
+
depth: e.depth,
|
|
160
|
+
ts: e.ts,
|
|
161
|
+
compositionKind: e.kind,
|
|
162
|
+
compositionName: e.name,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** Clamp `getCommitCount()` returns to a safe non-negative integer.
|
|
166
|
+
* Defensive against malformed injections returning NaN/Infinity/negatives
|
|
167
|
+
* (security panel review YELLOW #2). */
|
|
168
|
+
function sanitizeCommitCount(n) {
|
|
169
|
+
if (!Number.isFinite(n))
|
|
170
|
+
return 0;
|
|
171
|
+
if (n < 0)
|
|
172
|
+
return 0;
|
|
173
|
+
return n;
|
|
174
|
+
}
|
|
127
175
|
let _counter = 0;
|
|
128
176
|
/** Factory — matches the `inOutRecorder()` / `topologyRecorder()` style. */
|
|
129
177
|
export function boundaryRecorder(options = {}) {
|
|
@@ -134,12 +182,35 @@ export function boundaryRecorder(options = {}) {
|
|
|
134
182
|
* attach to the executor's FlowRecorder channel; exposes `subscribe()`
|
|
135
183
|
* to wire to the agentfootprint typed-event dispatcher.
|
|
136
184
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
185
|
+
* v5: composes a `SequenceStore<DomainEvent>` (storage) instead of
|
|
186
|
+
* extending the deprecated `SequenceRecorder<T>` base. Time-travel
|
|
187
|
+
* utilities (`getEntryRanges`, `accumulate`) are accessed through the
|
|
188
|
+
* store via the public read API on this class.
|
|
140
189
|
*/
|
|
141
|
-
export class BoundaryRecorder
|
|
190
|
+
export class BoundaryRecorder {
|
|
142
191
|
id;
|
|
192
|
+
/** Composition: storage shelf. */
|
|
193
|
+
store = new SequenceStore();
|
|
194
|
+
/**
|
|
195
|
+
* Phase 5 Layer 2 — interval index over commit indices, populated
|
|
196
|
+
* live as boundary entry/exit pairs fire. Consumers (Lens) read
|
|
197
|
+
* `enclosing(commitIdx)` for breadcrumbs and `overlapping(slice)`
|
|
198
|
+
* for time-range queries. Empty when `getCommitCount` is not
|
|
199
|
+
* injected. See `docs/design/boundary-commit-ranges.md`.
|
|
200
|
+
*/
|
|
201
|
+
boundaryIndex = new CommitRangeIndex();
|
|
202
|
+
/** Open-range tokens keyed by `runtimeStageId` so the matching exit
|
|
203
|
+
* can close the correct range. Pure side-table; cleared on runId
|
|
204
|
+
* reset. Not exposed externally. */
|
|
205
|
+
openTokens = new Map();
|
|
206
|
+
/** Live commit-count accessor injected by the runner. Sanitized
|
|
207
|
+
* (NaN/Infinity/negative → 0) before use. */
|
|
208
|
+
getCommitCount;
|
|
209
|
+
/** True when `getCommitCount` was explicitly injected. In LEGACY
|
|
210
|
+
* MODE (false), `boundaryIndex` is intentionally NOT populated —
|
|
211
|
+
* zero-width [0,0] ranges would mislead consumers querying the
|
|
212
|
+
* index. Multi-panel review flagged this footgun. */
|
|
213
|
+
hasCommitTracking;
|
|
143
214
|
/**
|
|
144
215
|
* Tracks whether the most recent `llm.end` had toolCalls. Used to
|
|
145
216
|
* classify the NEXT `llm.start` as `'tool→llm'` (vs `'user→llm'` if
|
|
@@ -147,50 +218,167 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
147
218
|
* `llm.start` event after the classification is applied.
|
|
148
219
|
*/
|
|
149
220
|
prevLLMEndHadTools = false;
|
|
221
|
+
/**
|
|
222
|
+
* Run-boundary observer — fires resetForNewRun() when
|
|
223
|
+
* traversalContext.runId changes between events AND no boundary is
|
|
224
|
+
* currently open. The "no open boundary" gate distinguishes:
|
|
225
|
+
*
|
|
226
|
+
* - **Legitimate new run** — consumer reuses one recorder across
|
|
227
|
+
* sequential `executor.run()` calls. All prior boundaries closed
|
|
228
|
+
* before the second run began; openTokens is empty when the new
|
|
229
|
+
* runId arrives → safe to wipe state so the second run doesn't
|
|
230
|
+
* alias with the first.
|
|
231
|
+
* - **Composition sub-run** — primitives like `LLMCall`, `Sequence`,
|
|
232
|
+
* and `Parallel` internally spawn their own `FlowChartExecutor`
|
|
233
|
+
* instances. Each sub-executor mints a NEW runId. When that
|
|
234
|
+
* sub-executor fires events on the SHARED recorder, the recorder
|
|
235
|
+
* is still inside the parent run — `openTokens` is non-empty.
|
|
236
|
+
* Resetting here would wipe the parent's boundary index mid-run
|
|
237
|
+
* (the bug Layer 4 surfaced in agentfootprint-lens fanout).
|
|
238
|
+
*
|
|
239
|
+
* The `openTokens.size === 0` check is the cleanest semantic signal:
|
|
240
|
+
* if nothing is in-flight, a runId change means "the consumer started
|
|
241
|
+
* fresh"; if something is open, the new runId is from a sub-executor
|
|
242
|
+
* nested inside the still-ongoing parent.
|
|
243
|
+
*/
|
|
244
|
+
runIdGuard = createRunIdObserver(() => {
|
|
245
|
+
if (this.openTokens.size > 0) {
|
|
246
|
+
// Inside an active run — new runId is from a composition sub-
|
|
247
|
+
// executor (LLMCall / Sequence / Parallel). Do NOT reset.
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.store.clear();
|
|
251
|
+
this.boundaryIndex.clear();
|
|
252
|
+
this.openTokens.clear();
|
|
253
|
+
this.prevLLMEndHadTools = false;
|
|
254
|
+
});
|
|
150
255
|
constructor(options = {}) {
|
|
151
|
-
super();
|
|
152
256
|
this.id = options.id ?? `boundary-${++_counter}`;
|
|
257
|
+
this.hasCommitTracking = options.getCommitCount !== undefined;
|
|
258
|
+
const raw = options.getCommitCount;
|
|
259
|
+
this.getCommitCount = raw === undefined ? () => 0 : () => sanitizeCommitCount(raw());
|
|
153
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Reset all transient state.
|
|
263
|
+
*
|
|
264
|
+
* **Composition-safe gate (Phase 5 Layer 4):** if `openTokens.size > 0`
|
|
265
|
+
* the call is a no-op. Rationale: `FlowChartExecutor.run()` calls
|
|
266
|
+
* `r.clear?.()` on every attached recorder during its pre-run loop.
|
|
267
|
+
* When agentfootprint composition primitives (LLMCall, Sequence,
|
|
268
|
+
* Parallel, etc.) propagate the parent's recorders to nested
|
|
269
|
+
* sub-executors, EACH sub-executor's pre-run clear loop calls
|
|
270
|
+
* `clear()` on the SHARED parent recorder mid-run — wiping live
|
|
271
|
+
* parent state. The `openTokens.size > 0` check distinguishes:
|
|
272
|
+
*
|
|
273
|
+
* - **Legitimate reset** — consumer or executor calls `clear()`
|
|
274
|
+
* when no boundary is in-flight (`openTokens` empty). Safe to
|
|
275
|
+
* wipe; the recorder is idle.
|
|
276
|
+
* - **Composition wipe** — sub-executor's pre-run clear fires
|
|
277
|
+
* while the parent has open boundaries (`openTokens` non-empty).
|
|
278
|
+
* Skip the wipe; the parent's state must be preserved.
|
|
279
|
+
*
|
|
280
|
+
* If a consumer needs to forcibly wipe state even with open tokens
|
|
281
|
+
* (e.g., manual recovery after a crashed run), pair `clear()` with
|
|
282
|
+
* an explicit `forceClear()` (TODO — add when the use case shows up;
|
|
283
|
+
* today the recorder lifecycle pattern is "one recorder per logical
|
|
284
|
+
* run" so leaked tokens shouldn't occur).
|
|
285
|
+
*/
|
|
154
286
|
clear() {
|
|
155
|
-
|
|
287
|
+
if (this.openTokens.size > 0) {
|
|
288
|
+
// Mid-run wipe attempt — almost certainly a sub-executor's
|
|
289
|
+
// pre-run clear via composition propagation. Skip.
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.store.clear();
|
|
293
|
+
this.boundaryIndex.clear();
|
|
294
|
+
this.openTokens.clear();
|
|
156
295
|
this.prevLLMEndHadTools = false;
|
|
296
|
+
this.runIdGuard.reset();
|
|
297
|
+
}
|
|
298
|
+
observeRunId(runId) {
|
|
299
|
+
this.runIdGuard.observe(runId);
|
|
157
300
|
}
|
|
158
301
|
// ── FlowRecorder hooks (footprintjs side) ───────────────────────────
|
|
159
302
|
onRunStart(event) {
|
|
160
|
-
this.
|
|
303
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
304
|
+
const commitIdxBefore = this.getCommitCount();
|
|
305
|
+
const e = buildRunEvent('run.entry', event.payload, commitIdxBefore);
|
|
306
|
+
// Open range BEFORE the store push so a failed push doesn't leak
|
|
307
|
+
// an unclosed range (DS+logic panel review). The label is the
|
|
308
|
+
// stripped projection (no payload) — security-panel YELLOW #1.
|
|
309
|
+
if (this.hasCommitTracking) {
|
|
310
|
+
const token = this.boundaryIndex.open(toBoundaryLabel(e), commitIdxBefore);
|
|
311
|
+
this.openTokens.set(e.runtimeStageId, token);
|
|
312
|
+
}
|
|
313
|
+
this.store.push(e);
|
|
161
314
|
}
|
|
162
315
|
onRunEnd(event) {
|
|
163
|
-
this.
|
|
316
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
317
|
+
const commitIdxBefore = this.getCommitCount();
|
|
318
|
+
const e = buildRunEvent('run.exit', event.payload, commitIdxBefore);
|
|
319
|
+
// Close the range BEFORE store.push so a failed push doesn't
|
|
320
|
+
// leak a permanently-open range. The range is the canonical
|
|
321
|
+
// truth; the store entry is downstream telemetry.
|
|
322
|
+
if (this.hasCommitTracking) {
|
|
323
|
+
const token = this.openTokens.get(e.runtimeStageId);
|
|
324
|
+
if (token) {
|
|
325
|
+
this.boundaryIndex.close(token, commitIdxBefore);
|
|
326
|
+
this.openTokens.delete(e.runtimeStageId);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
this.store.push(e);
|
|
164
330
|
}
|
|
165
331
|
onSubflowEntry(event) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
332
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
333
|
+
const commitIdxBefore = this.getCommitCount();
|
|
334
|
+
const e = buildSubflowEvent(event, 'subflow.entry', commitIdxBefore);
|
|
335
|
+
if (!e)
|
|
336
|
+
return;
|
|
337
|
+
if (this.hasCommitTracking) {
|
|
338
|
+
const token = this.boundaryIndex.open(toBoundaryLabel(e), commitIdxBefore);
|
|
339
|
+
this.openTokens.set(e.runtimeStageId, token);
|
|
340
|
+
}
|
|
341
|
+
this.store.push(e);
|
|
169
342
|
}
|
|
170
343
|
onSubflowExit(event) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
344
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
345
|
+
const commitIdxBefore = this.getCommitCount();
|
|
346
|
+
const e = buildSubflowEvent(event, 'subflow.exit', commitIdxBefore);
|
|
347
|
+
if (!e)
|
|
348
|
+
return;
|
|
349
|
+
if (this.hasCommitTracking) {
|
|
350
|
+
const token = this.openTokens.get(e.runtimeStageId);
|
|
351
|
+
if (token) {
|
|
352
|
+
this.boundaryIndex.close(token, commitIdxBefore);
|
|
353
|
+
this.openTokens.delete(e.runtimeStageId);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
this.store.push(e);
|
|
174
357
|
}
|
|
175
358
|
onFork(event) {
|
|
359
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
176
360
|
const ts = Date.now();
|
|
177
361
|
const ctx = event.traversalContext;
|
|
178
362
|
const runtimeStageId = ctx?.runtimeStageId ?? '';
|
|
179
363
|
const segments = ctx?.subflowPath ? ctx.subflowPath.split('/').filter(Boolean) : [];
|
|
180
364
|
const subflowPath = [ROOT_SUBFLOW_ID, ...segments];
|
|
365
|
+
const commitIdxBefore = this.getCommitCount();
|
|
181
366
|
for (const childName of event.children) {
|
|
182
|
-
this.
|
|
367
|
+
this.store.push({
|
|
183
368
|
type: 'fork.branch',
|
|
184
369
|
runtimeStageId,
|
|
185
370
|
subflowPath,
|
|
186
371
|
depth: subflowPath.length - 1,
|
|
187
372
|
ts,
|
|
373
|
+
commitIdxBefore,
|
|
374
|
+
commitIdxAfter: commitIdxBefore,
|
|
188
375
|
parentSubflowId: event.parent,
|
|
189
376
|
childName,
|
|
190
377
|
});
|
|
191
378
|
}
|
|
192
379
|
}
|
|
193
380
|
onDecision(event) {
|
|
381
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
194
382
|
const ctx = event.traversalContext;
|
|
195
383
|
// Agent-internal decisions (Route picking tool-calls / final) are
|
|
196
384
|
// identified by the deciding stage's stableId matching one of the
|
|
@@ -204,12 +392,15 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
204
392
|
? stageId.slice(stageId.lastIndexOf('/') + 1)
|
|
205
393
|
: stageId;
|
|
206
394
|
const isAgentInternal = isAgentInternalId(localStageId);
|
|
207
|
-
this.
|
|
395
|
+
const commitIdxBefore = this.getCommitCount();
|
|
396
|
+
this.store.push({
|
|
208
397
|
type: 'decision.branch',
|
|
209
398
|
runtimeStageId: ctx?.runtimeStageId ?? '',
|
|
210
399
|
subflowPath: pathFromCtx(ctx?.subflowPath),
|
|
211
400
|
depth: ctxDepth(ctx?.subflowPath),
|
|
212
401
|
ts: Date.now(),
|
|
402
|
+
commitIdxBefore,
|
|
403
|
+
commitIdxAfter: commitIdxBefore,
|
|
213
404
|
decider: event.decider,
|
|
214
405
|
chosen: event.chosen,
|
|
215
406
|
...(event.rationale ? { rationale: event.rationale } : {}),
|
|
@@ -217,13 +408,17 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
217
408
|
});
|
|
218
409
|
}
|
|
219
410
|
onLoop(event) {
|
|
411
|
+
this.observeRunId(event.traversalContext?.runId);
|
|
220
412
|
const ctx = event.traversalContext;
|
|
221
|
-
this.
|
|
413
|
+
const commitIdxBefore = this.getCommitCount();
|
|
414
|
+
this.store.push({
|
|
222
415
|
type: 'loop.iteration',
|
|
223
416
|
runtimeStageId: ctx?.runtimeStageId ?? '',
|
|
224
417
|
subflowPath: pathFromCtx(ctx?.subflowPath),
|
|
225
418
|
depth: ctxDepth(ctx?.subflowPath),
|
|
226
419
|
ts: Date.now(),
|
|
420
|
+
commitIdxBefore,
|
|
421
|
+
commitIdxAfter: commitIdxBefore,
|
|
227
422
|
target: event.target,
|
|
228
423
|
iteration: event.iteration,
|
|
229
424
|
});
|
|
@@ -241,11 +436,22 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
241
436
|
return dispatcher.on('*', (event) => this.ingestTypedEvent(event));
|
|
242
437
|
}
|
|
243
438
|
ingestTypedEvent(event) {
|
|
439
|
+
// NOTE: deliberately does NOT call observeRunId(event.meta.runId).
|
|
440
|
+
// The agentfootprint dispatcher's runId is generated by a DIFFERENT
|
|
441
|
+
// generator than footprintjs's traversalContext.runId. Mixing them
|
|
442
|
+
// would toggle lastRunId on every event and trigger a false reset.
|
|
443
|
+
// Run-boundary detection happens reliably via the FlowRecorder hooks
|
|
444
|
+
// (onRunStart fires FIRST in any new run, before any typed event).
|
|
244
445
|
const meta = event.meta;
|
|
245
446
|
const runtimeStageId = meta.runtimeStageId ?? '';
|
|
246
447
|
const subflowPath = [ROOT_SUBFLOW_ID, ...(meta.subflowPath ?? [])];
|
|
247
448
|
const depth = subflowPath.length - 1;
|
|
248
449
|
const ts = meta.wallClockMs;
|
|
450
|
+
// Phase 5 Layer 2: stamp commit index on every typed event for
|
|
451
|
+
// consumers that want to join domain events with the commit log
|
|
452
|
+
// (e.g., "which LLM call happened during this commit slice?").
|
|
453
|
+
// Typed events don't write to scope themselves, so before === after.
|
|
454
|
+
const commitIdxBefore = this.getCommitCount();
|
|
249
455
|
switch (event.type) {
|
|
250
456
|
case 'agentfootprint.stream.llm_start': {
|
|
251
457
|
const p = event.payload;
|
|
@@ -257,12 +463,14 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
257
463
|
? 'tool→llm'
|
|
258
464
|
: 'user→llm';
|
|
259
465
|
this.prevLLMEndHadTools = false;
|
|
260
|
-
this.
|
|
466
|
+
this.store.push({
|
|
261
467
|
type: 'llm.start',
|
|
262
468
|
runtimeStageId,
|
|
263
469
|
subflowPath,
|
|
264
470
|
depth,
|
|
265
471
|
ts,
|
|
472
|
+
commitIdxBefore,
|
|
473
|
+
commitIdxAfter: commitIdxBefore,
|
|
266
474
|
model: p.model,
|
|
267
475
|
provider: p.provider,
|
|
268
476
|
...(p.systemPromptChars !== undefined ? { systemPromptChars: p.systemPromptChars } : {}),
|
|
@@ -279,12 +487,14 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
279
487
|
// terminal call (toolCallCount === 0) leaves the flag false so
|
|
280
488
|
// a hypothetical follow-up call would correctly be 'user→llm'.
|
|
281
489
|
this.prevLLMEndHadTools = p.toolCallCount > 0;
|
|
282
|
-
this.
|
|
490
|
+
this.store.push({
|
|
283
491
|
type: 'llm.end',
|
|
284
492
|
runtimeStageId,
|
|
285
493
|
subflowPath,
|
|
286
494
|
depth,
|
|
287
495
|
ts,
|
|
496
|
+
commitIdxBefore,
|
|
497
|
+
commitIdxAfter: commitIdxBefore,
|
|
288
498
|
content: p.content,
|
|
289
499
|
toolCallCount: p.toolCallCount,
|
|
290
500
|
usage: { input: p.usage.input, output: p.usage.output },
|
|
@@ -295,12 +505,14 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
295
505
|
}
|
|
296
506
|
case 'agentfootprint.stream.tool_start': {
|
|
297
507
|
const p = event.payload;
|
|
298
|
-
this.
|
|
508
|
+
this.store.push({
|
|
299
509
|
type: 'tool.start',
|
|
300
510
|
runtimeStageId,
|
|
301
511
|
subflowPath,
|
|
302
512
|
depth,
|
|
303
513
|
ts,
|
|
514
|
+
commitIdxBefore,
|
|
515
|
+
commitIdxAfter: commitIdxBefore,
|
|
304
516
|
toolName: p.toolName,
|
|
305
517
|
toolCallId: p.toolCallId,
|
|
306
518
|
...(p.args !== undefined ? { args: p.args } : {}),
|
|
@@ -309,12 +521,14 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
309
521
|
}
|
|
310
522
|
case 'agentfootprint.stream.tool_end': {
|
|
311
523
|
const p = event.payload;
|
|
312
|
-
this.
|
|
524
|
+
this.store.push({
|
|
313
525
|
type: 'tool.end',
|
|
314
526
|
runtimeStageId,
|
|
315
527
|
subflowPath,
|
|
316
528
|
depth,
|
|
317
529
|
ts,
|
|
530
|
+
commitIdxBefore,
|
|
531
|
+
commitIdxAfter: commitIdxBefore,
|
|
318
532
|
toolCallId: p.toolCallId,
|
|
319
533
|
...(p.result !== undefined ? { result: p.result } : {}),
|
|
320
534
|
...(p.durationMs !== undefined ? { durationMs: p.durationMs } : {}),
|
|
@@ -324,12 +538,14 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
324
538
|
}
|
|
325
539
|
case 'agentfootprint.context.injected': {
|
|
326
540
|
const p = event.payload;
|
|
327
|
-
this.
|
|
541
|
+
this.store.push({
|
|
328
542
|
type: 'context.injected',
|
|
329
543
|
runtimeStageId,
|
|
330
544
|
subflowPath,
|
|
331
545
|
depth,
|
|
332
546
|
ts,
|
|
547
|
+
commitIdxBefore,
|
|
548
|
+
commitIdxAfter: commitIdxBefore,
|
|
333
549
|
slot: p.slot,
|
|
334
550
|
source: p.source ?? 'unknown',
|
|
335
551
|
...(p.sourceId ? { sourceId: p.sourceId } : {}),
|
|
@@ -347,23 +563,82 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
347
563
|
});
|
|
348
564
|
break;
|
|
349
565
|
}
|
|
566
|
+
case 'agentfootprint.composition.enter': {
|
|
567
|
+
// Open a boundary range for the composition. The MATCHING KEY
|
|
568
|
+
// for open/close is `payload.id` (the composition's stable id),
|
|
569
|
+
// NOT `meta.runtimeStageId`. Reason: the composition's enter
|
|
570
|
+
// event fires from a different stage (entry hook) than its
|
|
571
|
+
// exit event (merge / exit hook) — different `meta.runtimeStageId`s.
|
|
572
|
+
// The composition's `id` is the only field that's the same on
|
|
573
|
+
// both. The boundary range's runtimeStageId (used as the Lens
|
|
574
|
+
// group identity) is the ENTER event's `meta.runtimeStageId`
|
|
575
|
+
// (the entry stage's id) — that's the "fork moment."
|
|
576
|
+
const p = event.payload;
|
|
577
|
+
const e = {
|
|
578
|
+
type: 'composition.start',
|
|
579
|
+
runtimeStageId,
|
|
580
|
+
subflowPath,
|
|
581
|
+
depth,
|
|
582
|
+
ts,
|
|
583
|
+
commitIdxBefore,
|
|
584
|
+
commitIdxAfter: commitIdxBefore,
|
|
585
|
+
kind: p.kind,
|
|
586
|
+
compositionId: p.id,
|
|
587
|
+
name: p.name,
|
|
588
|
+
};
|
|
589
|
+
if (this.hasCommitTracking) {
|
|
590
|
+
const token = this.boundaryIndex.open(toCompositionBoundaryLabel(e), commitIdxBefore);
|
|
591
|
+
this.openTokens.set(`composition:${p.id}`, token);
|
|
592
|
+
}
|
|
593
|
+
this.store.push(e);
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
case 'agentfootprint.composition.exit': {
|
|
597
|
+
// Close the matching composition range. Keyed by `payload.id`
|
|
598
|
+
// — see the enter handler for why this differs from
|
|
599
|
+
// meta.runtimeStageId.
|
|
600
|
+
const p = event.payload;
|
|
601
|
+
const e = {
|
|
602
|
+
type: 'composition.end',
|
|
603
|
+
runtimeStageId,
|
|
604
|
+
subflowPath,
|
|
605
|
+
depth,
|
|
606
|
+
ts,
|
|
607
|
+
commitIdxBefore,
|
|
608
|
+
commitIdxAfter: commitIdxBefore,
|
|
609
|
+
kind: p.kind,
|
|
610
|
+
compositionId: p.id,
|
|
611
|
+
name: p.name ?? '',
|
|
612
|
+
status: p.status,
|
|
613
|
+
durationMs: p.durationMs,
|
|
614
|
+
};
|
|
615
|
+
if (this.hasCommitTracking) {
|
|
616
|
+
const key = `composition:${p.id}`;
|
|
617
|
+
const token = this.openTokens.get(key);
|
|
618
|
+
if (token) {
|
|
619
|
+
this.boundaryIndex.close(token, commitIdxBefore);
|
|
620
|
+
this.openTokens.delete(key);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
this.store.push(e);
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
350
626
|
default:
|
|
351
|
-
// Other typed events (
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
// (agent.turn_*) that downstream selectors derive on demand.
|
|
627
|
+
// Other typed events (agent.*, eval.*, etc.) are not mapped to
|
|
628
|
+
// DomainEvent for now — they're higher-level summaries that
|
|
629
|
+
// downstream selectors derive on demand.
|
|
355
630
|
break;
|
|
356
631
|
}
|
|
357
632
|
}
|
|
358
633
|
// ── Read API ────────────────────────────────────────────────────────
|
|
359
634
|
/** All events in capture order (the canonical projection). */
|
|
360
635
|
getEvents() {
|
|
361
|
-
return this.
|
|
636
|
+
return this.store.getAll();
|
|
362
637
|
}
|
|
363
638
|
/** Type-narrowed lookup: all events of one kind. */
|
|
364
639
|
getEventsByType(type) {
|
|
365
640
|
const out = [];
|
|
366
|
-
for (const e of this.
|
|
641
|
+
for (const e of this.store.getAll()) {
|
|
367
642
|
if (e.type === type)
|
|
368
643
|
out.push(e);
|
|
369
644
|
}
|
|
@@ -373,7 +648,7 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
373
648
|
/** All boundary events (run + subflow, entry + exit interleaved). */
|
|
374
649
|
getBoundaries() {
|
|
375
650
|
const out = [];
|
|
376
|
-
for (const e of this.
|
|
651
|
+
for (const e of this.store.getAll()) {
|
|
377
652
|
if (e.type === 'run.entry' ||
|
|
378
653
|
e.type === 'run.exit' ||
|
|
379
654
|
e.type === 'subflow.entry' ||
|
|
@@ -393,7 +668,7 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
393
668
|
}
|
|
394
669
|
/** Entry/exit pair for one chart execution by `runtimeStageId`. */
|
|
395
670
|
getBoundary(runtimeStageId) {
|
|
396
|
-
const matches = this.
|
|
671
|
+
const matches = this.store.getByKey(runtimeStageId);
|
|
397
672
|
let entry;
|
|
398
673
|
let exit;
|
|
399
674
|
for (const e of matches) {
|
|
@@ -420,7 +695,7 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
420
695
|
const systemPrompt = [];
|
|
421
696
|
const messages = [];
|
|
422
697
|
const tools = [];
|
|
423
|
-
for (const e of this.
|
|
698
|
+
for (const e of this.store.getAll()) {
|
|
424
699
|
if (e.type !== 'subflow.entry' && e.type !== 'subflow.exit')
|
|
425
700
|
continue;
|
|
426
701
|
if (e.slotKind === 'system-prompt')
|
|
@@ -459,7 +734,7 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
459
734
|
* matches `runtimeStageId`.
|
|
460
735
|
*/
|
|
461
736
|
aggregateForBoundary(runtimeStageId) {
|
|
462
|
-
const events = this.
|
|
737
|
+
const events = this.store.getAll();
|
|
463
738
|
let entry;
|
|
464
739
|
let exit;
|
|
465
740
|
for (const e of events) {
|
|
@@ -484,7 +759,7 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
484
759
|
* not user-facing rollup units.
|
|
485
760
|
*/
|
|
486
761
|
aggregateAllBoundaries() {
|
|
487
|
-
const events = this.
|
|
762
|
+
const events = this.store.getAll();
|
|
488
763
|
const out = [];
|
|
489
764
|
// Index exits by runtimeStageId for O(1) pair-up.
|
|
490
765
|
const exitByRid = new Map();
|
|
@@ -513,18 +788,20 @@ export class BoundaryRecorder extends SequenceRecorder {
|
|
|
513
788
|
}
|
|
514
789
|
}
|
|
515
790
|
// ── Internal helpers ─────────────────────────────────────────────────
|
|
516
|
-
function buildRunEvent(type, payload) {
|
|
791
|
+
function buildRunEvent(type, payload, commitIdxBefore) {
|
|
517
792
|
return {
|
|
518
793
|
type,
|
|
519
794
|
runtimeStageId: ROOT_RUNTIME_STAGE_ID,
|
|
520
795
|
subflowPath: [ROOT_SUBFLOW_ID],
|
|
521
796
|
depth: 0,
|
|
522
797
|
ts: Date.now(),
|
|
798
|
+
commitIdxBefore,
|
|
799
|
+
commitIdxAfter: commitIdxBefore,
|
|
523
800
|
payload,
|
|
524
801
|
isRoot: true,
|
|
525
802
|
};
|
|
526
803
|
}
|
|
527
|
-
function buildSubflowEvent(event, type) {
|
|
804
|
+
function buildSubflowEvent(event, type, commitIdxBefore) {
|
|
528
805
|
const subflowId = event.subflowId;
|
|
529
806
|
if (!subflowId)
|
|
530
807
|
return undefined;
|
|
@@ -545,6 +822,8 @@ function buildSubflowEvent(event, type) {
|
|
|
545
822
|
subflowPath,
|
|
546
823
|
depth,
|
|
547
824
|
ts: Date.now(),
|
|
825
|
+
commitIdxBefore,
|
|
826
|
+
commitIdxAfter: commitIdxBefore,
|
|
548
827
|
subflowId,
|
|
549
828
|
localSubflowId,
|
|
550
829
|
subflowName: event.name,
|