agentfootprint 2.14.5 → 3.1.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.
Files changed (314) hide show
  1. package/README.md +1 -1
  2. package/dist/cache/CacheDecisionSubflow.js +13 -16
  3. package/dist/cache/CacheDecisionSubflow.js.map +1 -1
  4. package/dist/cache/CacheGateDecider.js +18 -3
  5. package/dist/cache/CacheGateDecider.js.map +1 -1
  6. package/dist/cache/cacheRecorder.js +12 -3
  7. package/dist/cache/cacheRecorder.js.map +1 -1
  8. package/dist/conventions.js +155 -4
  9. package/dist/conventions.js.map +1 -1
  10. package/dist/core/Agent.js +115 -32
  11. package/dist/core/Agent.js.map +1 -1
  12. package/dist/core/LLMCall.js +213 -41
  13. package/dist/core/LLMCall.js.map +1 -1
  14. package/dist/core/RunnerBase.js +187 -0
  15. package/dist/core/RunnerBase.js.map +1 -1
  16. package/dist/core/agent/buildAgentChart.js +127 -48
  17. package/dist/core/agent/buildAgentChart.js.map +1 -1
  18. package/dist/core/agent/buildAgentMessageApiChart.js +201 -0
  19. package/dist/core/agent/buildAgentMessageApiChart.js.map +1 -0
  20. package/dist/core/agent/buildCacheSubflow.js +62 -0
  21. package/dist/core/agent/buildCacheSubflow.js.map +1 -0
  22. package/dist/core/agent/buildDynamicAgentChart.js +364 -0
  23. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -0
  24. package/dist/core/agent/buildMessageApiChart.js +154 -0
  25. package/dist/core/agent/buildMessageApiChart.js.map +1 -0
  26. package/dist/core/agent/stages/callLLM.js +11 -0
  27. package/dist/core/agent/stages/callLLM.js.map +1 -1
  28. package/dist/core/agent/stages/reliabilityExecution.js +64 -9
  29. package/dist/core/agent/stages/reliabilityExecution.js.map +1 -1
  30. package/dist/core/humanizeLLMError.js +66 -0
  31. package/dist/core/humanizeLLMError.js.map +1 -0
  32. package/dist/core/runner.js +4 -3
  33. package/dist/core/runner.js.map +1 -1
  34. package/dist/core/slots/buildMessagesSlot.js +2 -2
  35. package/dist/core/slots/buildMessagesSlot.js.map +1 -1
  36. package/dist/core/slots/buildSystemPromptSlot.js +1 -1
  37. package/dist/core/slots/buildSystemPromptSlot.js.map +1 -1
  38. package/dist/core/slots/buildThinkingSubflow.js +1 -1
  39. package/dist/core/slots/buildThinkingSubflow.js.map +1 -1
  40. package/dist/core/slots/buildToolsSlot.js +3 -1
  41. package/dist/core/slots/buildToolsSlot.js.map +1 -1
  42. package/dist/core/translator.js +32 -0
  43. package/dist/core/translator.js.map +1 -0
  44. package/dist/core-flow/Conditional.js +72 -10
  45. package/dist/core-flow/Conditional.js.map +1 -1
  46. package/dist/core-flow/Loop.js +59 -16
  47. package/dist/core-flow/Loop.js.map +1 -1
  48. package/dist/core-flow/Parallel.js +239 -92
  49. package/dist/core-flow/Parallel.js.map +1 -1
  50. package/dist/core-flow/Sequence.js +50 -8
  51. package/dist/core-flow/Sequence.js.map +1 -1
  52. package/dist/esm/cache/CacheDecisionSubflow.js +11 -15
  53. package/dist/esm/cache/CacheDecisionSubflow.js.map +1 -1
  54. package/dist/esm/cache/CacheGateDecider.js +18 -3
  55. package/dist/esm/cache/CacheGateDecider.js.map +1 -1
  56. package/dist/esm/cache/cacheRecorder.js +12 -3
  57. package/dist/esm/cache/cacheRecorder.js.map +1 -1
  58. package/dist/esm/conventions.js +151 -3
  59. package/dist/esm/conventions.js.map +1 -1
  60. package/dist/esm/core/Agent.js +116 -33
  61. package/dist/esm/core/Agent.js.map +1 -1
  62. package/dist/esm/core/LLMCall.js +213 -41
  63. package/dist/esm/core/LLMCall.js.map +1 -1
  64. package/dist/esm/core/RunnerBase.js +187 -0
  65. package/dist/esm/core/RunnerBase.js.map +1 -1
  66. package/dist/esm/core/agent/buildAgentChart.js +128 -49
  67. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  68. package/dist/esm/core/agent/buildAgentMessageApiChart.js +197 -0
  69. package/dist/esm/core/agent/buildAgentMessageApiChart.js.map +1 -0
  70. package/dist/esm/core/agent/buildCacheSubflow.js +58 -0
  71. package/dist/esm/core/agent/buildCacheSubflow.js.map +1 -0
  72. package/dist/esm/core/agent/buildDynamicAgentChart.js +360 -0
  73. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -0
  74. package/dist/esm/core/agent/buildMessageApiChart.js +150 -0
  75. package/dist/esm/core/agent/buildMessageApiChart.js.map +1 -0
  76. package/dist/esm/core/agent/stages/callLLM.js +11 -0
  77. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  78. package/dist/esm/core/agent/stages/reliabilityExecution.js +64 -9
  79. package/dist/esm/core/agent/stages/reliabilityExecution.js.map +1 -1
  80. package/dist/esm/core/humanizeLLMError.js +61 -0
  81. package/dist/esm/core/humanizeLLMError.js.map +1 -0
  82. package/dist/esm/core/runner.js +4 -3
  83. package/dist/esm/core/runner.js.map +1 -1
  84. package/dist/esm/core/slots/buildMessagesSlot.js +2 -2
  85. package/dist/esm/core/slots/buildMessagesSlot.js.map +1 -1
  86. package/dist/esm/core/slots/buildSystemPromptSlot.js +1 -1
  87. package/dist/esm/core/slots/buildSystemPromptSlot.js.map +1 -1
  88. package/dist/esm/core/slots/buildThinkingSubflow.js +1 -1
  89. package/dist/esm/core/slots/buildThinkingSubflow.js.map +1 -1
  90. package/dist/esm/core/slots/buildToolsSlot.js +3 -1
  91. package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
  92. package/dist/esm/core/translator.js +31 -0
  93. package/dist/esm/core/translator.js.map +1 -0
  94. package/dist/esm/core-flow/Conditional.js +72 -10
  95. package/dist/esm/core-flow/Conditional.js.map +1 -1
  96. package/dist/esm/core-flow/Loop.js +59 -16
  97. package/dist/esm/core-flow/Loop.js.map +1 -1
  98. package/dist/esm/core-flow/Parallel.js +240 -93
  99. package/dist/esm/core-flow/Parallel.js.map +1 -1
  100. package/dist/esm/core-flow/Sequence.js +50 -8
  101. package/dist/esm/core-flow/Sequence.js.map +1 -1
  102. package/dist/esm/events/registry.js +10 -0
  103. package/dist/esm/events/registry.js.map +1 -1
  104. package/dist/esm/index.js +22 -1
  105. package/dist/esm/index.js.map +1 -1
  106. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js +16 -9
  107. package/dist/esm/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  108. package/dist/esm/memory/causal/snapshotPipeline.js +6 -2
  109. package/dist/esm/memory/causal/snapshotPipeline.js.map +1 -1
  110. package/dist/esm/memory/pipeline/auto.js +2 -2
  111. package/dist/esm/memory/pipeline/auto.js.map +1 -1
  112. package/dist/esm/memory/pipeline/default.js +4 -2
  113. package/dist/esm/memory/pipeline/default.js.map +1 -1
  114. package/dist/esm/memory/pipeline/ephemeral.js +3 -1
  115. package/dist/esm/memory/pipeline/ephemeral.js.map +1 -1
  116. package/dist/esm/memory/pipeline/fact.js +4 -2
  117. package/dist/esm/memory/pipeline/fact.js.map +1 -1
  118. package/dist/esm/memory/pipeline/narrative.js +4 -2
  119. package/dist/esm/memory/pipeline/narrative.js.map +1 -1
  120. package/dist/esm/memory/pipeline/semantic.js +2 -2
  121. package/dist/esm/memory/pipeline/semantic.js.map +1 -1
  122. package/dist/esm/observe.js +1 -1
  123. package/dist/esm/observe.js.map +1 -1
  124. package/dist/esm/patterns/MapReduce.js +5 -5
  125. package/dist/esm/patterns/MapReduce.js.map +1 -1
  126. package/dist/esm/patterns/Swarm.js +1 -1
  127. package/dist/esm/patterns/Swarm.js.map +1 -1
  128. package/dist/esm/recorders/core/ContextEvaluatedRecorder.js +31 -0
  129. package/dist/esm/recorders/core/ContextEvaluatedRecorder.js.map +1 -0
  130. package/dist/esm/recorders/core/ContextRecorder.js +12 -14
  131. package/dist/esm/recorders/core/ContextRecorder.js.map +1 -1
  132. package/dist/esm/recorders/core/ErrorBridge.js +59 -0
  133. package/dist/esm/recorders/core/ErrorBridge.js.map +1 -0
  134. package/dist/esm/recorders/core/ReliabilityRecorder.js +29 -0
  135. package/dist/esm/recorders/core/ReliabilityRecorder.js.map +1 -0
  136. package/dist/esm/recorders/observability/BoundaryRecorder.js +338 -36
  137. package/dist/esm/recorders/observability/BoundaryRecorder.js.map +1 -1
  138. package/dist/esm/recorders/observability/FlowchartRecorder.js +10 -0
  139. package/dist/esm/recorders/observability/FlowchartRecorder.js.map +1 -1
  140. package/dist/esm/recorders/observability/LiveStateRecorder.js +120 -21
  141. package/dist/esm/recorders/observability/LiveStateRecorder.js.map +1 -1
  142. package/dist/esm/recorders/observability/RunStepRecorder.js +652 -0
  143. package/dist/esm/recorders/observability/RunStepRecorder.js.map +1 -0
  144. package/dist/esm/recorders/observability/commentary/commentaryTemplates.js +1 -0
  145. package/dist/esm/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
  146. package/dist/esm/recorders/observability/internal/ActorArrowClassifier.js +34 -0
  147. package/dist/esm/recorders/observability/internal/ActorArrowClassifier.js.map +1 -0
  148. package/dist/esm/recorders/observability/internal/CandidateAnswerBuffer.js +32 -0
  149. package/dist/esm/recorders/observability/internal/CandidateAnswerBuffer.js.map +1 -0
  150. package/dist/esm/recorders/observability/internal/ForkTracker.js +84 -0
  151. package/dist/esm/recorders/observability/internal/ForkTracker.js.map +1 -0
  152. package/dist/esm/recorders/observability/internal/RootInferrer.js +114 -0
  153. package/dist/esm/recorders/observability/internal/RootInferrer.js.map +1 -0
  154. package/dist/esm/recorders/observability/internal/SequenceSiblingTracker.js +31 -0
  155. package/dist/esm/recorders/observability/internal/SequenceSiblingTracker.js.map +1 -0
  156. package/dist/esm/recorders/observability/observeRunId.js +21 -0
  157. package/dist/esm/recorders/observability/observeRunId.js.map +1 -0
  158. package/dist/esm/reliability/buildReliabilityGateChart.js +11 -5
  159. package/dist/esm/reliability/buildReliabilityGateChart.js.map +1 -1
  160. package/dist/events/registry.js +10 -0
  161. package/dist/events/registry.js.map +1 -1
  162. package/dist/index.js +30 -3
  163. package/dist/index.js.map +1 -1
  164. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js +16 -9
  165. package/dist/lib/injection-engine/buildInjectionEngineSubflow.js.map +1 -1
  166. package/dist/memory/causal/snapshotPipeline.js +6 -2
  167. package/dist/memory/causal/snapshotPipeline.js.map +1 -1
  168. package/dist/memory/pipeline/auto.js +2 -2
  169. package/dist/memory/pipeline/auto.js.map +1 -1
  170. package/dist/memory/pipeline/default.js +4 -2
  171. package/dist/memory/pipeline/default.js.map +1 -1
  172. package/dist/memory/pipeline/ephemeral.js +3 -1
  173. package/dist/memory/pipeline/ephemeral.js.map +1 -1
  174. package/dist/memory/pipeline/fact.js +4 -2
  175. package/dist/memory/pipeline/fact.js.map +1 -1
  176. package/dist/memory/pipeline/narrative.js +4 -2
  177. package/dist/memory/pipeline/narrative.js.map +1 -1
  178. package/dist/memory/pipeline/semantic.js +2 -2
  179. package/dist/memory/pipeline/semantic.js.map +1 -1
  180. package/dist/observe.js +1 -1
  181. package/dist/observe.js.map +1 -1
  182. package/dist/patterns/MapReduce.js +5 -5
  183. package/dist/patterns/MapReduce.js.map +1 -1
  184. package/dist/patterns/Swarm.js +1 -1
  185. package/dist/patterns/Swarm.js.map +1 -1
  186. package/dist/recorders/core/ContextEvaluatedRecorder.js +35 -0
  187. package/dist/recorders/core/ContextEvaluatedRecorder.js.map +1 -0
  188. package/dist/recorders/core/ContextRecorder.js +11 -13
  189. package/dist/recorders/core/ContextRecorder.js.map +1 -1
  190. package/dist/recorders/core/ErrorBridge.js +64 -0
  191. package/dist/recorders/core/ErrorBridge.js.map +1 -0
  192. package/dist/recorders/core/ReliabilityRecorder.js +33 -0
  193. package/dist/recorders/core/ReliabilityRecorder.js.map +1 -0
  194. package/dist/recorders/observability/BoundaryRecorder.js +337 -35
  195. package/dist/recorders/observability/BoundaryRecorder.js.map +1 -1
  196. package/dist/recorders/observability/FlowchartRecorder.js +10 -0
  197. package/dist/recorders/observability/FlowchartRecorder.js.map +1 -1
  198. package/dist/recorders/observability/LiveStateRecorder.js +119 -20
  199. package/dist/recorders/observability/LiveStateRecorder.js.map +1 -1
  200. package/dist/recorders/observability/RunStepRecorder.js +658 -0
  201. package/dist/recorders/observability/RunStepRecorder.js.map +1 -0
  202. package/dist/recorders/observability/commentary/commentaryTemplates.js +1 -0
  203. package/dist/recorders/observability/commentary/commentaryTemplates.js.map +1 -1
  204. package/dist/recorders/observability/internal/ActorArrowClassifier.js +38 -0
  205. package/dist/recorders/observability/internal/ActorArrowClassifier.js.map +1 -0
  206. package/dist/recorders/observability/internal/CandidateAnswerBuffer.js +36 -0
  207. package/dist/recorders/observability/internal/CandidateAnswerBuffer.js.map +1 -0
  208. package/dist/recorders/observability/internal/ForkTracker.js +88 -0
  209. package/dist/recorders/observability/internal/ForkTracker.js.map +1 -0
  210. package/dist/recorders/observability/internal/RootInferrer.js +118 -0
  211. package/dist/recorders/observability/internal/RootInferrer.js.map +1 -0
  212. package/dist/recorders/observability/internal/SequenceSiblingTracker.js +35 -0
  213. package/dist/recorders/observability/internal/SequenceSiblingTracker.js.map +1 -0
  214. package/dist/recorders/observability/observeRunId.js +25 -0
  215. package/dist/recorders/observability/observeRunId.js.map +1 -0
  216. package/dist/reliability/buildReliabilityGateChart.js +11 -5
  217. package/dist/reliability/buildReliabilityGateChart.js.map +1 -1
  218. package/dist/types/cache/CacheDecisionSubflow.d.ts +7 -10
  219. package/dist/types/cache/CacheDecisionSubflow.d.ts.map +1 -1
  220. package/dist/types/cache/CacheGateDecider.d.ts +16 -2
  221. package/dist/types/cache/CacheGateDecider.d.ts.map +1 -1
  222. package/dist/types/cache/cacheRecorder.d.ts.map +1 -1
  223. package/dist/types/conventions.d.ts +101 -1
  224. package/dist/types/conventions.d.ts.map +1 -1
  225. package/dist/types/core/Agent.d.ts +28 -18
  226. package/dist/types/core/Agent.d.ts.map +1 -1
  227. package/dist/types/core/LLMCall.d.ts +73 -11
  228. package/dist/types/core/LLMCall.d.ts.map +1 -1
  229. package/dist/types/core/RunnerBase.d.ts +136 -4
  230. package/dist/types/core/RunnerBase.d.ts.map +1 -1
  231. package/dist/types/core/agent/buildAgentChart.d.ts +38 -19
  232. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  233. package/dist/types/core/agent/buildAgentMessageApiChart.d.ts +41 -0
  234. package/dist/types/core/agent/buildAgentMessageApiChart.d.ts.map +1 -0
  235. package/dist/types/core/agent/buildCacheSubflow.d.ts +36 -0
  236. package/dist/types/core/agent/buildCacheSubflow.d.ts.map +1 -0
  237. package/dist/types/core/agent/buildDynamicAgentChart.d.ts +57 -0
  238. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -0
  239. package/dist/types/core/agent/buildMessageApiChart.d.ts +48 -0
  240. package/dist/types/core/agent/buildMessageApiChart.d.ts.map +1 -0
  241. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  242. package/dist/types/core/agent/stages/reliabilityExecution.d.ts.map +1 -1
  243. package/dist/types/core/agent/types.d.ts +96 -0
  244. package/dist/types/core/agent/types.d.ts.map +1 -1
  245. package/dist/types/core/humanizeLLMError.d.ts +24 -0
  246. package/dist/types/core/humanizeLLMError.d.ts.map +1 -0
  247. package/dist/types/core/runner.d.ts +51 -5
  248. package/dist/types/core/runner.d.ts.map +1 -1
  249. package/dist/types/core/slots/buildMessagesSlot.d.ts.map +1 -1
  250. package/dist/types/core/slots/buildSystemPromptSlot.d.ts.map +1 -1
  251. package/dist/types/core/slots/buildThinkingSubflow.d.ts.map +1 -1
  252. package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
  253. package/dist/types/core/translator.d.ts +95 -0
  254. package/dist/types/core/translator.d.ts.map +1 -0
  255. package/dist/types/core-flow/Conditional.d.ts +48 -4
  256. package/dist/types/core-flow/Conditional.d.ts.map +1 -1
  257. package/dist/types/core-flow/Loop.d.ts +42 -3
  258. package/dist/types/core-flow/Loop.d.ts.map +1 -1
  259. package/dist/types/core-flow/Parallel.d.ts +99 -4
  260. package/dist/types/core-flow/Parallel.d.ts.map +1 -1
  261. package/dist/types/core-flow/Sequence.d.ts +49 -3
  262. package/dist/types/core-flow/Sequence.d.ts.map +1 -1
  263. package/dist/types/events/payloads.d.ts +99 -1
  264. package/dist/types/events/payloads.d.ts.map +1 -1
  265. package/dist/types/events/registry.d.ts +11 -1
  266. package/dist/types/events/registry.d.ts.map +1 -1
  267. package/dist/types/index.d.ts +8 -3
  268. package/dist/types/index.d.ts.map +1 -1
  269. package/dist/types/lib/injection-engine/buildInjectionEngineSubflow.d.ts.map +1 -1
  270. package/dist/types/memory/causal/snapshotPipeline.d.ts.map +1 -1
  271. package/dist/types/memory/pipeline/auto.d.ts.map +1 -1
  272. package/dist/types/memory/pipeline/default.d.ts.map +1 -1
  273. package/dist/types/memory/pipeline/ephemeral.d.ts.map +1 -1
  274. package/dist/types/memory/pipeline/fact.d.ts.map +1 -1
  275. package/dist/types/memory/pipeline/narrative.d.ts.map +1 -1
  276. package/dist/types/memory/pipeline/semantic.d.ts.map +1 -1
  277. package/dist/types/observe.d.ts +2 -2
  278. package/dist/types/observe.d.ts.map +1 -1
  279. package/dist/types/recorders/core/ContextEvaluatedRecorder.d.ts +24 -0
  280. package/dist/types/recorders/core/ContextEvaluatedRecorder.d.ts.map +1 -0
  281. package/dist/types/recorders/core/ContextRecorder.d.ts +0 -2
  282. package/dist/types/recorders/core/ContextRecorder.d.ts.map +1 -1
  283. package/dist/types/recorders/core/ErrorBridge.d.ts +39 -0
  284. package/dist/types/recorders/core/ErrorBridge.d.ts.map +1 -0
  285. package/dist/types/recorders/core/ReliabilityRecorder.d.ts +25 -0
  286. package/dist/types/recorders/core/ReliabilityRecorder.d.ts.map +1 -0
  287. package/dist/types/recorders/observability/BoundaryRecorder.d.ts +167 -6
  288. package/dist/types/recorders/observability/BoundaryRecorder.d.ts.map +1 -1
  289. package/dist/types/recorders/observability/FlowchartRecorder.d.ts.map +1 -1
  290. package/dist/types/recorders/observability/LiveStateRecorder.d.ts +42 -6
  291. package/dist/types/recorders/observability/LiveStateRecorder.d.ts.map +1 -1
  292. package/dist/types/recorders/observability/RunStepRecorder.d.ts +232 -0
  293. package/dist/types/recorders/observability/RunStepRecorder.d.ts.map +1 -0
  294. package/dist/types/recorders/observability/commentary/commentaryTemplates.d.ts.map +1 -1
  295. package/dist/types/recorders/observability/internal/ActorArrowClassifier.d.ts +26 -0
  296. package/dist/types/recorders/observability/internal/ActorArrowClassifier.d.ts.map +1 -0
  297. package/dist/types/recorders/observability/internal/CandidateAnswerBuffer.d.ts +29 -0
  298. package/dist/types/recorders/observability/internal/CandidateAnswerBuffer.d.ts.map +1 -0
  299. package/dist/types/recorders/observability/internal/ForkTracker.d.ts +61 -0
  300. package/dist/types/recorders/observability/internal/ForkTracker.d.ts.map +1 -0
  301. package/dist/types/recorders/observability/internal/RootInferrer.d.ts +52 -0
  302. package/dist/types/recorders/observability/internal/RootInferrer.d.ts.map +1 -0
  303. package/dist/types/recorders/observability/internal/SequenceSiblingTracker.d.ts +25 -0
  304. package/dist/types/recorders/observability/internal/SequenceSiblingTracker.d.ts.map +1 -0
  305. package/dist/types/recorders/observability/observeRunId.d.ts +37 -0
  306. package/dist/types/recorders/observability/observeRunId.d.ts.map +1 -0
  307. package/dist/types/reliability/buildReliabilityGateChart.d.ts.map +1 -1
  308. package/package.json +6 -5
  309. package/dist/core/agent/stages/iterationStart.js +0 -24
  310. package/dist/core/agent/stages/iterationStart.js.map +0 -1
  311. package/dist/esm/core/agent/stages/iterationStart.js +0 -20
  312. package/dist/esm/core/agent/stages/iterationStart.js.map +0 -1
  313. package/dist/types/core/agent/stages/iterationStart.d.ts +0 -16
  314. package/dist/types/core/agent/stages/iterationStart.d.ts.map +0 -1
