footprintjs 10.0.1 → 10.2.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 (368) hide show
  1. package/AGENTS.md +2 -1
  2. package/CLAUDE.md +61 -7
  3. package/README.md +136 -312
  4. package/dist/esm/index.d.ts +62 -2
  5. package/dist/esm/index.js +13 -6
  6. package/dist/esm/lib/attempt/anySignal.d.ts +19 -0
  7. package/dist/esm/lib/attempt/anySignal.js +39 -0
  8. package/dist/esm/lib/attempt/call.d.ts +88 -0
  9. package/dist/esm/lib/attempt/call.js +129 -0
  10. package/dist/esm/lib/attempt/category.d.ts +37 -0
  11. package/dist/esm/lib/attempt/category.js +131 -0
  12. package/dist/esm/lib/attempt/correlation.d.ts +95 -0
  13. package/dist/esm/lib/attempt/correlation.js +178 -0
  14. package/dist/esm/lib/attempt/deadline.d.ts +44 -0
  15. package/dist/esm/lib/attempt/deadline.js +101 -0
  16. package/dist/esm/lib/attempt/decision.d.ts +52 -0
  17. package/dist/esm/lib/attempt/decision.js +134 -0
  18. package/dist/esm/lib/attempt/effect.d.ts +76 -0
  19. package/dist/esm/lib/attempt/effect.js +160 -0
  20. package/dist/esm/lib/attempt/errors.d.ts +41 -0
  21. package/dist/esm/lib/attempt/errors.js +44 -0
  22. package/dist/esm/lib/attempt/index.d.ts +18 -0
  23. package/dist/esm/lib/attempt/index.js +15 -0
  24. package/dist/esm/lib/attempt/name.d.ts +21 -0
  25. package/dist/esm/lib/attempt/name.js +43 -0
  26. package/dist/esm/lib/attempt/observed.d.ts +66 -0
  27. package/dist/esm/lib/attempt/observed.js +171 -0
  28. package/dist/esm/lib/attempt/policy.d.ts +45 -0
  29. package/dist/esm/lib/attempt/policy.js +83 -0
  30. package/dist/esm/lib/attempt/record.d.ts +11 -0
  31. package/dist/esm/lib/attempt/record.js +24 -0
  32. package/dist/esm/lib/attempt/retryLoopContext.d.ts +18 -0
  33. package/dist/esm/lib/attempt/retryLoopContext.js +24 -0
  34. package/dist/esm/lib/attempt/retryLoopTypes.d.ts +18 -0
  35. package/dist/esm/lib/attempt/retryLoopTypes.js +2 -0
  36. package/dist/esm/lib/attempt/runner.d.ts +157 -0
  37. package/dist/esm/lib/attempt/runner.js +523 -0
  38. package/dist/esm/lib/attempt/standalone.d.ts +47 -0
  39. package/dist/esm/lib/attempt/standalone.js +126 -0
  40. package/dist/esm/lib/attempt/types.d.ts +453 -0
  41. package/dist/esm/lib/attempt/types.js +16 -0
  42. package/dist/esm/lib/budget/index.d.ts +9 -0
  43. package/dist/esm/lib/budget/index.js +9 -0
  44. package/dist/esm/lib/budget/timeBudget.d.ts +136 -0
  45. package/dist/esm/lib/budget/timeBudget.js +666 -0
  46. package/dist/esm/lib/builder/FlowChartBuilder.d.ts +88 -3
  47. package/dist/esm/lib/builder/FlowChartBuilder.js +199 -9
  48. package/dist/esm/lib/builder/retryLoop.d.ts +56 -0
  49. package/dist/esm/lib/builder/retryLoop.js +283 -0
  50. package/dist/esm/lib/builder/types.d.ts +35 -1
  51. package/dist/esm/lib/builder/types.js +1 -1
  52. package/dist/esm/lib/decide/decide.js +2 -2
  53. package/dist/esm/lib/decide/index.d.ts +1 -1
  54. package/dist/esm/lib/decide/index.js +2 -2
  55. package/dist/esm/lib/decide/types.d.ts +3 -0
  56. package/dist/esm/lib/decide/types.js +1 -8
  57. package/dist/esm/lib/engine/graph/StageNode.d.ts +11 -1
  58. package/dist/esm/lib/engine/graph/StageNode.js +1 -1
  59. package/dist/esm/lib/engine/graph/prefixNodeTree.js +5 -1
  60. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +36 -6
  61. package/dist/esm/lib/engine/handlers/DeciderHandler.js +9 -5
  62. package/dist/esm/lib/engine/handlers/ResumeEntry.js +3 -1
  63. package/dist/esm/lib/engine/handlers/SelectorHandler.js +6 -5
  64. package/dist/esm/lib/engine/handlers/StageRunner.d.ts +8 -1
  65. package/dist/esm/lib/engine/handlers/StageRunner.js +36 -9
  66. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +24 -5
  67. package/dist/esm/lib/engine/handlers/commitStage.d.ts +7 -0
  68. package/dist/esm/lib/engine/handlers/commitStage.js +22 -4
  69. package/dist/esm/lib/engine/handlers/stageError.d.ts +9 -0
  70. package/dist/esm/lib/engine/handlers/stageError.js +23 -1
  71. package/dist/esm/lib/engine/index.d.ts +2 -2
  72. package/dist/esm/lib/engine/index.js +1 -1
  73. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +27 -13
  74. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +68 -40
  75. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -1
  76. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +19 -2
  77. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.d.ts +22 -3
  78. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +61 -9
  79. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
  80. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  81. package/dist/esm/lib/engine/narrative/formatting/sentences.d.ts +23 -0
  82. package/dist/esm/lib/engine/narrative/formatting/sentences.js +127 -1
  83. package/dist/esm/lib/engine/narrative/index.d.ts +2 -2
  84. package/dist/esm/lib/engine/narrative/index.js +1 -1
  85. package/dist/esm/lib/engine/narrative/narrativeTypes.d.ts +27 -21
  86. package/dist/esm/lib/engine/narrative/narrativeTypes.js +1 -1
  87. package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.d.ts +45 -2
  88. package/dist/esm/lib/engine/narrative/recorders/ManifestFlowRecorder.js +55 -2
  89. package/dist/esm/lib/engine/narrative/recorders/index.d.ts +1 -1
  90. package/dist/esm/lib/engine/narrative/recorders/index.js +1 -1
  91. package/dist/esm/lib/engine/narrative/types.d.ts +74 -5
  92. package/dist/esm/lib/engine/narrative/types.js +1 -1
  93. package/dist/esm/lib/engine/traversal/FlowchartTraverser.d.ts +59 -19
  94. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +194 -118
  95. package/dist/esm/lib/engine/traversalContext.d.ts +7 -2
  96. package/dist/esm/lib/engine/traversalContext.js +6 -3
  97. package/dist/esm/lib/engine/types.d.ts +52 -57
  98. package/dist/esm/lib/engine/types.js +1 -1
  99. package/dist/esm/lib/errors/terminalFailure.d.ts +29 -0
  100. package/dist/esm/lib/errors/terminalFailure.js +60 -0
  101. package/dist/esm/lib/{decide → filter}/evaluator.d.ts +7 -2
  102. package/dist/esm/lib/filter/evaluator.js +135 -0
  103. package/dist/esm/lib/ids/idempotencyKey.d.ts +14 -0
  104. package/dist/esm/lib/ids/idempotencyKey.js +24 -0
  105. package/dist/esm/lib/memory/StageContext.d.ts +123 -3
  106. package/dist/esm/lib/memory/StageContext.js +245 -4
  107. package/dist/esm/lib/memory/attention.d.ts +26 -0
  108. package/dist/esm/lib/memory/attention.js +171 -0
  109. package/dist/esm/lib/memory/borrowedMutation.d.ts +8 -0
  110. package/dist/esm/lib/memory/borrowedMutation.js +14 -1
  111. package/dist/esm/lib/memory/frameTypes.d.ts +38 -0
  112. package/dist/esm/lib/memory/frameTypes.js +3 -1
  113. package/dist/esm/lib/memory/redaction.d.ts +12 -0
  114. package/dist/esm/lib/memory/redaction.js +54 -5
  115. package/dist/esm/lib/pause/record.js +10 -1
  116. package/dist/esm/lib/pause/types.d.ts +19 -0
  117. package/dist/esm/lib/pause/types.js +17 -1
  118. package/dist/esm/lib/reactive/createTypedScope.js +3 -1
  119. package/dist/esm/lib/reactive/types.d.ts +36 -4
  120. package/dist/esm/lib/reactive/types.js +3 -1
  121. package/dist/esm/lib/recorder/AttemptRecorder.d.ts +141 -0
  122. package/dist/esm/lib/recorder/AttemptRecorder.js +412 -0
  123. package/dist/esm/lib/recorder/ControlDepRecorder.d.ts +36 -2
  124. package/dist/esm/lib/recorder/ControlDepRecorder.js +50 -2
  125. package/dist/esm/lib/recorder/EmitRecorder.d.ts +3 -0
  126. package/dist/esm/lib/recorder/EmitRecorder.js +1 -1
  127. package/dist/esm/lib/recorder/InOutRecorder.d.ts +37 -1
  128. package/dist/esm/lib/recorder/InOutRecorder.js +75 -6
  129. package/dist/esm/lib/recorder/QualityRecorder.d.ts +32 -5
  130. package/dist/esm/lib/recorder/QualityRecorder.js +87 -29
  131. package/dist/esm/lib/recorder/TopologyRecorder.d.ts +35 -5
  132. package/dist/esm/lib/recorder/TopologyRecorder.js +76 -2
  133. package/dist/esm/lib/recorder/attemptMetricRows.d.ts +11 -0
  134. package/dist/esm/lib/recorder/attemptMetricRows.js +202 -0
  135. package/dist/esm/lib/recorder/attemptMetrics.d.ts +7 -0
  136. package/dist/esm/lib/recorder/attemptMetrics.js +165 -0
  137. package/dist/esm/lib/recorder/attemptMetricsTypes.d.ts +114 -0
  138. package/dist/esm/lib/recorder/attemptMetricsTypes.js +2 -0
  139. package/dist/esm/lib/recorder/attemptOTel.d.ts +78 -0
  140. package/dist/esm/lib/recorder/attemptOTel.js +107 -0
  141. package/dist/esm/lib/recorder/attemptSeries.d.ts +81 -0
  142. package/dist/esm/lib/recorder/attemptSeries.js +103 -0
  143. package/dist/esm/lib/recorder/hooks.d.ts +9 -0
  144. package/dist/esm/lib/recorder/hooks.js +4 -1
  145. package/dist/esm/lib/runner/DeferredObserverTier.js +13 -1
  146. package/dist/esm/lib/runner/ExecutionRuntime.d.ts +54 -6
  147. package/dist/esm/lib/runner/ExecutionRuntime.js +57 -5
  148. package/dist/esm/lib/runner/FlowChartExecutor.d.ts +46 -2
  149. package/dist/esm/lib/runner/FlowChartExecutor.js +330 -101
  150. package/dist/esm/lib/runner/RunnableChart.d.ts +6 -0
  151. package/dist/esm/lib/runner/RunnableChart.js +5 -1
  152. package/dist/esm/lib/runner/attach.d.ts +2 -1
  153. package/dist/esm/lib/runner/attach.js +8 -2
  154. package/dist/esm/lib/runner/checkpoint.js +3 -1
  155. package/dist/esm/lib/runner/deadline.d.ts +42 -0
  156. package/dist/esm/lib/runner/deadline.js +90 -0
  157. package/dist/esm/lib/runner/index.d.ts +3 -1
  158. package/dist/esm/lib/runner/index.js +2 -1
  159. package/dist/esm/lib/runner/options.d.ts +17 -0
  160. package/dist/esm/lib/runner/options.js +3 -1
  161. package/dist/esm/lib/runner/recordSink.d.ts +128 -0
  162. package/dist/esm/lib/runner/recordSink.js +296 -0
  163. package/dist/esm/lib/runner/resume.d.ts +3 -0
  164. package/dist/esm/lib/runner/resume.js +22 -2
  165. package/dist/esm/lib/runner/snapshot.d.ts +2 -2
  166. package/dist/esm/lib/runner/snapshot.js +6 -2
  167. package/dist/esm/lib/scope/ScopeFacade.d.ts +118 -4
  168. package/dist/esm/lib/scope/ScopeFacade.js +304 -7
  169. package/dist/esm/lib/scope/recorders/DebugRecorder.d.ts +17 -3
  170. package/dist/esm/lib/scope/recorders/DebugRecorder.js +79 -13
  171. package/dist/esm/lib/scope/recorders/MetricRecorder.d.ts +10 -0
  172. package/dist/esm/lib/scope/recorders/MetricRecorder.js +15 -2
  173. package/dist/esm/lib/scope/runtime.d.ts +3 -0
  174. package/dist/esm/lib/scope/runtime.js +1 -1
  175. package/dist/esm/lib/scope/types.d.ts +6 -0
  176. package/dist/esm/lib/scope/types.js +1 -1
  177. package/dist/esm/lib/sinks/fileSink.d.ts +135 -0
  178. package/dist/esm/lib/sinks/fileSink.js +387 -0
  179. package/dist/esm/recorders.d.ts +8 -2
  180. package/dist/esm/recorders.js +7 -1
  181. package/dist/esm/sinks.d.ts +23 -0
  182. package/dist/esm/sinks.js +23 -0
  183. package/dist/esm/trace.d.ts +2 -2
  184. package/dist/esm/trace.js +1 -1
  185. package/dist/index.js +77 -60
  186. package/dist/lib/attempt/anySignal.js +43 -0
  187. package/dist/lib/attempt/call.js +134 -0
  188. package/dist/lib/attempt/category.js +136 -0
  189. package/dist/lib/attempt/correlation.js +186 -0
  190. package/dist/lib/attempt/deadline.js +112 -0
  191. package/dist/lib/attempt/decision.js +139 -0
  192. package/dist/lib/attempt/effect.js +174 -0
  193. package/dist/lib/attempt/errors.js +49 -0
  194. package/dist/lib/attempt/index.js +26 -0
  195. package/dist/lib/attempt/name.js +48 -0
  196. package/dist/lib/attempt/observed.js +177 -0
  197. package/dist/lib/attempt/policy.js +92 -0
  198. package/dist/lib/attempt/record.js +29 -0
  199. package/dist/lib/attempt/retryLoopContext.js +31 -0
  200. package/dist/lib/attempt/retryLoopTypes.js +3 -0
  201. package/dist/lib/attempt/runner.js +530 -0
  202. package/dist/lib/attempt/standalone.js +130 -0
  203. package/dist/lib/attempt/types.js +17 -0
  204. package/dist/lib/budget/index.js +17 -0
  205. package/dist/lib/budget/timeBudget.js +674 -0
  206. package/dist/lib/builder/FlowChartBuilder.js +199 -9
  207. package/dist/lib/builder/retryLoop.js +287 -0
  208. package/dist/lib/builder/types.js +1 -1
  209. package/dist/lib/decide/decide.js +2 -2
  210. package/dist/lib/decide/index.js +2 -2
  211. package/dist/lib/decide/types.js +1 -8
  212. package/dist/lib/engine/graph/StageNode.js +1 -1
  213. package/dist/lib/engine/graph/prefixNodeTree.js +5 -1
  214. package/dist/lib/engine/handlers/ChildrenExecutor.js +36 -6
  215. package/dist/lib/engine/handlers/DeciderHandler.js +7 -3
  216. package/dist/lib/engine/handlers/ResumeEntry.js +3 -1
  217. package/dist/lib/engine/handlers/SelectorHandler.js +4 -3
  218. package/dist/lib/engine/handlers/StageRunner.js +36 -9
  219. package/dist/lib/engine/handlers/SubflowExecutor.js +24 -5
  220. package/dist/lib/engine/handlers/commitStage.js +24 -5
  221. package/dist/lib/engine/handlers/stageError.js +26 -2
  222. package/dist/lib/engine/index.js +1 -1
  223. package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +67 -39
  224. package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +19 -2
  225. package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +60 -8
  226. package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  227. package/dist/lib/engine/narrative/formatting/sentences.js +131 -2
  228. package/dist/lib/engine/narrative/index.js +1 -1
  229. package/dist/lib/engine/narrative/narrativeTypes.js +1 -1
  230. package/dist/lib/engine/narrative/recorders/ManifestFlowRecorder.js +55 -2
  231. package/dist/lib/engine/narrative/recorders/index.js +1 -1
  232. package/dist/lib/engine/narrative/types.js +1 -1
  233. package/dist/lib/engine/traversal/FlowchartTraverser.js +192 -116
  234. package/dist/lib/engine/traversalContext.js +6 -3
  235. package/dist/lib/engine/types.js +1 -1
  236. package/dist/lib/errors/terminalFailure.js +64 -0
  237. package/dist/lib/filter/evaluator.js +139 -0
  238. package/dist/lib/ids/idempotencyKey.js +28 -0
  239. package/dist/lib/memory/StageContext.js +246 -5
  240. package/dist/lib/memory/attention.js +175 -0
  241. package/dist/lib/memory/borrowedMutation.js +16 -2
  242. package/dist/lib/memory/frameTypes.js +3 -1
  243. package/dist/lib/memory/redaction.js +54 -5
  244. package/dist/lib/pause/record.js +10 -1
  245. package/dist/lib/pause/types.js +17 -1
  246. package/dist/lib/reactive/createTypedScope.js +3 -1
  247. package/dist/lib/reactive/types.js +3 -1
  248. package/dist/lib/recorder/AttemptRecorder.js +417 -0
  249. package/dist/lib/recorder/ControlDepRecorder.js +50 -2
  250. package/dist/lib/recorder/EmitRecorder.js +1 -1
  251. package/dist/lib/recorder/InOutRecorder.js +75 -6
  252. package/dist/lib/recorder/QualityRecorder.js +87 -29
  253. package/dist/lib/recorder/TopologyRecorder.js +76 -2
  254. package/dist/lib/recorder/attemptMetricRows.js +209 -0
  255. package/dist/lib/recorder/attemptMetrics.js +174 -0
  256. package/dist/lib/recorder/attemptMetricsTypes.js +3 -0
  257. package/dist/lib/recorder/attemptOTel.js +111 -0
  258. package/dist/lib/recorder/attemptSeries.js +113 -0
  259. package/dist/lib/recorder/hooks.js +4 -1
  260. package/dist/lib/runner/DeferredObserverTier.js +13 -1
  261. package/dist/lib/runner/ExecutionRuntime.js +57 -5
  262. package/dist/lib/runner/FlowChartExecutor.js +330 -101
  263. package/dist/lib/runner/RunnableChart.js +5 -1
  264. package/dist/lib/runner/attach.js +8 -2
  265. package/dist/lib/runner/checkpoint.js +3 -1
  266. package/dist/lib/runner/deadline.js +94 -0
  267. package/dist/lib/runner/index.js +4 -2
  268. package/dist/lib/runner/options.js +3 -1
  269. package/dist/lib/runner/recordSink.js +302 -0
  270. package/dist/lib/runner/resume.js +22 -2
  271. package/dist/lib/runner/snapshot.js +6 -2
  272. package/dist/lib/scope/ScopeFacade.js +303 -6
  273. package/dist/lib/scope/recorders/DebugRecorder.js +79 -13
  274. package/dist/lib/scope/recorders/MetricRecorder.js +15 -2
  275. package/dist/lib/scope/runtime.js +1 -1
  276. package/dist/lib/scope/types.js +1 -1
  277. package/dist/lib/sinks/fileSink.js +392 -0
  278. package/dist/recorders.js +10 -2
  279. package/dist/sinks.js +32 -0
  280. package/dist/trace.js +1 -1
  281. package/dist/types/index.d.ts +62 -2
  282. package/dist/types/lib/attempt/anySignal.d.ts +19 -0
  283. package/dist/types/lib/attempt/call.d.ts +88 -0
  284. package/dist/types/lib/attempt/category.d.ts +37 -0
  285. package/dist/types/lib/attempt/correlation.d.ts +95 -0
  286. package/dist/types/lib/attempt/deadline.d.ts +44 -0
  287. package/dist/types/lib/attempt/decision.d.ts +52 -0
  288. package/dist/types/lib/attempt/effect.d.ts +76 -0
  289. package/dist/types/lib/attempt/errors.d.ts +41 -0
  290. package/dist/types/lib/attempt/index.d.ts +18 -0
  291. package/dist/types/lib/attempt/name.d.ts +21 -0
  292. package/dist/types/lib/attempt/observed.d.ts +66 -0
  293. package/dist/types/lib/attempt/policy.d.ts +45 -0
  294. package/dist/types/lib/attempt/record.d.ts +11 -0
  295. package/dist/types/lib/attempt/retryLoopContext.d.ts +18 -0
  296. package/dist/types/lib/attempt/retryLoopTypes.d.ts +18 -0
  297. package/dist/types/lib/attempt/runner.d.ts +157 -0
  298. package/dist/types/lib/attempt/standalone.d.ts +47 -0
  299. package/dist/types/lib/attempt/types.d.ts +453 -0
  300. package/dist/types/lib/budget/index.d.ts +9 -0
  301. package/dist/types/lib/budget/timeBudget.d.ts +136 -0
  302. package/dist/types/lib/builder/FlowChartBuilder.d.ts +88 -3
  303. package/dist/types/lib/builder/retryLoop.d.ts +56 -0
  304. package/dist/types/lib/builder/types.d.ts +35 -1
  305. package/dist/types/lib/decide/index.d.ts +1 -1
  306. package/dist/types/lib/decide/types.d.ts +3 -0
  307. package/dist/types/lib/engine/graph/StageNode.d.ts +11 -1
  308. package/dist/types/lib/engine/handlers/StageRunner.d.ts +8 -1
  309. package/dist/types/lib/engine/handlers/commitStage.d.ts +7 -0
  310. package/dist/types/lib/engine/handlers/stageError.d.ts +9 -0
  311. package/dist/types/lib/engine/index.d.ts +2 -2
  312. package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +27 -13
  313. package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -1
  314. package/dist/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +22 -3
  315. package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
  316. package/dist/types/lib/engine/narrative/formatting/sentences.d.ts +23 -0
  317. package/dist/types/lib/engine/narrative/index.d.ts +2 -2
  318. package/dist/types/lib/engine/narrative/narrativeTypes.d.ts +27 -21
  319. package/dist/types/lib/engine/narrative/recorders/ManifestFlowRecorder.d.ts +45 -2
  320. package/dist/types/lib/engine/narrative/recorders/index.d.ts +1 -1
  321. package/dist/types/lib/engine/narrative/types.d.ts +74 -5
  322. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +59 -19
  323. package/dist/types/lib/engine/traversalContext.d.ts +7 -2
  324. package/dist/types/lib/engine/types.d.ts +52 -57
  325. package/dist/types/lib/errors/terminalFailure.d.ts +29 -0
  326. package/dist/types/lib/{decide → filter}/evaluator.d.ts +7 -2
  327. package/dist/types/lib/ids/idempotencyKey.d.ts +14 -0
  328. package/dist/types/lib/memory/StageContext.d.ts +123 -3
  329. package/dist/types/lib/memory/attention.d.ts +26 -0
  330. package/dist/types/lib/memory/borrowedMutation.d.ts +8 -0
  331. package/dist/types/lib/memory/frameTypes.d.ts +38 -0
  332. package/dist/types/lib/memory/redaction.d.ts +12 -0
  333. package/dist/types/lib/pause/types.d.ts +19 -0
  334. package/dist/types/lib/reactive/types.d.ts +36 -4
  335. package/dist/types/lib/recorder/AttemptRecorder.d.ts +141 -0
  336. package/dist/types/lib/recorder/ControlDepRecorder.d.ts +36 -2
  337. package/dist/types/lib/recorder/EmitRecorder.d.ts +3 -0
  338. package/dist/types/lib/recorder/InOutRecorder.d.ts +37 -1
  339. package/dist/types/lib/recorder/QualityRecorder.d.ts +32 -5
  340. package/dist/types/lib/recorder/TopologyRecorder.d.ts +35 -5
  341. package/dist/types/lib/recorder/attemptMetricRows.d.ts +11 -0
  342. package/dist/types/lib/recorder/attemptMetrics.d.ts +7 -0
  343. package/dist/types/lib/recorder/attemptMetricsTypes.d.ts +114 -0
  344. package/dist/types/lib/recorder/attemptOTel.d.ts +78 -0
  345. package/dist/types/lib/recorder/attemptSeries.d.ts +81 -0
  346. package/dist/types/lib/recorder/hooks.d.ts +9 -0
  347. package/dist/types/lib/runner/ExecutionRuntime.d.ts +54 -6
  348. package/dist/types/lib/runner/FlowChartExecutor.d.ts +46 -2
  349. package/dist/types/lib/runner/RunnableChart.d.ts +6 -0
  350. package/dist/types/lib/runner/attach.d.ts +2 -1
  351. package/dist/types/lib/runner/deadline.d.ts +42 -0
  352. package/dist/types/lib/runner/index.d.ts +3 -1
  353. package/dist/types/lib/runner/options.d.ts +17 -0
  354. package/dist/types/lib/runner/recordSink.d.ts +128 -0
  355. package/dist/types/lib/runner/resume.d.ts +3 -0
  356. package/dist/types/lib/runner/snapshot.d.ts +2 -2
  357. package/dist/types/lib/scope/ScopeFacade.d.ts +118 -4
  358. package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +17 -3
  359. package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +10 -0
  360. package/dist/types/lib/scope/runtime.d.ts +3 -0
  361. package/dist/types/lib/scope/types.d.ts +6 -0
  362. package/dist/types/lib/sinks/fileSink.d.ts +135 -0
  363. package/dist/types/recorders.d.ts +8 -2
  364. package/dist/types/sinks.d.ts +23 -0
  365. package/dist/types/trace.d.ts +2 -2
  366. package/package.json +20 -6
  367. package/dist/esm/lib/decide/evaluator.js +0 -130
  368. package/dist/lib/decide/evaluator.js +0 -134
