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
@@ -0,0 +1,652 @@
1
+ /**
2
+ * RunStepRecorder — slider-ready ordered list of RunSteps, BUILT
3
+ * INCREMENTALLY during traversal. Real-time recorder, not a walker.
4
+ *
5
+ * Pattern: extends `SequenceRecorder<RunStep>` (shared storage shelf)
6
+ * and implements `CombinedRecorder` (FlowRecorder hooks).
7
+ * Subscribes to the agentfootprint typed-event dispatcher
8
+ * for actor-arrow events. Each event handler decides whether
9
+ * to emit a step; state lives on the instance and persists
10
+ * across the run.
11
+ * Role: The single source of truth for "what slider positions
12
+ * exist in this run, and what transitions does each light
13
+ * up." Lens consumers attach the recorder once and read
14
+ * `getSteps()` — no per-render re-derivation.
15
+ *
16
+ * Why this matters: the older `buildRunSteps(events)` walker violated
17
+ * footprintjs's core principle ("collect during traversal, never
18
+ * post-process"). Each call walked the full event log multiple times;
19
+ * the playground triggered a full walk on every flowchart update,
20
+ * yielding O(N²) total work for a streaming run. The recorder pattern
21
+ * is O(N) — one handler call per event — and matches BoundaryRecorder /
22
+ * FlowchartRecorder / KeyedRecorder idioms throughout the library.
23
+ *
24
+ * The `buildRunSteps(...)` function is RETAINED as a thin compatibility
25
+ * shim that constructs a fresh recorder, replays events through it,
26
+ * and returns the resulting entries. Useful for snapshot-from-saved-
27
+ * events use cases (replay, testing, post-hoc analysis). Live consumers
28
+ * should attach the recorder directly via `runner.attach(rec)`.
29
+ */
30
+ import { ROOT_RUNTIME_STAGE_ID, ROOT_SUBFLOW_ID, SequenceStore, splitStageId, } from 'footprintjs/trace';
31
+ import { createRunIdObserver } from './observeRunId.js';
32
+ import { ForkTracker } from './internal/ForkTracker.js';
33
+ import { SequenceSiblingTracker } from './internal/SequenceSiblingTracker.js';
34
+ import { CandidateAnswerBuffer } from './internal/CandidateAnswerBuffer.js';
35
+ import { RootInferrer } from './internal/RootInferrer.js';
36
+ import { ActorArrowClassifier } from './internal/ActorArrowClassifier.js';
37
+ let _counter = 0;
38
+ /** Factory — matches the `boundaryRecorder()` / `topologyRecorder()` style. */
39
+ export function runStepRecorder(options = {}) {
40
+ return new RunStepRecorder(options);
41
+ }
42
+ // ─── Constants ──────────────────────────────────────────────────────
43
+ const ACTOR_USER = 'actor:user';
44
+ const LEAF_PRIMITIVES = new Set(['Agent', 'LLMCall']);
45
+ function isLeafPrimitive(kind) {
46
+ return kind !== undefined && LEAF_PRIMITIVES.has(kind);
47
+ }
48
+ function lastSegment(subflowId) {
49
+ const i = subflowId.lastIndexOf('/');
50
+ return i >= 0 ? subflowId.slice(i + 1) : subflowId;
51
+ }
52
+ function isPathPrefix(prefix, path) {
53
+ if (prefix.length > path.length)
54
+ return false;
55
+ for (let i = 0; i < prefix.length; i++) {
56
+ if (prefix[i] !== path[i])
57
+ return false;
58
+ }
59
+ return true;
60
+ }
61
+ function pathFromCtx(s) {
62
+ const segments = s ? s.split('/').filter(Boolean) : [];
63
+ return [ROOT_SUBFLOW_ID, ...segments];
64
+ }
65
+ // ─── Recorder class ────────────────────────────────────────────────
66
+ /**
67
+ * Real-time slider-step recorder. Emits a `RunStep` whenever an event
68
+ * marks a meaningful slider transition. State persists on the instance
69
+ * so successive events update bookkeeping in O(1).
70
+ *
71
+ * Attach via `runner.attach(rec)` for FlowRecorder events; call
72
+ * `rec.subscribe(runner.dispatcher)` for actor-arrow events. The
73
+ * `getSteps(drillPath?)` method returns the already-built list (no
74
+ * walking) with optional drill-scope filtering.
75
+ */
76
+ export class RunStepRecorder {
77
+ id;
78
+ /** Composition: storage shelf for the slider-step sequence. */
79
+ store = new SequenceStore();
80
+ /** Run-boundary observer — fires this.clear() when traversalContext.runId
81
+ * changes between events. THIS IS THE FIX for the Parallel multi-run
82
+ * aliasing bug — without it `forkKey = ${parent}@${rid}` collides
83
+ * because rid resets to `seed#0` on each run. */
84
+ runIdGuard = createRunIdObserver(() => this.resetForNewRun());
85
+ // ── Composed sub-trackers (one concern each) ─────────────────────
86
+ /** Stack of currently-open boundaries. The recorder owns this
87
+ * directly because it's a simple stack and frames are recorder-
88
+ * shaped. */
89
+ boundaryStack = [];
90
+ /** Fork-emission coalescing + branch-exit tally. */
91
+ forks = new ForkTracker();
92
+ /** Tracks the most-recent leaf exit per depth → "forwards" handoff. */
93
+ siblings = new SequenceSiblingTracker();
94
+ /** Buffers a "this MIGHT be the answer" leaf until onRunEnd. */
95
+ answerBuffer = new CandidateAnswerBuffer();
96
+ /** Run-root inference state machine (leaf vs composition). */
97
+ rootInferrer = new RootInferrer();
98
+ /** llm.start / llm.end actor-arrow classifier. */
99
+ actorArrows = new ActorArrowClassifier();
100
+ /** Has the first `asks` step fired? */
101
+ asksEmitted = false;
102
+ constructor(options = {}) {
103
+ this.id = options.id ?? `run-step-${++_counter}`;
104
+ }
105
+ /**
106
+ * Emit a RunStep, auto-mirroring `anchor.runtimeStageId` to the
107
+ * top-level `runtimeStageId` field that the keyed index uses. Single
108
+ * source of truth (the anchor) — never inconsistent with the storage
109
+ * key.
110
+ */
111
+ push(step) {
112
+ this.store.push({ ...step, runtimeStageId: step.anchor.runtimeStageId });
113
+ }
114
+ /** Internal seq-numbering helper — mirrors the store size so each
115
+ * RunStep gets a unique 0-based index in emit order. */
116
+ get entryCount() {
117
+ return this.store.size;
118
+ }
119
+ clear() {
120
+ this.resetForNewRun();
121
+ this.runIdGuard.reset();
122
+ }
123
+ /** Internal — wipe all per-run state WITHOUT resetting the runIdGuard
124
+ * itself. Called by `clear()` (which then resets the guard) AND by
125
+ * the runIdGuard's onNewRun callback (where the guard is mid-update
126
+ * and must NOT be reset, only the recorder's data should be).
127
+ *
128
+ * Note: each sub-tracker owns its OWN clear; the orchestrator just
129
+ * fans out. Adding new state to a sub-tracker requires no edit here. */
130
+ resetForNewRun() {
131
+ this.store.clear();
132
+ this.boundaryStack = [];
133
+ this.forks.clear();
134
+ this.siblings.clear();
135
+ this.answerBuffer.clear();
136
+ this.rootInferrer.clear();
137
+ this.actorArrows.clear();
138
+ this.asksEmitted = false;
139
+ }
140
+ observeRunId(runId) {
141
+ this.runIdGuard.observe(runId);
142
+ }
143
+ // ── FlowRecorder hooks ─────────────────────────────────────────
144
+ onRunStart(event) {
145
+ this.observeRunId(event.traversalContext?.runId);
146
+ // Nothing to emit yet — the first leaf entry / fork event starts
147
+ // the slider.
148
+ }
149
+ onRunEnd(event) {
150
+ this.observeRunId(event.traversalContext?.runId);
151
+ // Emit the deferred `answers` step for the last leaf exit at run
152
+ // scope. Without this, runs with a Sequence-rooted shape never
153
+ // see their final answer reflected on the slider.
154
+ this.flushCandidateAnswer();
155
+ }
156
+ onSubflowEntry(event) {
157
+ if (!event.subflowId)
158
+ return;
159
+ this.observeRunId(event.traversalContext?.runId);
160
+ const ctx = event.traversalContext;
161
+ const runtimeStageId = ctx?.runtimeStageId ?? '';
162
+ const subflowPath = pathFromCtx(ctx?.subflowPath);
163
+ const depth = subflowPath.length - 1;
164
+ const ts = Date.now();
165
+ const subflowId = event.subflowId;
166
+ const description = event.description;
167
+ const primitiveKind = parsePrimitiveKind(description);
168
+ // Delegate root inference to the dedicated state machine.
169
+ this.rootInferrer.observeSubflowEntry(depth, primitiveKind);
170
+ const frame = {
171
+ subflowId,
172
+ subflowPath,
173
+ ...(primitiveKind ? { primitiveKind } : {}),
174
+ depth,
175
+ };
176
+ const isLeaf = isLeafPrimitive(primitiveKind);
177
+ if (this.forks.isForkChild(subflowId)) {
178
+ // Branch wrapper of a tracked fork — push frame so the matching
179
+ // exit can tally for the merge step. No sequential emission.
180
+ this.boundaryStack.push(frame);
181
+ return;
182
+ }
183
+ if (isLeaf) {
184
+ const prevSibling = this.siblings.peekPrevSibling(depth);
185
+ if (prevSibling) {
186
+ // Sequence-style sibling handoff: previous leaf exited, new
187
+ // leaf entered at the same depth → emit `forwards`.
188
+ this.push({
189
+ seq: this.entryCount,
190
+ kind: 'sequential',
191
+ transitions: [{ from: prevSibling, to: subflowId, via: 'next', label: 'forwards' }],
192
+ anchor: { runtimeStageId, subflowPath },
193
+ label: 'forwards',
194
+ tsMs: ts,
195
+ });
196
+ }
197
+ else if (!this.asksEmitted) {
198
+ // First leaf in the run — User asks it.
199
+ this.push({
200
+ seq: this.entryCount,
201
+ kind: 'sequential',
202
+ transitions: [{ from: ACTOR_USER, to: subflowId, via: 'next', label: 'asks' }],
203
+ anchor: { runtimeStageId, subflowPath },
204
+ label: 'asks',
205
+ tsMs: ts,
206
+ });
207
+ this.asksEmitted = true;
208
+ }
209
+ }
210
+ this.boundaryStack.push(frame);
211
+ }
212
+ onSubflowExit(event) {
213
+ this.observeRunId(event.traversalContext?.runId);
214
+ const ctx = event.traversalContext;
215
+ const runtimeStageId = ctx?.runtimeStageId ?? '';
216
+ const ts = Date.now();
217
+ const frame = this.boundaryStack.pop();
218
+ if (!frame)
219
+ return;
220
+ const isLeaf = isLeafPrimitive(frame.primitiveKind);
221
+ // Fork-branch exit: ForkTracker tallies completion and signals
222
+ // when ALL branches have exited so we can emit the merge step.
223
+ const merge = this.forks.recordChildExit(frame.subflowId);
224
+ if (merge) {
225
+ this.push({
226
+ seq: this.entryCount,
227
+ kind: 'merge',
228
+ transitions: merge.branches.map((sid) => ({
229
+ from: sid,
230
+ to: ACTOR_USER,
231
+ via: 'next',
232
+ label: lastSegment(sid),
233
+ })),
234
+ anchor: { runtimeStageId, subflowPath: frame.subflowPath },
235
+ label: `merge (${merge.branches.length})`,
236
+ tsMs: ts,
237
+ meta: { kind: 'merge', mergedCount: merge.branches.length },
238
+ });
239
+ return;
240
+ }
241
+ // If it WAS a fork child but not the last to exit, still skip the
242
+ // leaf-handoff path below.
243
+ if (this.forks.isForkChild(frame.subflowId))
244
+ return;
245
+ if (isLeaf) {
246
+ // Sibling handoff + answer-candidate buffering. Both delegated.
247
+ this.siblings.recordExit(frame.depth, frame.subflowId);
248
+ this.answerBuffer.set(frame, ts, runtimeStageId);
249
+ }
250
+ }
251
+ onFork(event) {
252
+ this.observeRunId(event.traversalContext?.runId);
253
+ const ctx = event.traversalContext;
254
+ const runtimeStageId = ctx?.runtimeStageId ?? '';
255
+ const subflowPath = pathFromCtx(ctx?.subflowPath);
256
+ const ts = Date.now();
257
+ const depth = subflowPath.length - 1;
258
+ // ForkTracker handles registration + dedup. Cross-run collisions
259
+ // (Committee + TolerantCommittee back-to-back, both emitting
260
+ // `seed#0` as the parent stage) are prevented by observeRunId
261
+ // above — the tracker's state is wiped when runId changes.
262
+ const reg = this.forks.registerFork(event.parent, runtimeStageId, event.children);
263
+ if (!reg.fresh)
264
+ return;
265
+ // Fork at depth 0 = Parallel root signal.
266
+ this.rootInferrer.observeFork(depth);
267
+ const branches = [...event.children];
268
+ this.push({
269
+ seq: this.entryCount,
270
+ kind: 'fork',
271
+ transitions: branches.map((childName) => ({
272
+ from: ACTOR_USER,
273
+ to: childName,
274
+ via: 'fork-branch',
275
+ label: childName,
276
+ })),
277
+ anchor: { runtimeStageId, subflowPath },
278
+ label: `fork (${branches.length})`,
279
+ tsMs: ts,
280
+ meta: { kind: 'fork', parentSubflowId: event.parent },
281
+ });
282
+ }
283
+ onDecision(event) {
284
+ this.observeRunId(event.traversalContext?.runId);
285
+ const ctx = event.traversalContext;
286
+ // Skip Agent-internal decisions — those are encoded by the actor
287
+ // arrows that follow.
288
+ const stageId = ctx?.stageId ?? '';
289
+ const { localStageId } = splitStageId(stageId);
290
+ if (isAgentInternalStageId(localStageId))
291
+ return;
292
+ const runtimeStageId = ctx?.runtimeStageId ?? '';
293
+ const subflowPath = pathFromCtx(ctx?.subflowPath);
294
+ const ts = Date.now();
295
+ const depth = subflowPath.length - 1;
296
+ this.rootInferrer.observeDecision(depth);
297
+ this.push({
298
+ seq: this.entryCount,
299
+ kind: 'decide',
300
+ transitions: [
301
+ {
302
+ from: runtimeStageId,
303
+ to: event.chosen,
304
+ via: 'decision-branch',
305
+ label: event.chosen,
306
+ },
307
+ ],
308
+ anchor: { runtimeStageId, subflowPath },
309
+ label: `routes to ${event.chosen}`,
310
+ tsMs: ts,
311
+ meta: {
312
+ kind: 'decide',
313
+ chosen: event.chosen,
314
+ ...(event.rationale ? { rationale: event.rationale } : {}),
315
+ },
316
+ });
317
+ }
318
+ onLoop(event) {
319
+ this.observeRunId(event.traversalContext?.runId);
320
+ const ctx = event.traversalContext;
321
+ const runtimeStageId = ctx?.runtimeStageId ?? '';
322
+ const subflowPath = pathFromCtx(ctx?.subflowPath);
323
+ const ts = Date.now();
324
+ const depth = subflowPath.length - 1;
325
+ this.rootInferrer.observeLoop(depth);
326
+ this.push({
327
+ seq: this.entryCount,
328
+ kind: 'iteration',
329
+ transitions: [
330
+ {
331
+ from: event.target,
332
+ to: event.target,
333
+ via: 'loop-iteration',
334
+ label: `iter ${event.iteration}`,
335
+ },
336
+ ],
337
+ anchor: { runtimeStageId, subflowPath },
338
+ label: `iter ${event.iteration}`,
339
+ tsMs: ts,
340
+ meta: { kind: 'iteration', index: event.iteration, target: event.target },
341
+ });
342
+ }
343
+ // ── Typed-event subscription (actor arrows) ──────────────────────
344
+ /**
345
+ * Subscribe to the runner's typed-event dispatcher and emit a
346
+ * `react` RunStep on every `llm.start` / `llm.end`. The recorder
347
+ * classifies `actorArrow` locally (mirrors BoundaryRecorder's
348
+ * pattern) so consumers don't have to depend on BoundaryRecorder's
349
+ * own subscription order.
350
+ */
351
+ subscribe(dispatcher) {
352
+ return dispatcher.on('*', (event) => this.ingestTypedEvent(event));
353
+ }
354
+ /** Internal — also called by `ingestDomainEvent` for shim replay.
355
+ *
356
+ * NOTE: deliberately does NOT call observeRunId(event.meta.runId).
357
+ * The agentfootprint dispatcher's runId is a DIFFERENT generator
358
+ * than footprintjs's traversalContext.runId — mixing them would
359
+ * toggle lastRunId on every event and trigger a false reset.
360
+ * Run-boundary detection happens reliably on the FlowRecorder side
361
+ * (onRunStart fires FIRST in any new run before any typed event). */
362
+ ingestTypedEvent(event) {
363
+ if (event.type === 'agentfootprint.stream.llm_start') {
364
+ const meta = event.meta;
365
+ const runtimeStageId = meta.runtimeStageId ?? '';
366
+ const subflowPath = [ROOT_SUBFLOW_ID, ...(meta.subflowPath ?? [])];
367
+ const ts = meta.wallClockMs;
368
+ const arrow = this.actorArrows.classifyStart();
369
+ const llmStage = `stage:llm:${runtimeStageId}`;
370
+ const from = arrow === 'tool→llm' ? `stage:tool:${runtimeStageId}` : ACTOR_USER;
371
+ this.push({
372
+ seq: this.entryCount,
373
+ kind: 'react',
374
+ transitions: [{ from, to: llmStage, via: 'actor-arrow', label: arrow }],
375
+ anchor: { runtimeStageId, subflowPath },
376
+ label: arrow,
377
+ tsMs: ts,
378
+ meta: { kind: 'react', actorArrow: arrow },
379
+ });
380
+ }
381
+ else if (event.type === 'agentfootprint.stream.llm_end') {
382
+ const p = event.payload;
383
+ const meta = event.meta;
384
+ const runtimeStageId = meta.runtimeStageId ?? '';
385
+ const subflowPath = [ROOT_SUBFLOW_ID, ...(meta.subflowPath ?? [])];
386
+ const ts = meta.wallClockMs;
387
+ const arrow = this.actorArrows.classifyEnd(p.toolCallCount);
388
+ const llmStage = `stage:llm:${runtimeStageId}`;
389
+ const to = arrow === 'llm→user' ? ACTOR_USER : `stage:tool:${runtimeStageId}`;
390
+ this.push({
391
+ seq: this.entryCount,
392
+ kind: 'react',
393
+ transitions: [{ from: llmStage, to, via: 'actor-arrow', label: arrow }],
394
+ anchor: { runtimeStageId, subflowPath },
395
+ label: arrow,
396
+ tsMs: ts,
397
+ meta: { kind: 'react', actorArrow: arrow },
398
+ });
399
+ }
400
+ }
401
+ // ── Replay API (for shim / tests / offline analysis) ─────────────
402
+ /**
403
+ * Feed a single recorded `DomainEvent` (from BoundaryRecorder) into
404
+ * this recorder as if it had fired live. Used by `buildRunSteps`
405
+ * for snapshot replay; tests use it for fixture-driven projection.
406
+ *
407
+ * Live consumers should use `runner.attach(rec)` +
408
+ * `rec.subscribe(dispatcher)` instead — the recorder's hooks fire
409
+ * naturally during traversal.
410
+ */
411
+ ingestDomainEvent(e) {
412
+ const traversalContext = {
413
+ runId: 'replay',
414
+ stageId: lastSegment(e.subflowPath.join('/') || ROOT_SUBFLOW_ID),
415
+ runtimeStageId: e.runtimeStageId,
416
+ stageName: lastSegment(e.subflowPath.join('/') || ROOT_SUBFLOW_ID),
417
+ depth: e.depth,
418
+ ...(e.subflowPath.length > 1 ? { subflowPath: e.subflowPath.slice(1).join('/') } : {}),
419
+ };
420
+ switch (e.type) {
421
+ case 'run.entry':
422
+ this.onRunStart({ payload: e.payload, traversalContext });
423
+ break;
424
+ case 'run.exit':
425
+ this.onRunEnd({ payload: e.payload, traversalContext });
426
+ break;
427
+ case 'subflow.entry':
428
+ this.onSubflowEntry({
429
+ name: e.subflowName,
430
+ subflowId: e.subflowId,
431
+ ...(e.description ? { description: e.description } : {}),
432
+ traversalContext,
433
+ });
434
+ break;
435
+ case 'subflow.exit':
436
+ this.onSubflowExit({
437
+ name: e.subflowName,
438
+ subflowId: e.subflowId,
439
+ traversalContext,
440
+ });
441
+ break;
442
+ case 'fork.branch':
443
+ // Replay layer; coalescing is handled at the call-site
444
+ // (`buildRunSteps`) which groups events by parent+ts and
445
+ // calls `onFork` once. A single fork.branch slipping through
446
+ // to here is treated as a 1-child fork — degenerate but
447
+ // harmless.
448
+ this.onFork({
449
+ parent: e.parentSubflowId,
450
+ children: [e.childName],
451
+ traversalContext,
452
+ });
453
+ break;
454
+ case 'decision.branch':
455
+ this.onDecision({
456
+ decider: e.decider,
457
+ chosen: e.chosen,
458
+ ...(e.rationale ? { rationale: e.rationale } : {}),
459
+ traversalContext,
460
+ });
461
+ break;
462
+ case 'loop.iteration':
463
+ this.onLoop({
464
+ target: e.target,
465
+ iteration: e.iteration,
466
+ traversalContext,
467
+ });
468
+ break;
469
+ case 'llm.start': {
470
+ // Synthesize a typed event matching the dispatcher payload
471
+ // shape so `ingestTypedEvent` produces the same react step.
472
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
473
+ this.ingestTypedEvent({
474
+ type: 'agentfootprint.stream.llm_start',
475
+ payload: {
476
+ provider: e.provider,
477
+ model: e.model,
478
+ ...(e.systemPromptChars !== undefined
479
+ ? { systemPromptChars: e.systemPromptChars }
480
+ : {}),
481
+ ...(e.messagesCount !== undefined ? { messagesCount: e.messagesCount } : {}),
482
+ ...(e.toolsCount !== undefined ? { toolsCount: e.toolsCount } : {}),
483
+ },
484
+ meta: {
485
+ wallClockMs: e.ts,
486
+ runOffsetMs: 0,
487
+ runtimeStageId: e.runtimeStageId,
488
+ // Strip the ROOT_SUBFLOW_ID prefix for the typed-event
489
+ // convention (typed events use the inner path, BoundaryRecorder
490
+ // adds the root prefix on ingest).
491
+ subflowPath: e.subflowPath.slice(1),
492
+ compositionPath: [],
493
+ runId: 'replay',
494
+ },
495
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
496
+ });
497
+ break;
498
+ }
499
+ case 'llm.end': {
500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
501
+ this.ingestTypedEvent({
502
+ type: 'agentfootprint.stream.llm_end',
503
+ payload: {
504
+ content: e.content,
505
+ toolCallCount: e.toolCallCount,
506
+ usage: e.usage,
507
+ ...(e.stopReason ? { stopReason: e.stopReason } : {}),
508
+ },
509
+ meta: {
510
+ wallClockMs: e.ts,
511
+ runOffsetMs: 0,
512
+ runtimeStageId: e.runtimeStageId,
513
+ subflowPath: e.subflowPath.slice(1),
514
+ compositionPath: [],
515
+ runId: 'replay',
516
+ },
517
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
518
+ });
519
+ break;
520
+ }
521
+ // tool.start / tool.end / context.injected — not currently used
522
+ // by the projection (tools are paired with the next llm.start;
523
+ // context injections decorate steps but don't add new ones).
524
+ default:
525
+ break;
526
+ }
527
+ }
528
+ // ── Query API ─────────────────────────────────────────────────
529
+ /**
530
+ * Read-only query — returns the already-built step list filtered to
531
+ * `drillPath` scope. O(1) per call when scope is empty; O(N) filter
532
+ * otherwise. Composition-vs-leaf root filter is applied so the
533
+ * slider semantics match the user's mental model:
534
+ *
535
+ * - **Leaf root** (single Agent / LLMCall): show `react` steps only.
536
+ * - **Composition root** (Sequence / Parallel / Conditional / Loop):
537
+ * show composition steps; hide intra-leaf `react` steps.
538
+ *
539
+ * Drill-down filters by `anchor.subflowPath` prefix and re-applies
540
+ * the leaf-vs-composition rule for the drilled scope.
541
+ */
542
+ getSteps(drillPath) {
543
+ const all = this.store.getAll();
544
+ const path = drillPath ?? [];
545
+ // Leaf-vs-composition filter — delegated to RootInferrer.
546
+ const isLeafRoot = this.rootInferrer.isLeafRoot();
547
+ const kindFiltered = isLeafRoot
548
+ ? all.filter((s) => s.kind === 'react')
549
+ : all.filter((s) => s.kind !== 'react');
550
+ if (path.length === 0) {
551
+ return reseq(kindFiltered);
552
+ }
553
+ return reseq(kindFiltered.filter((s) => isPathPrefix(path, s.anchor.subflowPath)));
554
+ }
555
+ // ── Helpers ───────────────────────────────────────────────────
556
+ /** Flush any deferred answer-candidate from the buffer. Called by
557
+ * `onRunEnd` so a single `answers` step appears for runs that end
558
+ * on a leaf exit (no further leaf entries followed). */
559
+ flushCandidateAnswer() {
560
+ const c = this.answerBuffer.flush();
561
+ if (!c)
562
+ return;
563
+ this.push({
564
+ seq: this.entryCount,
565
+ kind: 'sequential',
566
+ transitions: [{ from: c.frame.subflowId, to: ACTOR_USER, via: 'next', label: 'answers' }],
567
+ anchor: {
568
+ runtimeStageId: c.runtimeStageId,
569
+ subflowPath: c.frame.subflowPath,
570
+ },
571
+ label: 'answers',
572
+ tsMs: c.tsMs,
573
+ });
574
+ }
575
+ }
576
+ // ─── Helpers ─────────────────────────────────────────────────────
577
+ function parsePrimitiveKind(description) {
578
+ if (!description)
579
+ return undefined;
580
+ const colon = description.indexOf(':');
581
+ if (colon < 0)
582
+ return undefined;
583
+ const prefix = description.slice(0, colon).trim();
584
+ return prefix.length > 0 ? prefix : undefined;
585
+ }
586
+ const AGENT_INTERNAL_STAGES = new Set(['route', 'tool-calls', 'final', 'merge']);
587
+ function isAgentInternalStageId(localStageId) {
588
+ return AGENT_INTERNAL_STAGES.has(localStageId);
589
+ }
590
+ /** Renumber `seq` after filtering so consumers see contiguous indices. */
591
+ function reseq(steps) {
592
+ return steps.map((s, i) => (s.seq === i ? s : { ...s, seq: i }));
593
+ }
594
+ /**
595
+ * Compatibility shim for snapshot-from-events use cases (replay,
596
+ * post-hoc analysis, tests). For LIVE use, prefer attaching a
597
+ * `RunStepRecorder` directly via `runner.attach(rec)` —
598
+ * `buildRunSteps(events)` constructs a fresh recorder, replays the
599
+ * events through its handlers, and returns the resulting entries.
600
+ *
601
+ * @deprecated Prefer `runStepRecorder()` + `runner.attach(rec)` for
602
+ * live consumers. This shim remains for offline / testing
603
+ * scenarios where only a recorded event list is available.
604
+ */
605
+ export function buildRunSteps(source, options = {}) {
606
+ const events = Array.isArray(source)
607
+ ? source
608
+ : source.getEvents();
609
+ const rec = new RunStepRecorder();
610
+ // Coalesce fork.branch bursts before replay. BoundaryRecorder emits
611
+ // N fork.branch events per fork (all sharing parent+ts); the
612
+ // recorder expects ONE `onFork` call carrying all children. We pass
613
+ // through the first occurrence of each (parent, ts) pair as a single
614
+ // synthetic event with all children, and drop the rest.
615
+ const forkSeen = new Set();
616
+ for (let i = 0; i < events.length; i++) {
617
+ const e = events[i];
618
+ if (e.type === 'fork.branch') {
619
+ const key = `${e.parentSubflowId}@${e.ts}`;
620
+ if (forkSeen.has(key))
621
+ continue;
622
+ forkSeen.add(key);
623
+ const children = [];
624
+ for (let j = i; j < events.length; j++) {
625
+ const f = events[j];
626
+ if (f.type === 'fork.branch' && f.parentSubflowId === e.parentSubflowId && f.ts === e.ts) {
627
+ children.push(f.childName);
628
+ }
629
+ else if (f.ts > e.ts) {
630
+ break;
631
+ }
632
+ }
633
+ rec.onFork({
634
+ parent: e.parentSubflowId,
635
+ children,
636
+ traversalContext: {
637
+ runId: 'replay',
638
+ stageId: lastSegment(e.subflowPath.join('/') || ROOT_SUBFLOW_ID),
639
+ runtimeStageId: e.runtimeStageId,
640
+ stageName: lastSegment(e.subflowPath.join('/') || ROOT_SUBFLOW_ID),
641
+ depth: e.depth,
642
+ },
643
+ });
644
+ continue;
645
+ }
646
+ rec.ingestDomainEvent(e);
647
+ }
648
+ return [...rec.getSteps(options.drillPath)];
649
+ }
650
+ // Touch unused imports defensively for tree-shaking.
651
+ void ROOT_RUNTIME_STAGE_ID;
652
+ //# sourceMappingURL=RunStepRecorder.js.map