@@ -75,8 +75,9 @@
75
75
  * }
76
76
  * ```
77
77
  */
78
- import { ROOT_RUNTIME_STAGE_ID, ROOT_SUBFLOW_ID, SequenceRecorder } from 'footprintjs/trace';
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.
@@ -90,15 +91,21 @@ import { SUBFLOW_IDS, STAGE_IDS, slotFromSubflowId } from '../../conventions.js'
90
91
  const AGENT_INTERNAL_LOCAL_IDS = new Set([
91
92
  // Subflow ids (`sf-*`)
92
93
  SUBFLOW_IDS.INJECTION_ENGINE, // collects activeInjections; pure plumbing
94
+ SUBFLOW_IDS.LLM_CALL, // LLMCall's inner invocation wrapper — the meaningful step is the call-llm stage INSIDE; the wrapper itself is a chart-shape container
93
95
  SUBFLOW_IDS.ROUTE,
94
96
  SUBFLOW_IDS.TOOL_CALLS,
95
97
  SUBFLOW_IDS.FINAL,
96
98
  SUBFLOW_IDS.MERGE,
99
+ SUBFLOW_IDS.CACHE, // v2.14 — per-turn cache decision wrapper; pure plumbing
97
100
  SUBFLOW_IDS.CACHE_DECISION, // v2.6 — emits cacheMarkers; not a user step
98
101
  SUBFLOW_IDS.THINKING, // v2.14 — normalize result lands on parent LLM step
99
102
  // Decider stage ids (the same set is used to filter `decision.branch`
100
103
  // events whose deciding stage is plumbing rather than user-facing).
101
104
  STAGE_IDS.CACHE_GATE, // v2.6 — apply-markers / no-markers routing; plumbing
105
+ // LLMCall outer wrapper stage + post-invocation marker — pure chart
106
+ // shape, not user-meaningful steps.
107
+ STAGE_IDS.CLIENT,
108
+ STAGE_IDS.EXTRACT_FINAL,
102
109
  ]);
103
110
  // Constructed as a set on a separate line so we can extend with the
104
111
  // thinking-handler inner-subflow ids below without the literal set
@@ -124,6 +131,53 @@ function isAgentInternalId(localId) {
124
131
  }
125
132
  return false;
126
133
  }
134
+ function toBoundaryLabel(e) {
135
+ if (e.type === 'subflow.entry') {
136
+ return {
137
+ type: 'subflow.entry',
138
+ runtimeStageId: e.runtimeStageId,
139
+ subflowPath: e.subflowPath,
140
+ depth: e.depth,
141
+ ts: e.ts,
142
+ subflowId: e.subflowId,
143
+ localSubflowId: e.localSubflowId,
144
+ subflowName: e.subflowName,
145
+ ...(e.description !== undefined ? { description: e.description } : {}),
146
+ ...(e.primitiveKind !== undefined ? { primitiveKind: e.primitiveKind } : {}),
147
+ ...(e.slotKind !== undefined ? { slotKind: e.slotKind } : {}),
148
+ isAgentInternal: e.isAgentInternal,
149
+ };
150
+ }
151
+ return {
152
+ type: 'run.entry',
153
+ runtimeStageId: e.runtimeStageId,
154
+ subflowPath: e.subflowPath,
155
+ depth: e.depth,
156
+ ts: e.ts,
157
+ };
158
+ }
159
+ /** Build a BoundaryRangeLabel for the open side of a composition pair. */
160
+ function toCompositionBoundaryLabel(e) {
161
+ return {
162
+ type: 'composition.start',
163
+ runtimeStageId: e.runtimeStageId,
164
+ subflowPath: e.subflowPath,
165
+ depth: e.depth,
166
+ ts: e.ts,
167
+ compositionKind: e.kind,
168
+ compositionName: e.name,
169
+ };
170
+ }
171
+ /** Clamp `getCommitCount()` returns to a safe non-negative integer.
172
+ * Defensive against malformed injections returning NaN/Infinity/negatives
173
+ * (security panel review YELLOW #2). */
174
+ function sanitizeCommitCount(n) {
175
+ if (!Number.isFinite(n))
176
+ return 0;
177
+ if (n < 0)
178
+ return 0;
179
+ return n;
180
+ }
127
181
  let _counter = 0;
128
182
  /** Factory — matches the `inOutRecorder()` / `topologyRecorder()` style. */
129
183
  export function boundaryRecorder(options = {}) {
@@ -134,12 +188,35 @@ export function boundaryRecorder(options = {}) {
134
188
  * attach to the executor's FlowRecorder channel; exposes `subscribe()`
135
189
  * to wire to the agentfootprint typed-event dispatcher.
136
190
  *
137
- * Internally stores events in a `SequenceRecorder<DomainEvent>` so the
138
- * usual time-travel utilities (`getEntryRanges`, `accumulate`) work
139
- * out of the box.
191
+ * v5: composes a `SequenceStore<DomainEvent>` (storage) instead of
192
+ * extending the deprecated `SequenceRecorder<T>` base. Time-travel
193
+ * utilities (`getEntryRanges`, `accumulate`) are accessed through the
194
+ * store via the public read API on this class.
140
195
  */
141
- export class BoundaryRecorder extends SequenceRecorder {
196
+ export class BoundaryRecorder {
142
197
  id;
198
+ /** Composition: storage shelf. */
199
+ store = new SequenceStore();
200
+ /**
201
+ * Phase 5 Layer 2 — interval index over commit indices, populated
202
+ * live as boundary entry/exit pairs fire. Consumers (Lens) read
203
+ * `enclosing(commitIdx)` for breadcrumbs and `overlapping(slice)`
204
+ * for time-range queries. Empty when `getCommitCount` is not
205
+ * injected. See `docs/design/boundary-commit-ranges.md`.
206
+ */
207
+ boundaryIndex = new CommitRangeIndex();
208
+ /** Open-range tokens keyed by `runtimeStageId` so the matching exit
209
+ * can close the correct range. Pure side-table; cleared on runId
210
+ * reset. Not exposed externally. */
211
+ openTokens = new Map();
212
+ /** Live commit-count accessor injected by the runner. Sanitized
213
+ * (NaN/Infinity/negative → 0) before use. */
214
+ getCommitCount;
215
+ /** True when `getCommitCount` was explicitly injected. In LEGACY
216
+ * MODE (false), `boundaryIndex` is intentionally NOT populated —
217
+ * zero-width [0,0] ranges would mislead consumers querying the
218
+ * index. Multi-panel review flagged this footgun. */
219
+ hasCommitTracking;
143
220
  /**
144
221
  * Tracks whether the most recent `llm.end` had toolCalls. Used to
145
222
  * classify the NEXT `llm.start` as `'tool→llm'` (vs `'user→llm'` if
@@ -147,50 +224,184 @@ export class BoundaryRecorder extends SequenceRecorder {
147
224
  * `llm.start` event after the classification is applied.
148
225
  */
149
226
  prevLLMEndHadTools = false;
227
+ /**
228
+ * Run-boundary observer — fires resetForNewRun() when
229
+ * traversalContext.runId changes between events AND no boundary is
230
+ * currently open. The "no open boundary" gate distinguishes:
231
+ *
232
+ * - **Legitimate new run** — consumer reuses one recorder across
233
+ * sequential `executor.run()` calls. All prior boundaries closed
234
+ * before the second run began; openTokens is empty when the new
235
+ * runId arrives → safe to wipe state so the second run doesn't
236
+ * alias with the first.
237
+ * - **Composition sub-run** — primitives like `LLMCall`, `Sequence`,
238
+ * and `Parallel` internally spawn their own `FlowChartExecutor`
239
+ * instances. Each sub-executor mints a NEW runId. When that
240
+ * sub-executor fires events on the SHARED recorder, the recorder
241
+ * is still inside the parent run — `openTokens` is non-empty.
242
+ * Resetting here would wipe the parent's boundary index mid-run
243
+ * (the bug Layer 4 surfaced in agentfootprint-lens fanout).
244
+ *
245
+ * The `openTokens.size === 0` check is the cleanest semantic signal:
246
+ * if nothing is in-flight, a runId change means "the consumer started
247
+ * fresh"; if something is open, the new runId is from a sub-executor
248
+ * nested inside the still-ongoing parent.
249
+ */
250
+ runIdGuard = createRunIdObserver(() => {
251
+ if (this.openTokens.size > 0) {
252
+ // Inside an active run — new runId is from a composition sub-
253
+ // executor (LLMCall / Sequence / Parallel). Do NOT reset.
254
+ return;
255
+ }
256
+ this.store.clear();
257
+ this.boundaryIndex.clear();
258
+ this.openTokens.clear();
259
+ this.prevLLMEndHadTools = false;
260
+ });
150
261
  constructor(options = {}) {
151
- super();
152
262
  this.id = options.id ?? `boundary-${++_counter}`;
263
+ this.hasCommitTracking = options.getCommitCount !== undefined;
264
+ const raw = options.getCommitCount;
265
+ this.getCommitCount = raw === undefined ? () => 0 : () => sanitizeCommitCount(raw());
153
266
  }
267
+ /**
268
+ * Reset all transient state.
269
+ *
270
+ * **Composition-safe gate (Phase 5 Layer 4):** if `openTokens.size > 0`
271
+ * the call is a no-op. Rationale: `FlowChartExecutor.run()` calls
272
+ * `r.clear?.()` on every attached recorder during its pre-run loop.
273
+ * When agentfootprint composition primitives (LLMCall, Sequence,
274
+ * Parallel, etc.) propagate the parent's recorders to nested
275
+ * sub-executors, EACH sub-executor's pre-run clear loop calls
276
+ * `clear()` on the SHARED parent recorder mid-run — wiping live
277
+ * parent state. The `openTokens.size > 0` check distinguishes:
278
+ *
279
+ * - **Legitimate reset** — consumer or executor calls `clear()`
280
+ * when no boundary is in-flight (`openTokens` empty). Safe to
281
+ * wipe; the recorder is idle.
282
+ * - **Composition wipe** — sub-executor's pre-run clear fires
283
+ * while the parent has open boundaries (`openTokens` non-empty).
284
+ * Skip the wipe; the parent's state must be preserved.
285
+ *
286
+ * If a consumer needs to forcibly wipe state even with open tokens
287
+ * (e.g., manual recovery after a crashed run), pair `clear()` with
288
+ * an explicit `forceClear()` (TODO — add when the use case shows up;
289
+ * today the recorder lifecycle pattern is "one recorder per logical
290
+ * run" so leaked tokens shouldn't occur).
291
+ */
154
292
  clear() {
155
- super.clear();
293
+ if (this.openTokens.size > 0) {
294
+ // Mid-run wipe attempt — almost certainly a sub-executor's
295
+ // pre-run clear via composition propagation. Skip.
296
+ return;
297
+ }
298
+ this.store.clear();
299
+ this.boundaryIndex.clear();
300
+ this.openTokens.clear();
156
301
  this.prevLLMEndHadTools = false;
302
+ this.runIdGuard.reset();
303
+ }
304
+ observeRunId(runId) {
305
+ this.runIdGuard.observe(runId);
157
306
  }
158
307
  // ── FlowRecorder hooks (footprintjs side) ───────────────────────────
159
308
  onRunStart(event) {
160
- this.emit(buildRunEvent('run.entry', event.payload));
309
+ this.observeRunId(event.traversalContext?.runId);
310
+ const commitIdxBefore = this.getCommitCount();
311
+ const e = buildRunEvent('run.entry', event.payload, commitIdxBefore);
312
+ // Open range BEFORE the store push so a failed push doesn't leak
313
+ // an unclosed range (DS+logic panel review). The label is the
314
+ // stripped projection (no payload) — security-panel YELLOW #1.
315
+ if (this.hasCommitTracking) {
316
+ const token = this.boundaryIndex.open(toBoundaryLabel(e), commitIdxBefore);
317
+ this.openTokens.set(e.runtimeStageId, token);
318
+ }
319
+ this.store.push(e);
161
320
  }
162
321
  onRunEnd(event) {
163
- this.emit(buildRunEvent('run.exit', event.payload));
322
+ this.observeRunId(event.traversalContext?.runId);
323
+ const commitIdxBefore = this.getCommitCount();
324
+ const e = buildRunEvent('run.exit', event.payload, commitIdxBefore);
325
+ // Close the range BEFORE store.push so a failed push doesn't
326
+ // leak a permanently-open range. The range is the canonical
327
+ // truth; the store entry is downstream telemetry.
328
+ if (this.hasCommitTracking) {
329
+ const token = this.openTokens.get(e.runtimeStageId);
330
+ if (token) {
331
+ this.boundaryIndex.close(token, commitIdxBefore);
332
+ this.openTokens.delete(e.runtimeStageId);
333
+ }
334
+ }
335
+ this.store.push(e);
336
+ }
337
+ onRunFailed(event) {
338
+ this.observeRunId(event.traversalContext?.runId);
339
+ const commitIdxBefore = this.getCommitCount();
340
+ // A failed run still TERMINATES — close the root range (mirror
341
+ // onRunEnd) so consumers get a terminal "Run · failed" boundary
342
+ // position instead of a slider that stops mid-call. The error rides
343
+ // as the exit payload so the WHY is reachable at that boundary.
344
+ const e = buildRunEvent('run.exit', { error: event.structuredError.message }, commitIdxBefore);
345
+ if (this.hasCommitTracking) {
346
+ const token = this.openTokens.get(e.runtimeStageId);
347
+ if (token) {
348
+ this.boundaryIndex.close(token, commitIdxBefore);
349
+ this.openTokens.delete(e.runtimeStageId);
350
+ }
351
+ }
352
+ this.store.push(e);
164
353
  }
165
354
  onSubflowEntry(event) {
166
- const e = buildSubflowEvent(event, 'subflow.entry');
167
- if (e)
168
- this.emit(e);
355
+ this.observeRunId(event.traversalContext?.runId);
356
+ const commitIdxBefore = this.getCommitCount();
357
+ const e = buildSubflowEvent(event, 'subflow.entry', commitIdxBefore);
358
+ if (!e)
359
+ return;
360
+ if (this.hasCommitTracking) {
361
+ const token = this.boundaryIndex.open(toBoundaryLabel(e), commitIdxBefore);
362
+ this.openTokens.set(e.runtimeStageId, token);
363
+ }
364
+ this.store.push(e);
169
365
  }
170
366
  onSubflowExit(event) {
171
- const e = buildSubflowEvent(event, 'subflow.exit');
172
- if (e)
173
- this.emit(e);
367
+ this.observeRunId(event.traversalContext?.runId);
368
+ const commitIdxBefore = this.getCommitCount();
369
+ const e = buildSubflowEvent(event, 'subflow.exit', commitIdxBefore);
370
+ if (!e)
371
+ return;
372
+ if (this.hasCommitTracking) {
373
+ const token = this.openTokens.get(e.runtimeStageId);
374
+ if (token) {
375
+ this.boundaryIndex.close(token, commitIdxBefore);
376
+ this.openTokens.delete(e.runtimeStageId);
377
+ }
378
+ }
379
+ this.store.push(e);
174
380
  }
175
381
  onFork(event) {
382
+ this.observeRunId(event.traversalContext?.runId);
176
383
  const ts = Date.now();
177
384
  const ctx = event.traversalContext;
178
385
  const runtimeStageId = ctx?.runtimeStageId ?? '';
179
386
  const segments = ctx?.subflowPath ? ctx.subflowPath.split('/').filter(Boolean) : [];
180
387
  const subflowPath = [ROOT_SUBFLOW_ID, ...segments];
388
+ const commitIdxBefore = this.getCommitCount();
181
389
  for (const childName of event.children) {
182
- this.emit({
390
+ this.store.push({
183
391
  type: 'fork.branch',
184
392
  runtimeStageId,
185
393
  subflowPath,
186
394
  depth: subflowPath.length - 1,
187
395
  ts,
396
+ commitIdxBefore,
397
+ commitIdxAfter: commitIdxBefore,
188
398
  parentSubflowId: event.parent,
189
399
  childName,
190
400
  });
191
401
  }
192
402
  }
193
403
  onDecision(event) {
404
+ this.observeRunId(event.traversalContext?.runId);
194
405
  const ctx = event.traversalContext;
195
406
  // Agent-internal decisions (Route picking tool-calls / final) are
196
407
  // identified by the deciding stage's stableId matching one of the
@@ -204,12 +415,15 @@ export class BoundaryRecorder extends SequenceRecorder {
204
415
  ? stageId.slice(stageId.lastIndexOf('/') + 1)
205
416
  : stageId;
206
417
  const isAgentInternal = isAgentInternalId(localStageId);
207
- this.emit({
418
+ const commitIdxBefore = this.getCommitCount();
419
+ this.store.push({
208
420
  type: 'decision.branch',
209
421
  runtimeStageId: ctx?.runtimeStageId ?? '',
210
422
  subflowPath: pathFromCtx(ctx?.subflowPath),
211
423
  depth: ctxDepth(ctx?.subflowPath),
212
424
  ts: Date.now(),
425
+ commitIdxBefore,
426
+ commitIdxAfter: commitIdxBefore,
213
427
  decider: event.decider,
214
428
  chosen: event.chosen,
215
429
  ...(event.rationale ? { rationale: event.rationale } : {}),
@@ -217,13 +431,17 @@ export class BoundaryRecorder extends SequenceRecorder {
217
431
  });
218
432
  }
219
433
  onLoop(event) {
434
+ this.observeRunId(event.traversalContext?.runId);
220
435
  const ctx = event.traversalContext;
221
- this.emit({
436
+ const commitIdxBefore = this.getCommitCount();
437
+ this.store.push({
222
438
  type: 'loop.iteration',
223
439
  runtimeStageId: ctx?.runtimeStageId ?? '',
224
440
  subflowPath: pathFromCtx(ctx?.subflowPath),
225
441
  depth: ctxDepth(ctx?.subflowPath),
226
442
  ts: Date.now(),
443
+ commitIdxBefore,
444
+ commitIdxAfter: commitIdxBefore,
227
445
  target: event.target,
228
446
  iteration: event.iteration,
229
447
  });
@@ -241,11 +459,22 @@ export class BoundaryRecorder extends SequenceRecorder {
241
459
  return dispatcher.on('*', (event) => this.ingestTypedEvent(event));
242
460
  }
243
461
  ingestTypedEvent(event) {
462
+ // NOTE: deliberately does NOT call observeRunId(event.meta.runId).
463
+ // The agentfootprint dispatcher's runId is generated by a DIFFERENT
464
+ // generator than footprintjs's traversalContext.runId. Mixing them
465
+ // would toggle lastRunId on every event and trigger a false reset.
466
+ // Run-boundary detection happens reliably via the FlowRecorder hooks
467
+ // (onRunStart fires FIRST in any new run, before any typed event).
244
468
  const meta = event.meta;
245
469
  const runtimeStageId = meta.runtimeStageId ?? '';
246
470
  const subflowPath = [ROOT_SUBFLOW_ID, ...(meta.subflowPath ?? [])];
247
471
  const depth = subflowPath.length - 1;
248
472
  const ts = meta.wallClockMs;
473
+ // Phase 5 Layer 2: stamp commit index on every typed event for
474
+ // consumers that want to join domain events with the commit log
475
+ // (e.g., "which LLM call happened during this commit slice?").
476
+ // Typed events don't write to scope themselves, so before === after.
477
+ const commitIdxBefore = this.getCommitCount();
249
478
  switch (event.type) {
250
479
  case 'agentfootprint.stream.llm_start': {
251
480
  const p = event.payload;
@@ -257,12 +486,14 @@ export class BoundaryRecorder extends SequenceRecorder {
257
486
  ? 'tool→llm'
258
487
  : 'user→llm';
259
488
  this.prevLLMEndHadTools = false;
260
- this.emit({
489
+ this.store.push({
261
490
  type: 'llm.start',
262
491
  runtimeStageId,
263
492
  subflowPath,
264
493
  depth,
265
494
  ts,
495
+ commitIdxBefore,
496
+ commitIdxAfter: commitIdxBefore,
266
497
  model: p.model,
267
498
  provider: p.provider,
268
499
  ...(p.systemPromptChars !== undefined ? { systemPromptChars: p.systemPromptChars } : {}),
@@ -279,12 +510,14 @@ export class BoundaryRecorder extends SequenceRecorder {
279
510
  // terminal call (toolCallCount === 0) leaves the flag false so
280
511
  // a hypothetical follow-up call would correctly be 'user→llm'.
281
512
  this.prevLLMEndHadTools = p.toolCallCount > 0;
282
- this.emit({
513
+ this.store.push({
283
514
  type: 'llm.end',
284
515
  runtimeStageId,
285
516
  subflowPath,
286
517
  depth,
287
518
  ts,
519
+ commitIdxBefore,
520
+ commitIdxAfter: commitIdxBefore,
288
521
  content: p.content,
289
522
  toolCallCount: p.toolCallCount,
290
523
  usage: { input: p.usage.input, output: p.usage.output },
@@ -295,12 +528,14 @@ export class BoundaryRecorder extends SequenceRecorder {
295
528
  }
296
529
  case 'agentfootprint.stream.tool_start': {
297
530
  const p = event.payload;
298
- this.emit({
531
+ this.store.push({
299
532
  type: 'tool.start',
300
533
  runtimeStageId,
301
534
  subflowPath,
302
535
  depth,
303
536
  ts,
537
+ commitIdxBefore,
538
+ commitIdxAfter: commitIdxBefore,
304
539
  toolName: p.toolName,
305
540
  toolCallId: p.toolCallId,
306
541
  ...(p.args !== undefined ? { args: p.args } : {}),
@@ -309,12 +544,14 @@ export class BoundaryRecorder extends SequenceRecorder {
309
544
  }
310
545
  case 'agentfootprint.stream.tool_end': {
311
546
  const p = event.payload;
312
- this.emit({
547
+ this.store.push({
313
548
  type: 'tool.end',
314
549
  runtimeStageId,
315
550
  subflowPath,
316
551
  depth,
317
552
  ts,
553
+ commitIdxBefore,
554
+ commitIdxAfter: commitIdxBefore,
318
555
  toolCallId: p.toolCallId,
319
556
  ...(p.result !== undefined ? { result: p.result } : {}),
320
557
  ...(p.durationMs !== undefined ? { durationMs: p.durationMs } : {}),
@@ -324,12 +561,14 @@ export class BoundaryRecorder extends SequenceRecorder {
324
561
  }
325
562
  case 'agentfootprint.context.injected': {
326
563
  const p = event.payload;
327
- this.emit({
564
+ this.store.push({
328
565
  type: 'context.injected',
329
566
  runtimeStageId,
330
567
  subflowPath,
331
568
  depth,
332
569
  ts,
570
+ commitIdxBefore,
571
+ commitIdxAfter: commitIdxBefore,
333
572
  slot: p.slot,
334
573
  source: p.source ?? 'unknown',
335
574
  ...(p.sourceId ? { sourceId: p.sourceId } : {}),
@@ -347,23 +586,82 @@ export class BoundaryRecorder extends SequenceRecorder {
347
586
  });
348
587
  break;
349
588
  }
589
+ case 'agentfootprint.composition.enter': {
590
+ // Open a boundary range for the composition. The MATCHING KEY
591
+ // for open/close is `payload.id` (the composition's stable id),
592
+ // NOT `meta.runtimeStageId`. Reason: the composition's enter
593
+ // event fires from a different stage (entry hook) than its
594
+ // exit event (merge / exit hook) — different `meta.runtimeStageId`s.
595
+ // The composition's `id` is the only field that's the same on
596
+ // both. The boundary range's runtimeStageId (used as the Lens
597
+ // group identity) is the ENTER event's `meta.runtimeStageId`
598
+ // (the entry stage's id) — that's the "fork moment."
599
+ const p = event.payload;
600
+ const e = {
601
+ type: 'composition.start',
602
+ runtimeStageId,
603
+ subflowPath,
604
+ depth,
605
+ ts,
606
+ commitIdxBefore,
607
+ commitIdxAfter: commitIdxBefore,
608
+ kind: p.kind,
609
+ compositionId: p.id,
610
+ name: p.name,
611
+ };
612
+ if (this.hasCommitTracking) {
613
+ const token = this.boundaryIndex.open(toCompositionBoundaryLabel(e), commitIdxBefore);
614
+ this.openTokens.set(`composition:${p.id}`, token);
615
+ }
616
+ this.store.push(e);
617
+ break;
618
+ }
619
+ case 'agentfootprint.composition.exit': {
620
+ // Close the matching composition range. Keyed by `payload.id`
621
+ // — see the enter handler for why this differs from
622
+ // meta.runtimeStageId.
623
+ const p = event.payload;
624
+ const e = {
625
+ type: 'composition.end',
626
+ runtimeStageId,
627
+ subflowPath,
628
+ depth,
629
+ ts,
630
+ commitIdxBefore,
631
+ commitIdxAfter: commitIdxBefore,
632
+ kind: p.kind,
633
+ compositionId: p.id,
634
+ name: p.name ?? '',
635
+ status: p.status,
636
+ durationMs: p.durationMs,
637
+ };
638
+ if (this.hasCommitTracking) {
639
+ const key = `composition:${p.id}`;
640
+ const token = this.openTokens.get(key);
641
+ if (token) {
642
+ this.boundaryIndex.close(token, commitIdxBefore);
643
+ this.openTokens.delete(key);
644
+ }
645
+ }
646
+ this.store.push(e);
647
+ break;
648
+ }
350
649
  default:
351
- // Other typed events (composition.*, agent.*, etc.) are not
352
- // mapped to DomainEvent for now — they're either implied by
353
- // FlowRecorder events (composition) or higher-level summaries
354
- // (agent.turn_*) that downstream selectors derive on demand.
650
+ // Other typed events (agent.*, eval.*, etc.) are not mapped to
651
+ // DomainEvent for now — they're higher-level summaries that
652
+ // downstream selectors derive on demand.
355
653
  break;
356
654
  }
357
655
  }
358
656
  // ── Read API ────────────────────────────────────────────────────────
359
657
  /** All events in capture order (the canonical projection). */
360
658
  getEvents() {
361
- return this.getEntries();
659
+ return this.store.getAll();
362
660
  }
363
661
  /** Type-narrowed lookup: all events of one kind. */
364
662
  getEventsByType(type) {
365
663
  const out = [];
366
- for (const e of this.getEntries()) {
664
+ for (const e of this.store.getAll()) {
367
665
  if (e.type === type)
368
666
  out.push(e);
369
667
  }
@@ -373,7 +671,7 @@ export class BoundaryRecorder extends SequenceRecorder {
373
671
  /** All boundary events (run + subflow, entry + exit interleaved). */
374
672
  getBoundaries() {
375
673
  const out = [];
376
- for (const e of this.getEntries()) {
674
+ for (const e of this.store.getAll()) {
377
675
  if (e.type === 'run.entry' ||
378
676
  e.type === 'run.exit' ||
379
677
  e.type === 'subflow.entry' ||
@@ -393,7 +691,7 @@ export class BoundaryRecorder extends SequenceRecorder {
393
691
  }
394
692
  /** Entry/exit pair for one chart execution by `runtimeStageId`. */
395
693
  getBoundary(runtimeStageId) {
396
- const matches = this.getEntriesForStep(runtimeStageId);
694
+ const matches = this.store.getByKey(runtimeStageId);
397
695
  let entry;
398
696
  let exit;
399
697
  for (const e of matches) {
@@ -420,7 +718,7 @@ export class BoundaryRecorder extends SequenceRecorder {
420
718
  const systemPrompt = [];
421
719
  const messages = [];
422
720
  const tools = [];
423
- for (const e of this.getEntries()) {
721
+ for (const e of this.store.getAll()) {
424
722
  if (e.type !== 'subflow.entry' && e.type !== 'subflow.exit')
425
723
  continue;
426
724
  if (e.slotKind === 'system-prompt')
@@ -459,7 +757,7 @@ export class BoundaryRecorder extends SequenceRecorder {
459
757
  * matches `runtimeStageId`.
460
758
  */
461
759
  aggregateForBoundary(runtimeStageId) {
462
- const events = this.getEntries();
760
+ const events = this.store.getAll();
463
761
  let entry;
464
762
  let exit;
465
763
  for (const e of events) {
@@ -484,7 +782,7 @@ export class BoundaryRecorder extends SequenceRecorder {
484
782
  * not user-facing rollup units.
485
783
  */
486
784
  aggregateAllBoundaries() {
487
- const events = this.getEntries();
785
+ const events = this.store.getAll();
488
786
  const out = [];
489
787
  // Index exits by runtimeStageId for O(1) pair-up.
490
788
  const exitByRid = new Map();
@@ -513,18 +811,20 @@ export class BoundaryRecorder extends SequenceRecorder {
513
811
  }
514
812
  }
515
813
  // ── Internal helpers ─────────────────────────────────────────────────
516
- function buildRunEvent(type, payload) {
814
+ function buildRunEvent(type, payload, commitIdxBefore) {
517
815
  return {
518
816
  type,
519
817
  runtimeStageId: ROOT_RUNTIME_STAGE_ID,
520
818
  subflowPath: [ROOT_SUBFLOW_ID],
521
819
  depth: 0,
522
820
  ts: Date.now(),
821
+ commitIdxBefore,
822
+ commitIdxAfter: commitIdxBefore,
523
823
  payload,
524
824
  isRoot: true,
525
825
  };
526
826
  }
527
- function buildSubflowEvent(event, type) {
827
+ function buildSubflowEvent(event, type, commitIdxBefore) {
528
828
  const subflowId = event.subflowId;
529
829
  if (!subflowId)
530
830
  return undefined;
@@ -545,6 +845,8 @@ function buildSubflowEvent(event, type) {
545
845
  subflowPath,
546
846
  depth,
547
847
  ts: Date.now(),
848
+ commitIdxBefore,
849
+ commitIdxAfter: commitIdxBefore,
548
850
  subflowId,
549
851
  localSubflowId,
550
852
  subflowName: event.name,