@@ -13,6 +13,9 @@
13
13
  * }
14
14
  * ```
15
15
  */
16
+ import type { AttemptContext, AttemptError, CallOptions, RetryPolicy, RunCorrelation } from '../attempt/types.js';
17
+ import type { FlowChart } from '../builder/types.js';
18
+ import type { DetachDriver, DetachHandle } from '../detach/types.js';
16
19
  import type { ExecutionEnv } from '../engine/types.js';
17
20
  import { StageContext } from '../memory/StageContext.js';
18
21
  import type { CommitEvent, RedactionPolicy, RedactionReport, ScopeRecorder } from './types.js';
@@ -63,6 +66,28 @@ export declare class ScopeFacade {
63
66
  * tree and this stays unset; see {@link rule}.
64
67
  */
65
68
  private _localRule?;
69
+ /**
70
+ * The run's correlation (10.1), installed by the executor's composed scope factory
71
+ * (`RunObservers · composeScopeFactory`) — stamped LAST on every scope and emit event this facade fires,
72
+ * only when present, so a run without one fires byte-identical events.
73
+ */
74
+ private _correlation?;
75
+ /**
76
+ * The attempt this facade was built for (P1b) — the runner's, read off the
77
+ * context at construction. A facade is ONE attempt's handle: once that
78
+ * attempt ends without an answer (its cap, the deadline, a cancellation, a
79
+ * throw), the function behind it may still be running, and anything it
80
+ * touches through this facade is refused ({@link refuseIfAttemptEnded}) —
81
+ * a write or an emit is also recorded on the stage. Absent for a stage the
82
+ * runner does not run: nothing is fenced, as before.
83
+ */
84
+ private readonly _attempt?;
85
+ /** The run's signal, for `$attempt()` of a stage the runner does not run. */
86
+ private readonly _runSignal?;
87
+ /** `$call`s with `of > 1` in flight through this facade — a keyless touching call inside one is refused (P3). */
88
+ private _retryingCalls;
89
+ /** This attempt's observed reports, grouped into series (P2) — made on the first report. */
90
+ private _observedBook?;
66
91
  constructor(context: StageContext, stageName: string, readOnlyValues?: unknown, executionEnv?: ExecutionEnv);
67
92
  /**
68
93
  * The redaction rule this facade decides with — the SAME object its
@@ -93,6 +118,12 @@ export declare class ScopeFacade {
93
118
  * @internal
94
119
  */
95
120
  useRedactionPolicy(policy: RedactionPolicy): void;
121
+ /**
122
+ * Install the run's correlation — every scope and emit event this facade fires carries it; `undefined` clears
123
+ * it. Engine-only (the composed scope factory calls it once per stage), like `useRedactionPolicy`.
124
+ * @internal
125
+ */
126
+ useCorrelation(correlation: RunCorrelation | undefined): void;
96
127
  /** @internal */
97
128
  getRedactionPolicy(): RedactionPolicy | undefined;
98
129
  /**
@@ -119,6 +150,85 @@ export declare class ScopeFacade {
119
150
  * to the stage that made the write.
120
151
  */
121
152
  private assertLive;
153
+ /**
154
+ * The attempt fence (P1b): this facade's attempt has ended, so whatever the
155
+ * abandoned function does now must not reach the stage — not its buffer, its
156
+ * reads, its untracked sources or a recorder. Refused; a write or an emit is
157
+ * recorded on the stage first (`StageSnapshot.lateWrites`, the name only),
158
+ * a read (`channel` undefined) is not.
159
+ */
160
+ private refuseIfAttemptEnded;
161
+ /**
162
+ * The attempt this stage function runs in — `scope.$attempt()` (design
163
+ * §3.1). Its `signal` aborts when the attempt must stop (its cap, the run's
164
+ * deadline, a cancellation): pass it to `fetch` and SDK calls. Its
165
+ * `timeLeftMs()` reads the run's deadline — time is an input the record
166
+ * cannot replay, so with a deadline the read marks the stage's commit
167
+ * `untrackedSources: ['env']`, as `getEnv()` does.
168
+ *
169
+ * A stage the attempt runner does not run (no retry policy, no `timeoutMs`,
170
+ * no deadline) is attempt 1 of 1, with the run's signal and `Infinity` left.
171
+ *
172
+ * ```typescript
173
+ * const t = scope.$attempt();
174
+ * const res = await fetch(url, { signal: t.signal });
175
+ * if (t.timeLeftMs() < 1_000) scope.summary = 'partial';
176
+ * ```
177
+ */
178
+ getAttempt(): AttemptContext;
179
+ /**
180
+ * A call to an outside system, made inside this stage — `scope.$call(target, fn, policy?)` (design §3.6, P2).
181
+ * `fn` runs on the attempt runner under `policy` (none = ONE attempt, still recorded: a single call is evidence
182
+ * too), and every attempt is an `AttemptRecord` of kind `'call'` on this stage's execution-tree node and on
183
+ * `onAttempt` — its timing, outcome, failure category and the retry-or-stop decision, its error served under the
184
+ * run's redaction rule.
185
+ *
186
+ * The call lives within this stage attempt: it is capped by the time the attempt has left (its cap, the run's
187
+ * deadline) and `fn`'s `signal` aborts when the attempt does; once the attempt is over nothing of the call lands
188
+ * and no further attempt of it starts (`attempt/call.ts`). `target` is a NAME from code — never a value.
189
+ *
190
+ * ```typescript
191
+ * const quote = await scope.$call('stripe.quotes.create', (t) => stripe.quotes.create(body, { signal: t.signal }),
192
+ * { attempts: 3, timeoutMs: 10_000, backoffMs: (n) => 500 * n });
193
+ * ```
194
+ *
195
+ * A call that CHANGES an outside system declares it (P3): `options.touches` names the system and
196
+ * `options.idempotencyKey` the key every attempt is made under (`t.idempotencyKey`) — a value from the stage's
197
+ * input or state, never from model output. A touching call that may retry without a key is refused here,
198
+ * before anything is sent; each record says what its attempt did (`effect`), and a call that ends without an answer after any attempt left the
199
+ * effect unknown rejects with `UnknownEffectError` — reconcile by the key, do not retry blindly.
200
+ */
201
+ call<T>(target: string, fn: (attempt: AttemptContext) => Promise<T> | T, policy?: RetryPolicy, options?: CallOptions): Promise<T>;
202
+ /** `runCall` with this stage attempt as its host (`call`). */
203
+ private startCall;
204
+ /** @internal The explicit retry helper owns this state; the normal write path still owns retention/commit. */
205
+ writeRetryLoopState(key: string, state: {
206
+ readonly idempotencyKey: string;
207
+ }): void;
208
+ /** @internal Exactly the existing P3 key-retention law; operational state keeps the real key. */
209
+ serveRetryLoopKey(key: string): string;
210
+ /** @internal Loop state is durable data: never persist the thrown value, stack, cause or payload. */
211
+ serveRetryLoopError(error: unknown): AttemptError;
212
+ /** A touching call's target, its key served for the records (`RedactionRule · retainIdempotencyKey`: by name and by its sources). */
213
+ private callEffect;
214
+ /**
215
+ * A `$call` from an ended attempt, or from a frame that has committed: recorded by name (`lateWrites`, channel
216
+ * `'attempt'`) and refused — before anything reaches outside. The P1b fence first, so its message and record are
217
+ * the same.
218
+ */
219
+ private refuseLateCall;
220
+ /**
221
+ * An outside attempt an adapter reported — `AttemptContext.observed` on this stage's attempt or on one of its
222
+ * calls' (P2). Read and validated first (a malformed report is the adapter's bug: a `TypeError`, and it never
223
+ * reaches the record). A report that comes after its attempt ended — the call attempt it was made through, this
224
+ * stage attempt, or the frame's commit — is recorded by name (`lateWrites`, channel `'attempt'`) and DROPPED,
225
+ * never thrown: adapters report from an SDK's own callbacks, which may fire after the stage moved on, and an
226
+ * observed attempt never changes control flow — in either direction. Its series opens at this stage attempt's call
227
+ * level: an SDK's call is a call the stage attempt made, so a report made through a `$call` is that call's sibling.
228
+ */
229
+ private recordObserved;
230
+ /** This facade's attempt has ended — the P1b fence is closed. */
231
+ private attemptHasEnded;
122
232
  /** Called by StageContext.commit() observer. Converts tracked writes to CommitEvent format.
123
233
  * Errors are caught to prevent recorder issues from aborting the traversal. */
124
234
  private _onCommitFired;
@@ -147,9 +257,9 @@ export declare class ScopeFacade {
147
257
  */
148
258
  emitEvent(name: string, payload: unknown): void;
149
259
  /** See `ScopeMethods.$detachAndJoinLater`. */
150
- detachAndJoinLater(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): import('../detach/types.js').DetachHandle;
260
+ detachAndJoinLater(driver: DetachDriver, child: FlowChart, input?: unknown): DetachHandle;
151
261
  /** See `ScopeMethods.$detachAndForget`. */
152
- detachAndForget(driver: import('../detach/types.js').DetachDriver, child: import('../builder/types.js').FlowChart, input?: unknown): void;
262
+ detachAndForget(driver: DetachDriver, child: FlowChart, input?: unknown): void;
153
263
  /**
154
264
  * Build the subflowPath (outer → inner) for event enrichment.
155
265
  *
@@ -251,8 +361,10 @@ export declare class ScopeFacade {
251
361
  * Returns the execution environment — read-only infrastructure values
252
362
  * that propagate through nested executors (like `process.env` for flowcharts).
253
363
  *
254
- * Contains: signal (abort), timeoutMs, traceId.
255
- * Frozen at construction time. Inherited by subflows automatically.
364
+ * Contains: signal (abort — the run's one effective signal), timeoutMs, traceId, operationId, traceparent,
365
+ * deadline (epoch ms) — the run's env as admitted (`attempt/correlation.ts · admitRunEnv`: one own-field
366
+ * snapshot; `traceparent` is the canonical header the run's correlation carries, never the received bytes), then
367
+ * armed (`runner/deadline.ts · armRun`). Frozen at construction time. Inherited by subflows automatically.
256
368
  *
257
369
  * ```typescript
258
370
  * const { signal, traceId } = scope.getEnv();
@@ -266,6 +378,8 @@ export declare class ScopeFacade {
266
378
  /** @internal */
267
379
  getPipelineId(): string;
268
380
  private _invokeHook;
381
+ /** The event as fired: unchanged without a correlation, else with the run's correlation as its last key. */
382
+ private _correlated;
269
383
  /**
270
384
  * The scope channel's isolation: a recorder's throw becomes an `onError` on every recorder
271
385
  * (stamped with this stage), and a throw from `onError` itself is dropped.
@@ -1,14 +1,27 @@
1
1
  /**
2
2
  * DebugRecorder — Development-focused recorder for detailed debugging
3
3
  *
4
- * Captures errors (always), mutations and reads (in verbose mode),
5
- * and stage lifecycle events for troubleshooting.
4
+ * Captures errors (always — a scope recorder's own failure, the scope
5
+ * channel's `onError`; a stage's failure is not one), mutations and reads
6
+ * (in verbose mode), and stage lifecycle events for troubleshooting.
7
+ *
8
+ * Attempts (10.1): one `attempt` row per attempt of a series that retried,
9
+ * gave up or timed out (`recorder/attemptSeries.ts · isRetryEvidence`) — its
10
+ * outcome, category, duration, decision, the cap and time left it ran under
11
+ * (present with a cap or a deadline) and served error — and one `attemptStop`
12
+ * row when the series gave up (exhausted, declined, no time left). Both verbosities: like a
13
+ * pause, an attempt is significant — and for a retried stage these rows are
14
+ * the failure evidence (a stage's error is never an `error` row). They arrive on the FLOW channel
15
+ * (`onAttempt`), so attach with `attachCombinedRecorder` to see them; the
16
+ * flow channel's own `onError` / `onPause` / `onResume` are ignored, so a
17
+ * combined attach never doubles a scope row.
6
18
  */
19
+ import type { FlowAttemptEvent } from '../../engine/narrative/types.js';
7
20
  import type { RecorderOperation } from '../../recorder/RecorderOperation.js';
8
21
  import type { ErrorEvent, PauseEvent, ReadEvent, ResumeEvent, ScopeRecorder, StageEvent, WriteEvent } from '../types.js';
9
22
  export type DebugVerbosity = 'minimal' | 'verbose';
10
23
  export interface DebugEntry {
11
- type: 'read' | 'write' | 'error' | 'stageStart' | 'stageEnd' | 'pause' | 'resume';
24
+ type: 'read' | 'write' | 'error' | 'stageStart' | 'stageEnd' | 'pause' | 'resume' | 'attempt' | 'attemptStop';
12
25
  stageName: string;
13
26
  timestamp: number;
14
27
  data: unknown;
@@ -48,6 +61,7 @@ export declare class DebugRecorder implements ScopeRecorder {
48
61
  onStageEnd(event: StageEvent): void;
49
62
  onPause(event: PauseEvent): void;
50
63
  onResume(event: ResumeEvent): void;
64
+ onAttempt(event: FlowAttemptEvent): void;
51
65
  getEntries(): DebugEntry[];
52
66
  getErrors(): DebugEntry[];
53
67
  getEntriesForStage(stageName: string): DebugEntry[];
@@ -6,6 +6,14 @@
6
6
  * (Convention 1 — one purpose per recorder: this is the ScopeRecorder; the store
7
7
  * is the storage).
8
8
  *
9
+ * Attempts (10.1): a stage with a retry policy runs each attempt in a fresh
10
+ * scope, so this recorder sees one `onStageStart` per attempt. `attempts` is
11
+ * that count, present when it is 2 or more; `duration` spans the whole
12
+ * execution — from the FIRST attempt's start, waits included — never just the
13
+ * attempt that succeeded. An execution that never returned (it gave up) has
14
+ * no `onStageEnd`, so its duration stays `0`, as for any failed stage. A stage
15
+ * that ran once reads exactly as before.
16
+ *
9
17
  * @example
10
18
  * ```typescript
11
19
  * const metric = new MetricRecorder();
@@ -38,6 +46,8 @@ export interface StepMetrics {
38
46
  pauseCount: number;
39
47
  /** Duration in ms for this invocation. */
40
48
  duration: number;
49
+ /** How many attempts the execution made (its `onStageStart` count) — present only when it retried (≥ 2). */
50
+ attempts?: number;
41
51
  }
42
52
  /** Aggregated metrics across all invocations (backward compatible). */
43
53
  export interface AggregatedMetrics {
@@ -7,6 +7,7 @@
7
7
  * ScopeFacade owns recording, redaction and state access; custom scopes may
8
8
  * explicitly supply a smaller port (including `{}` for data-only scopes).
9
9
  */
10
+ import type { RunCorrelation } from '../attempt/types.js';
10
11
  import type { RedactionPolicy, ScopeRecorder } from './types.js';
11
12
  /** Engine-facing capabilities. Never use a strict user-field proxy as this port. */
12
13
  export interface ScopeRuntimeTarget {
@@ -15,6 +16,8 @@ export interface ScopeRuntimeTarget {
15
16
  getScopeRecorders?(): ScopeRecorder[];
16
17
  useSharedRedactedKeys?(keys: Set<string>): void;
17
18
  useRedactionPolicy?(policy: RedactionPolicy): void;
19
+ /** The run's correlation, stamped on every scope and emit event this scope fires (10.1). */
20
+ useCorrelation?(correlation: RunCorrelation | undefined): void;
18
21
  getRedactedKeys?(): Set<string>;
19
22
  notifyStageStart?(): void;
20
23
  notifyStageEnd?(duration?: number): void;
@@ -5,6 +5,7 @@
5
5
  * Architecture follows composition-over-inheritance: Recorders are
6
6
  * attached to Scope instances to observe read/write/commit operations.
7
7
  */
8
+ import type { RunCorrelation } from '../attempt/types.js';
8
9
  import type { StructuredErrorInfo } from '../errors/errorInfo.js';
9
10
  export interface RecorderContext {
10
11
  stageName: string;
@@ -14,6 +15,11 @@ export interface RecorderContext {
14
15
  runtimeStageId: string;
15
16
  pipelineId: string;
16
17
  timestamp: number;
18
+ /**
19
+ * The run's correlation (10.1) — the same frozen object as `TraversalContext.correlation` on the flow events
20
+ * and `getSnapshot().correlation`. Absent — no key — when the run's env gave none.
21
+ */
22
+ readonly correlation?: RunCorrelation;
17
23
  }
18
24
  export interface ReadEvent extends RecorderContext {
19
25
  key?: string;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * sinks/fileSink.ts — the record, one JSON line per entry, in a file (design §3.5, §4, packet P6). NODE ONLY: the
3
+ * one file in `src/` that imports a Node built-in, reachable only through the `footprintjs/sinks` door.
4
+ *
5
+ * WHY A FILE, WHY THIS SHAPE. A run's record must outlive the process that made it — a killed process, a crashed
6
+ * host, a serverless freeze. Each entry the log hands over is encoded, checked and given its own offset in the file,
7
+ * and its write is handed to the I/O pool AT ONCE (a positional write — no entry waits for the event loop to turn);
8
+ * the pool's threads perform them while the main thread runs on. A process killed after a pool thread wrote an entry
9
+ * keeps it. Until then it is not on disk: microseconds on a free pool, but the pool (4 threads by default) is shared
10
+ * with DNS lookups, zlib, crypto and other fs work, so while it is busy the window is as long as that work — and since
11
+ * writes complete out of order, a kill can lose an entry in the middle (the reader reports the hole). `flush()` waits for every write and
12
+ * `fsync`s — the step that also survives the machine losing power, and the one the executor awaits after each run's
13
+ * closing entry.
14
+ *
15
+ * THE THREAT MODEL, as code — every resource bounded, nothing that can stall the run:
16
+ * - ONLY THE PATH THE APP GIVES. Resolved once and opened once, at `fileSink(path)`; every entry goes to that open
17
+ * file. A symbolic link at the path is refused (`O_NOFOLLOW`, where the platform has it), and so is anything
18
+ * that is not a regular file, a file with another hard link, another owner, or group/world write. A file the
19
+ * sink creates is `0o600`.
20
+ * - ATOMIC PER ENTRY. A line is encoded in full first — an entry JSON cannot write (a `BigInt`, a cycle) is
21
+ * refused whole — then written at its own offset. A write that fails part-way leaves a hole up to the next
22
+ * entry's offset; the reader splits at it (NUL), so a torn line never swallows the one after it (an existing
23
+ * file with a torn tail gets a newline first).
24
+ * - BOUNDED: `maxEntryBytes` (one line, 8 MiB — refused by name, before it is encoded when its length already
25
+ * says so), `maxQueuedBytes` (accepted, not yet written, 16 MiB — past it an entry is DROPPED and counted, never
26
+ * buffered without limit), `maxBytes` (the file, 64 MiB — once full, every further entry is refused and counted;
27
+ * a closing entry may use a 64 KiB reserve so a full file can still say how the run ended), `flushTimeoutMs`
28
+ * (10 s — then `flush()` rejects). Every refusal throws from the sink method, which the log counts
29
+ * (`getSnapshot().sink`); a write that fails later rejects that entry's promise, counted the same way.
30
+ * - ASYNCHRONOUS WRITES ONLY. No method blocks on the disk; the file is released asynchronously, after every
31
+ * write and every started fsync.
32
+ * - NO SECRET ON DISK is the EXECUTOR's law, applied before any entry reaches an adapter (`runner/recordSink.ts`):
33
+ * this file writes what it is handed and reads nothing else.
34
+ * - ONE WRITER PER FILE. A second sink on a file this process has open is refused (by device and inode, across the
35
+ * CommonJS and ESM builds). ANOTHER PROCESS writing the same path is not seen at open: each writes at its own
36
+ * offsets and would overwrite the other. `flush()` checks the file's size against what this sink placed and rejects
37
+ * (a loss the run counts) when another writer's bytes reach past it — best effort: an overwrite this sink's own
38
+ * later writes cover is not seen. Give each process its own file.
39
+ *
40
+ * WHAT JSON KEEPS. A line holds what `JSON.stringify` keeps: a `Date` becomes its ISO string, a `Map` / `Set` an
41
+ * empty object, `undefined` disappears. A record of plain JSON values reads back exactly (`readRecordFile` →
42
+ * `readStoredRecord`); for richer values, bring an adapter with a richer encoding.
43
+ */
44
+ import type { RecordSink } from 'foottrace/write';
45
+ /** The default cap on a record file: 64 MiB. */
46
+ export declare const DEFAULT_FILE_SINK_MAX_BYTES: number;
47
+ /** The default cap on ONE entry's line: 8 MiB. */
48
+ export declare const DEFAULT_FILE_SINK_MAX_ENTRY_BYTES: number;
49
+ /** The default cap on bytes accepted but not yet written: 16 MiB. */
50
+ export declare const DEFAULT_FILE_SINK_MAX_QUEUED_BYTES: number;
51
+ /** The default bound on `flush()`: 10 s. */
52
+ export declare const DEFAULT_FILE_SINK_FLUSH_TIMEOUT_MS = 10000;
53
+ /** `fileSink`'s options — every resource the sink can spend, bounded. */
54
+ export interface FileSinkOptions {
55
+ /**
56
+ * The most bytes the file may hold (the bytes already in it count). Once an entry would pass it, the file is FULL:
57
+ * that entry and every later one is refused and counted by the run, never written — except a closing entry, which
58
+ * may use 64 KiB more so a full file can still say how the run ended. Default 64 MiB.
59
+ */
60
+ readonly maxBytes?: number;
61
+ /** The largest ONE entry's line may be; a larger entry is refused whole (`maxEntryBytes` named). Default 8 MiB. */
62
+ readonly maxEntryBytes?: number;
63
+ /**
64
+ * The most bytes accepted and not yet on disk. Writes are asynchronous, so a sink slower than the run queues; past
65
+ * this bound an entry is DROPPED and counted (never buffered without limit, never a stall). Default 16 MiB.
66
+ */
67
+ readonly maxQueuedBytes?: number;
68
+ /** How long `flush()` (drain the queue, then `fsync`) may take before it rejects. Default 10 s. */
69
+ readonly flushTimeoutMs?: number;
70
+ }
71
+ /** A record sink that appends to one file — `RecordSink` plus what the app needs to manage the file. */
72
+ export interface FileSink extends RecordSink {
73
+ /** The absolute path the sink writes to — resolved once, when it was made. */
74
+ readonly path: string;
75
+ /**
76
+ * Bytes this sink knows are on disk: the file's size when it opened plus every byte its writes completed — a
77
+ * running sum, not the file's end (writes complete out of order; a failed one leaves a hole).
78
+ */
79
+ readonly bytes: number;
80
+ /** Bytes accepted and not yet written. */
81
+ readonly queuedBytes: number;
82
+ /** `true` once an entry was refused for `maxBytes`: the file is full, and only a closing entry may still go. */
83
+ readonly full: boolean;
84
+ /**
85
+ * Release the file. Idempotent. Every entry after it is refused (the run counts it), and `flush()` rejects;
86
+ * entries already accepted are still written (and a flush already started finishes), then the file is closed —
87
+ * the promise settles then.
88
+ */
89
+ close(): Promise<void>;
90
+ }
91
+ /**
92
+ * A record sink that appends each entry to `path` as one JSON line — the crash-safe record on a Node host.
93
+ *
94
+ * Every method returns at once: the line is encoded, checked against every bound, given its offset and handed to
95
+ * the I/O pool. The method's promise settles when that line is written; a write that fails rejects it, and the run
96
+ * counts the loss.
97
+ *
98
+ * @throws TypeError when `path` is not a non-empty string or does not name a regular file the writer owns alone;
99
+ * RangeError for a bound that is not a whole number ≥ 1; the operating system's error when
100
+ * the file cannot be opened (a symbolic link at `path` included). Made once, at setup — never during a run.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * import { fileSink, readRecordFile } from 'footprintjs/sinks';
105
+ * import { readStoredRecord } from 'foottrace';
106
+ *
107
+ * const sink = fileSink('/var/footprint/orders.jsonl');
108
+ * const executor = new FlowChartExecutor(chart, { sink });
109
+ * await executor.run({ input });
110
+ * // …later, or after a crash:
111
+ * const record = readStoredRecord(readRecordFile('/var/footprint/orders.jsonl').entries);
112
+ * record.root(runId)?.complete; // false for a run that never ended
113
+ * ```
114
+ */
115
+ export declare function fileSink(path: string, options?: FileSinkOptions): FileSink;
116
+ /** A record file read back: every parsed line, and every line that could not be. */
117
+ export interface RecordFile {
118
+ /** The parsed entries, in file order — untrusted input for `readStoredRecord` (`foottrace`). */
119
+ readonly entries: readonly unknown[];
120
+ /** Each line that is not JSON (a torn last line, a corrupt one), by its 1-based number. Blank lines are skipped. */
121
+ readonly unreadable: readonly {
122
+ readonly line: number;
123
+ readonly reason: string;
124
+ }[];
125
+ }
126
+ /**
127
+ * Read a record file back, line by line — the entries for `readStoredRecord`, and every line that could not be
128
+ * read, by number, never a failed read. A stored key named `__proto__` stays data (`JSON.parse` makes it an own
129
+ * property). The file is untrusted: a file larger than `maxBytes` (default 256 MiB) is refused before it is read.
130
+ *
131
+ * @throws RangeError when the file is larger than `maxBytes`; the operating system's error when it cannot be read.
132
+ */
133
+ export declare function readRecordFile(path: string, options?: {
134
+ readonly maxBytes?: number;
135
+ }): RecordFile;
@@ -25,7 +25,7 @@
25
25
  */
26
26
  import type { CombinedNarrativeRecorderOptions } from './lib/engine/narrative/CombinedNarrativeRecorder.js';
27
27
  import { CombinedNarrativeRecorder } from './lib/engine/narrative/CombinedNarrativeRecorder.js';
28
- import type { BreakRenderContext, DecisionRenderContext, EmitRenderContext, ErrorRenderContext, ForkRenderContext, LoopRenderContext, NarrativeFormatter, OpRenderContext, SelectedRenderContext, StageRenderContext, SubflowRenderContext } from './lib/engine/narrative/narrativeTypes.js';
28
+ import type { AttemptRenderContext, BreakRenderContext, DecisionRenderContext, EmitRenderContext, ErrorRenderContext, ForkRenderContext, LoopRenderContext, NarrativeFormatter, OpRenderContext, SelectedRenderContext, StageRenderContext, SubflowRenderContext } from './lib/engine/narrative/narrativeTypes.js';
29
29
  import { AdaptiveNarrativeFlowRecorder } from './lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.js';
30
30
  import type { ManifestEntry } from './lib/engine/narrative/recorders/ManifestFlowRecorder.js';
31
31
  import { ManifestFlowRecorder } from './lib/engine/narrative/recorders/ManifestFlowRecorder.js';
@@ -66,7 +66,13 @@ export declare function manifest(): ManifestInstance;
66
66
  export declare function adaptive(): AdaptiveNarrativeFlowRecorder;
67
67
  export declare function milestone(): MilestoneNarrativeFlowRecorder;
68
68
  export declare function windowed(maxEntries?: number): WindowedNarrativeFlowRecorder;
69
- export type { BreakRenderContext, CombinedNarrativeRecorderOptions, DecisionRenderContext, EmitEvent, EmitRecorder, EmitRenderContext, ErrorRenderContext, ForkRenderContext, LoopRenderContext, NarrativeFormatter, OpRenderContext, SelectedRenderContext, StageRenderContext, SubflowRenderContext, };
69
+ export type { AttemptRenderContext, BreakRenderContext, CombinedNarrativeRecorderOptions, DecisionRenderContext, EmitEvent, EmitRecorder, EmitRenderContext, ErrorRenderContext, ForkRenderContext, LoopRenderContext, NarrativeFormatter, OpRenderContext, SelectedRenderContext, StageRenderContext, SubflowRenderContext, };
70
70
  export type { AggregatedMetrics, MetricRecorderOptions, StageMetrics };
71
+ export type { AttemptAggregate, AttemptDecisionRow, AttemptMetricRef, AttemptQuery, AttemptRecorderOptions, AttemptRow, AttemptRunRow, AttemptRunStats, AttemptStats, AttemptStorageStats, AttemptStore, PhysicalAttemptRow, } from './lib/recorder/attemptMetricsTypes.js';
72
+ export { memoryStore } from './lib/recorder/AttemptRecorder.js';
71
73
  export type { CompositeSnapshot } from './lib/recorder/index.js';
72
74
  export { CompositeRecorder } from './lib/recorder/index.js';
75
+ import type { AttemptRecorderOptions } from './lib/recorder/attemptMetricsTypes.js';
76
+ import { AttemptRecorder } from './lib/recorder/AttemptRecorder.js';
77
+ /** Collect payload-free attempts during traversal; storage adapters are optional and provider-neutral. */
78
+ export declare function attempts(options?: AttemptRecorderOptions): AttemptRecorder;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * footprintjs/sinks — where a run's crash-safe record goes on a Node host (10.1, design §3.5).
3
+ *
4
+ * NODE ONLY. This door imports `node:fs`; it is the one door that does (`test/architecture/layering.test.ts`), so
5
+ * every other door stays loadable in a browser. The port itself — `RecordSink`, and the reference `memorySink()` —
6
+ * is foottrace's (`foottrace/write`); the reader is `readStoredRecord` (`foottrace`).
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { flowChart, FlowChartExecutor } from 'footprintjs';
11
+ * import { fileSink, readRecordFile } from 'footprintjs/sinks';
12
+ * import { readStoredRecord } from 'foottrace';
13
+ *
14
+ * const executor = new FlowChartExecutor(chart, { sink: fileSink('/var/footprint/orders.jsonl') });
15
+ * await executor.run({ input });
16
+ *
17
+ * // After a crash — in another process:
18
+ * const record = readStoredRecord(readRecordFile('/var/footprint/orders.jsonl').entries);
19
+ * for (const log of record.logs.values()) if (!log.complete) console.log('never finished:', log.address);
20
+ * ```
21
+ */
22
+ export type { FileSink, FileSinkOptions, RecordFile } from './lib/sinks/fileSink.js';
23
+ export { DEFAULT_FILE_SINK_FLUSH_TIMEOUT_MS, DEFAULT_FILE_SINK_MAX_BYTES, DEFAULT_FILE_SINK_MAX_ENTRY_BYTES, DEFAULT_FILE_SINK_MAX_QUEUED_BYTES, fileSink, readRecordFile, } from './lib/sinks/fileSink.js';
@@ -11,7 +11,7 @@ export { BRANCH_SEGMENT_MARKER, buildBranchSegment, hasBranchSegmentMarker, isBr
11
11
  export { BoundaryStateStore } from './lib/recorder/BoundaryStateStore.js';
12
12
  export type { ControlDecisionRecord, ControlDepRecorderOptions } from './lib/recorder/ControlDepRecorder.js';
13
13
  export { ControlDepRecorder, controlDepRecorder } from './lib/recorder/ControlDepRecorder.js';
14
- export type { InOutEntry, InOutPhase, InOutRecorderOptions } from './lib/recorder/InOutRecorder.js';
14
+ export type { InOutAttempts, InOutEntry, InOutPhase, InOutRecorderOptions } from './lib/recorder/InOutRecorder.js';
15
15
  export { InOutRecorder, inOutRecorder, ROOT_RUNTIME_STAGE_ID, ROOT_SUBFLOW_ID } from './lib/recorder/InOutRecorder.js';
16
16
  export { KeyedStore } from './lib/recorder/KeyedStore.js';
17
17
  export type { QualityEntry, QualityRecorderOptions, QualityScoringFn } from './lib/recorder/QualityRecorder.js';
@@ -19,5 +19,5 @@ export { QualityRecorder } from './lib/recorder/QualityRecorder.js';
19
19
  export type { QualityFrame, QualityStackTrace } from './lib/recorder/qualityTrace.js';
20
20
  export { formatQualityTrace, qualityTrace } from './lib/recorder/qualityTrace.js';
21
21
  export { SequenceStore } from './lib/recorder/SequenceStore.js';
22
- export type { Topology, TopologyEdge, TopologyIncomingKind, TopologyNode, TopologyRecorderOptions, } from './lib/recorder/TopologyRecorder.js';
22
+ export type { Topology, TopologyEdge, TopologyIncomingKind, TopologyNode, TopologyNodeKind, TopologyRecorderOptions, } from './lib/recorder/TopologyRecorder.js';
23
23
  export { TopologyRecorder, topologyRecorder } from './lib/recorder/TopologyRecorder.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "10.0.1",
3
+ "version": "10.2.0",
4
4
  "description": "Explainable backend flows — automatic causal traces, decision evidence, and MCP tool generation for AI agents",
5
5
  "license": "MIT",
6
6
  "author": "Sanjay Krishna Anbalagan",
@@ -38,7 +38,7 @@
38
38
  "node": ">=22"
39
39
  },
40
40
  "scripts": {
41
- "clean": "rm -rf build/* coverage dist node_modules package-lock.json",
41
+ "clean": "rm -rf build/* coverage dist node_modules",
42
42
  "build": "rm -rf dist && tsc && tsc -p tsconfig.esm.json && node scripts/postbuild-esm.mjs",
43
43
  "prepare": "husky",
44
44
  "watch": "tsc -w",
@@ -137,6 +137,16 @@
137
137
  "default": "./dist/detach.js"
138
138
  }
139
139
  },
140
+ "./sinks": {
141
+ "import": {
142
+ "types": "./dist/esm/sinks.d.ts",
143
+ "default": "./dist/esm/sinks.js"
144
+ },
145
+ "require": {
146
+ "types": "./dist/types/sinks.d.ts",
147
+ "default": "./dist/sinks.js"
148
+ }
149
+ },
140
150
  "./zod": {
141
151
  "import": {
142
152
  "types": "./dist/esm/zod.d.ts",
@@ -163,6 +173,9 @@
163
173
  "detach": [
164
174
  "./dist/types/detach.d.ts"
165
175
  ],
176
+ "sinks": [
177
+ "./dist/types/sinks.d.ts"
178
+ ],
166
179
  "zod": [
167
180
  "./dist/types/zod.d.ts"
168
181
  ]
@@ -176,7 +189,7 @@
176
189
  },
177
190
  "sideEffects": false,
178
191
  "dependencies": {
179
- "foottrace": "^1.0.0"
192
+ "foottrace": "^1.3.0"
180
193
  },
181
194
  "peerDependencies": {
182
195
  "zod": "^3.0.0 || ^4.0.0"
@@ -188,12 +201,12 @@
188
201
  },
189
202
  "devDependencies": {
190
203
  "@arethetypeswrong/cli": "^0.18.1",
191
- "@typescript-eslint/eslint-plugin": "^5.61.0",
192
- "@typescript-eslint/parser": "^5.61.0",
204
+ "@typescript-eslint/eslint-plugin": "^8.71.2",
205
+ "@typescript-eslint/parser": "^8.71.2",
193
206
  "@vitest/coverage-v8": "^4.1.8",
194
207
  "concurrently": "^8.0.1",
195
208
  "esbuild": "^0.28.0",
196
- "eslint": "^8.44.0",
209
+ "eslint": "^8.57.1",
197
210
  "eslint-config-prettier": "^6.15.0",
198
211
  "eslint-config-standard": "^17.0.0",
199
212
  "eslint-import-resolver-typescript": "^3.5.2",
@@ -210,6 +223,7 @@
210
223
  "prettier": "^2.8.1",
211
224
  "publint": "^0.3.0",
212
225
  "semver": "^7.8.5",
226
+ "tsx": "^4.23.15",
213
227
  "typedoc": "^0.28.17",
214
228
  "typescript": "~5.4.5",
215
229
  "vitest": "^4.0.18",