@smthrs/flow 0.0.0-stage → 1.0.0-rc.12

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 (654) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +21 -0
  3. package/README.md +212 -2
  4. package/THIRD_PARTY_NOTICES.md +52 -0
  5. package/dist/cjs/Action/Action.d.ts +250 -0
  6. package/dist/cjs/Action/Action.d.ts.map +1 -0
  7. package/dist/cjs/Action/Action.js +42 -0
  8. package/dist/cjs/Action/Action.js.map +7 -0
  9. package/dist/cjs/Action/AttemptTimedOut.d.ts +33 -0
  10. package/dist/cjs/Action/AttemptTimedOut.d.ts.map +1 -0
  11. package/dist/cjs/Action/AttemptTimedOut.js +51 -0
  12. package/dist/cjs/Action/AttemptTimedOut.js.map +7 -0
  13. package/dist/cjs/Action/BoundaryMode.d.ts +29 -0
  14. package/dist/cjs/Action/BoundaryMode.d.ts.map +1 -0
  15. package/dist/cjs/Action/BoundaryMode.js +36 -0
  16. package/dist/cjs/Action/BoundaryMode.js.map +7 -0
  17. package/dist/cjs/Action/CacheEnvironment.d.ts +158 -0
  18. package/dist/cjs/Action/CacheEnvironment.d.ts.map +1 -0
  19. package/dist/cjs/Action/CacheEnvironment.js +57 -0
  20. package/dist/cjs/Action/CacheEnvironment.js.map +7 -0
  21. package/dist/cjs/Action/ConcurrentKeylessDispatch.d.ts +34 -0
  22. package/dist/cjs/Action/ConcurrentKeylessDispatch.d.ts.map +1 -0
  23. package/dist/cjs/Action/ConcurrentKeylessDispatch.js +46 -0
  24. package/dist/cjs/Action/ConcurrentKeylessDispatch.js.map +7 -0
  25. package/dist/cjs/Action/Context.d.ts +131 -0
  26. package/dist/cjs/Action/Context.d.ts.map +1 -0
  27. package/dist/cjs/Action/Context.js +68 -0
  28. package/dist/cjs/Action/Context.js.map +7 -0
  29. package/dist/cjs/Action/DuplicateImplementation.d.ts +20 -0
  30. package/dist/cjs/Action/DuplicateImplementation.d.ts.map +1 -0
  31. package/dist/cjs/Action/DuplicateImplementation.js +46 -0
  32. package/dist/cjs/Action/DuplicateImplementation.js.map +7 -0
  33. package/dist/cjs/Action/FileBoundary.d.ts +59 -0
  34. package/dist/cjs/Action/FileBoundary.d.ts.map +1 -0
  35. package/dist/cjs/Action/FileBoundary.js +81 -0
  36. package/dist/cjs/Action/FileBoundary.js.map +7 -0
  37. package/dist/cjs/Action/FileInput.d.ts +28 -0
  38. package/dist/cjs/Action/FileInput.d.ts.map +1 -0
  39. package/dist/cjs/Action/FileInput.js +42 -0
  40. package/dist/cjs/Action/FileInput.js.map +7 -0
  41. package/dist/cjs/Action/Filegroup.d.ts +16 -0
  42. package/dist/cjs/Action/Filegroup.d.ts.map +1 -0
  43. package/dist/cjs/Action/Filegroup.js +27 -0
  44. package/dist/cjs/Action/Filegroup.js.map +7 -0
  45. package/dist/cjs/Action/Glob.d.ts +8 -0
  46. package/dist/cjs/Action/Glob.d.ts.map +1 -0
  47. package/dist/cjs/Action/Glob.js +28 -0
  48. package/dist/cjs/Action/Glob.js.map +7 -0
  49. package/dist/cjs/Action/ImplementationVersionMismatch.d.ts +26 -0
  50. package/dist/cjs/Action/ImplementationVersionMismatch.d.ts.map +1 -0
  51. package/dist/cjs/Action/ImplementationVersionMismatch.js +45 -0
  52. package/dist/cjs/Action/ImplementationVersionMismatch.js.map +7 -0
  53. package/dist/cjs/Action/Implementations.d.ts +114 -0
  54. package/dist/cjs/Action/Implementations.d.ts.map +1 -0
  55. package/dist/cjs/Action/Implementations.js +65 -0
  56. package/dist/cjs/Action/Implementations.js.map +7 -0
  57. package/dist/cjs/Action/InfraInterrupt.d.ts +26 -0
  58. package/dist/cjs/Action/InfraInterrupt.d.ts.map +1 -0
  59. package/dist/cjs/Action/InfraInterrupt.js +46 -0
  60. package/dist/cjs/Action/InfraInterrupt.js.map +7 -0
  61. package/dist/cjs/Action/InfraInterruptRetriesExhausted.d.ts +33 -0
  62. package/dist/cjs/Action/InfraInterruptRetriesExhausted.d.ts.map +1 -0
  63. package/dist/cjs/Action/InfraInterruptRetriesExhausted.js +54 -0
  64. package/dist/cjs/Action/InfraInterruptRetriesExhausted.js.map +7 -0
  65. package/dist/cjs/Action/IrreversibleRetryRequiresIdempotencyKey.d.ts +26 -0
  66. package/dist/cjs/Action/IrreversibleRetryRequiresIdempotencyKey.d.ts.map +1 -0
  67. package/dist/cjs/Action/IrreversibleRetryRequiresIdempotencyKey.js +47 -0
  68. package/dist/cjs/Action/IrreversibleRetryRequiresIdempotencyKey.js.map +7 -0
  69. package/dist/cjs/Action/StepIdentity.d.ts +153 -0
  70. package/dist/cjs/Action/StepIdentity.d.ts.map +1 -0
  71. package/dist/cjs/Action/StepIdentity.js +75 -0
  72. package/dist/cjs/Action/StepIdentity.js.map +7 -0
  73. package/dist/cjs/Action/TreeArtifact.d.ts +8 -0
  74. package/dist/cjs/Action/TreeArtifact.d.ts.map +1 -0
  75. package/dist/cjs/Action/TreeArtifact.js +25 -0
  76. package/dist/cjs/Action/TreeArtifact.js.map +7 -0
  77. package/dist/cjs/Action/TypeId.d.ts +8 -0
  78. package/dist/cjs/Action/TypeId.d.ts.map +1 -0
  79. package/dist/cjs/Action/TypeId.js +25 -0
  80. package/dist/cjs/Action/TypeId.js.map +7 -0
  81. package/dist/cjs/Action/UncanonicalIdempotencyKey.d.ts +32 -0
  82. package/dist/cjs/Action/UncanonicalIdempotencyKey.d.ts.map +1 -0
  83. package/dist/cjs/Action/UncanonicalIdempotencyKey.js +51 -0
  84. package/dist/cjs/Action/UncanonicalIdempotencyKey.js.map +7 -0
  85. package/dist/cjs/Action/idempotencyKey.d.ts +21 -0
  86. package/dist/cjs/Action/idempotencyKey.d.ts.map +1 -0
  87. package/dist/cjs/Action/idempotencyKey.js +59 -0
  88. package/dist/cjs/Action/idempotencyKey.js.map +7 -0
  89. package/dist/cjs/Action/index.d.ts +29 -0
  90. package/dist/cjs/Action/index.d.ts.map +1 -0
  91. package/dist/cjs/Action/index.js +60 -0
  92. package/dist/cjs/Action/index.js.map +7 -0
  93. package/dist/cjs/Action/make.d.ts +128 -0
  94. package/dist/cjs/Action/make.d.ts.map +1 -0
  95. package/dist/cjs/Action/make.js +387 -0
  96. package/dist/cjs/Action/make.js.map +7 -0
  97. package/dist/cjs/Action/raceAll.d.ts +21 -0
  98. package/dist/cjs/Action/raceAll.d.ts.map +1 -0
  99. package/dist/cjs/Action/raceAll.js +46 -0
  100. package/dist/cjs/Action/raceAll.js.map +7 -0
  101. package/dist/cjs/Action/retry.d.ts +19 -0
  102. package/dist/cjs/Action/retry.d.ts.map +1 -0
  103. package/dist/cjs/Action/retry.js +75 -0
  104. package/dist/cjs/Action/retry.js.map +7 -0
  105. package/dist/cjs/Deadline.d.ts +110 -0
  106. package/dist/cjs/Deadline.d.ts.map +1 -0
  107. package/dist/cjs/Deadline.js +112 -0
  108. package/dist/cjs/Deadline.js.map +7 -0
  109. package/dist/cjs/DurableClock.d.ts +60 -0
  110. package/dist/cjs/DurableClock.d.ts.map +1 -0
  111. package/dist/cjs/DurableClock.js +107 -0
  112. package/dist/cjs/DurableClock.js.map +7 -0
  113. package/dist/cjs/DurableDeferred.d.ts +311 -0
  114. package/dist/cjs/DurableDeferred.d.ts.map +1 -0
  115. package/dist/cjs/DurableDeferred.js +322 -0
  116. package/dist/cjs/DurableDeferred.js.map +7 -0
  117. package/dist/cjs/DurableQueue.d.ts +160 -0
  118. package/dist/cjs/DurableQueue.d.ts.map +1 -0
  119. package/dist/cjs/DurableQueue.js +229 -0
  120. package/dist/cjs/DurableQueue.js.map +7 -0
  121. package/dist/cjs/ExternalJob.d.ts +179 -0
  122. package/dist/cjs/ExternalJob.d.ts.map +1 -0
  123. package/dist/cjs/ExternalJob.js +339 -0
  124. package/dist/cjs/ExternalJob.js.map +7 -0
  125. package/dist/cjs/Fault.d.ts +131 -0
  126. package/dist/cjs/Fault.d.ts.map +1 -0
  127. package/dist/cjs/Fault.js +157 -0
  128. package/dist/cjs/Fault.js.map +7 -0
  129. package/dist/cjs/Flow/Annotations.d.ts +245 -0
  130. package/dist/cjs/Flow/Annotations.d.ts.map +1 -0
  131. package/dist/cjs/Flow/Annotations.js +72 -0
  132. package/dist/cjs/Flow/Annotations.js.map +7 -0
  133. package/dist/cjs/Flow/CapabilityCeiling.d.ts +21 -0
  134. package/dist/cjs/Flow/CapabilityCeiling.d.ts.map +1 -0
  135. package/dist/cjs/Flow/CapabilityCeiling.js +49 -0
  136. package/dist/cjs/Flow/CapabilityCeiling.js.map +7 -0
  137. package/dist/cjs/Flow/DeadlineExceeded.d.ts +33 -0
  138. package/dist/cjs/Flow/DeadlineExceeded.d.ts.map +1 -0
  139. package/dist/cjs/Flow/DeadlineExceeded.js +52 -0
  140. package/dist/cjs/Flow/DeadlineExceeded.js.map +7 -0
  141. package/dist/cjs/Flow/ExecutionIdRequired.d.ts +25 -0
  142. package/dist/cjs/Flow/ExecutionIdRequired.d.ts.map +1 -0
  143. package/dist/cjs/Flow/ExecutionIdRequired.js +46 -0
  144. package/dist/cjs/Flow/ExecutionIdRequired.js.map +7 -0
  145. package/dist/cjs/Flow/ExecutionIds.d.ts +80 -0
  146. package/dist/cjs/Flow/ExecutionIds.d.ts.map +1 -0
  147. package/dist/cjs/Flow/ExecutionIds.js +66 -0
  148. package/dist/cjs/Flow/ExecutionIds.js.map +7 -0
  149. package/dist/cjs/Flow/Flow.d.ts +422 -0
  150. package/dist/cjs/Flow/Flow.d.ts.map +1 -0
  151. package/dist/cjs/Flow/Flow.js +17 -0
  152. package/dist/cjs/Flow/Flow.js.map +7 -0
  153. package/dist/cjs/Flow/MaxRoundsExceeded.d.ts +35 -0
  154. package/dist/cjs/Flow/MaxRoundsExceeded.d.ts.map +1 -0
  155. package/dist/cjs/Flow/MaxRoundsExceeded.js +50 -0
  156. package/dist/cjs/Flow/MaxRoundsExceeded.js.map +7 -0
  157. package/dist/cjs/Flow/Outcome.d.ts +162 -0
  158. package/dist/cjs/Flow/Outcome.d.ts.map +1 -0
  159. package/dist/cjs/Flow/Outcome.js +80 -0
  160. package/dist/cjs/Flow/Outcome.js.map +7 -0
  161. package/dist/cjs/Flow/Result.d.ts +186 -0
  162. package/dist/cjs/Flow/Result.d.ts.map +1 -0
  163. package/dist/cjs/Flow/Result.js +159 -0
  164. package/dist/cjs/Flow/Result.js.map +7 -0
  165. package/dist/cjs/Flow/Runtime.d.ts +98 -0
  166. package/dist/cjs/Flow/Runtime.d.ts.map +1 -0
  167. package/dist/cjs/Flow/Runtime.js +186 -0
  168. package/dist/cjs/Flow/Runtime.js.map +7 -0
  169. package/dist/cjs/Flow/TypeId.d.ts +8 -0
  170. package/dist/cjs/Flow/TypeId.d.ts.map +1 -0
  171. package/dist/cjs/Flow/TypeId.js +25 -0
  172. package/dist/cjs/Flow/TypeId.js.map +7 -0
  173. package/dist/cjs/Flow/index.d.ts +17 -0
  174. package/dist/cjs/Flow/index.d.ts.map +1 -0
  175. package/dist/cjs/Flow/index.js +29 -0
  176. package/dist/cjs/Flow/index.js.map +7 -0
  177. package/dist/cjs/Flow/internal.d.ts +34 -0
  178. package/dist/cjs/Flow/internal.d.ts.map +1 -0
  179. package/dist/cjs/Flow/internal.js +187 -0
  180. package/dist/cjs/Flow/internal.js.map +7 -0
  181. package/dist/cjs/Flow/make.d.ts +135 -0
  182. package/dist/cjs/Flow/make.d.ts.map +1 -0
  183. package/dist/cjs/Flow/make.js +107 -0
  184. package/dist/cjs/Flow/make.js.map +7 -0
  185. package/dist/cjs/FlowRuntime/CancelRequestFailed.d.ts +55 -0
  186. package/dist/cjs/FlowRuntime/CancelRequestFailed.d.ts.map +1 -0
  187. package/dist/cjs/FlowRuntime/CancelRequestFailed.js +51 -0
  188. package/dist/cjs/FlowRuntime/CancelRequestFailed.js.map +7 -0
  189. package/dist/cjs/FlowRuntime/ExecutionIdentityConflict.d.ts +23 -0
  190. package/dist/cjs/FlowRuntime/ExecutionIdentityConflict.d.ts.map +1 -0
  191. package/dist/cjs/FlowRuntime/ExecutionIdentityConflict.js +55 -0
  192. package/dist/cjs/FlowRuntime/ExecutionIdentityConflict.js.map +7 -0
  193. package/dist/cjs/FlowRuntime/FlowCycleDetected.d.ts +28 -0
  194. package/dist/cjs/FlowRuntime/FlowCycleDetected.d.ts.map +1 -0
  195. package/dist/cjs/FlowRuntime/FlowCycleDetected.js +49 -0
  196. package/dist/cjs/FlowRuntime/FlowCycleDetected.js.map +7 -0
  197. package/dist/cjs/FlowRuntime/FlowExecutionNotFound.d.ts +38 -0
  198. package/dist/cjs/FlowRuntime/FlowExecutionNotFound.d.ts.map +1 -0
  199. package/dist/cjs/FlowRuntime/FlowExecutionNotFound.js +53 -0
  200. package/dist/cjs/FlowRuntime/FlowExecutionNotFound.js.map +7 -0
  201. package/dist/cjs/FlowRuntime/FlowInstance.d.ts +115 -0
  202. package/dist/cjs/FlowRuntime/FlowInstance.d.ts.map +1 -0
  203. package/dist/cjs/FlowRuntime/FlowInstance.js +37 -0
  204. package/dist/cjs/FlowRuntime/FlowInstance.js.map +7 -0
  205. package/dist/cjs/FlowRuntime/FlowRuntime.d.ts +204 -0
  206. package/dist/cjs/FlowRuntime/FlowRuntime.d.ts.map +1 -0
  207. package/dist/cjs/FlowRuntime/FlowRuntime.js +37 -0
  208. package/dist/cjs/FlowRuntime/FlowRuntime.js.map +7 -0
  209. package/dist/cjs/FlowRuntime/NodeRecord.d.ts +177 -0
  210. package/dist/cjs/FlowRuntime/NodeRecord.d.ts.map +1 -0
  211. package/dist/cjs/FlowRuntime/NodeRecord.js +17 -0
  212. package/dist/cjs/FlowRuntime/NodeRecord.js.map +7 -0
  213. package/dist/cjs/FlowRuntime/WaitingAnnotation.d.ts +69 -0
  214. package/dist/cjs/FlowRuntime/WaitingAnnotation.d.ts.map +1 -0
  215. package/dist/cjs/FlowRuntime/WaitingAnnotation.js +53 -0
  216. package/dist/cjs/FlowRuntime/WaitingAnnotation.js.map +7 -0
  217. package/dist/cjs/FlowRuntime/index.d.ts +14 -0
  218. package/dist/cjs/FlowRuntime/index.d.ts.map +1 -0
  219. package/dist/cjs/FlowRuntime/index.js +26 -0
  220. package/dist/cjs/FlowRuntime/index.js.map +7 -0
  221. package/dist/cjs/Graph.d.ts +325 -0
  222. package/dist/cjs/Graph.d.ts.map +1 -0
  223. package/dist/cjs/Graph.js +1020 -0
  224. package/dist/cjs/Graph.js.map +7 -0
  225. package/dist/cjs/HumanTask.d.ts +442 -0
  226. package/dist/cjs/HumanTask.d.ts.map +1 -0
  227. package/dist/cjs/HumanTask.js +413 -0
  228. package/dist/cjs/HumanTask.js.map +7 -0
  229. package/dist/cjs/Interpreter.d.ts +223 -0
  230. package/dist/cjs/Interpreter.d.ts.map +1 -0
  231. package/dist/cjs/Interpreter.js +908 -0
  232. package/dist/cjs/Interpreter.js.map +7 -0
  233. package/dist/cjs/Poll.d.ts +240 -0
  234. package/dist/cjs/Poll.d.ts.map +1 -0
  235. package/dist/cjs/Poll.js +154 -0
  236. package/dist/cjs/Poll.js.map +7 -0
  237. package/dist/cjs/RetryPolicy.d.ts +273 -0
  238. package/dist/cjs/RetryPolicy.d.ts.map +1 -0
  239. package/dist/cjs/RetryPolicy.js +251 -0
  240. package/dist/cjs/RetryPolicy.js.map +7 -0
  241. package/dist/cjs/Sleep.d.ts +98 -0
  242. package/dist/cjs/Sleep.d.ts.map +1 -0
  243. package/dist/cjs/Sleep.js +141 -0
  244. package/dist/cjs/Sleep.js.map +7 -0
  245. package/dist/cjs/Stall.d.ts +173 -0
  246. package/dist/cjs/Stall.d.ts.map +1 -0
  247. package/dist/cjs/Stall.js +92 -0
  248. package/dist/cjs/Stall.js.map +7 -0
  249. package/dist/cjs/WaitFor.d.ts +109 -0
  250. package/dist/cjs/WaitFor.d.ts.map +1 -0
  251. package/dist/cjs/WaitFor.js +117 -0
  252. package/dist/cjs/WaitFor.js.map +7 -0
  253. package/dist/cjs/index.d.ts +107 -0
  254. package/dist/cjs/index.d.ts.map +1 -0
  255. package/dist/cjs/index.js +69 -0
  256. package/dist/cjs/index.js.map +7 -0
  257. package/dist/cjs/internal/BoundedJson.d.ts +48 -0
  258. package/dist/cjs/internal/BoundedJson.d.ts.map +1 -0
  259. package/dist/cjs/internal/BoundedJson.js +96 -0
  260. package/dist/cjs/internal/BoundedJson.js.map +7 -0
  261. package/dist/cjs/internal/DeclarationSite.d.ts +135 -0
  262. package/dist/cjs/internal/DeclarationSite.d.ts.map +1 -0
  263. package/dist/cjs/internal/DeclarationSite.js +89 -0
  264. package/dist/cjs/internal/DeclarationSite.js.map +7 -0
  265. package/dist/cjs/internal/Declarations.d.ts +35 -0
  266. package/dist/cjs/internal/Declarations.d.ts.map +1 -0
  267. package/dist/cjs/internal/Declarations.js +50 -0
  268. package/dist/cjs/internal/Declarations.js.map +7 -0
  269. package/dist/cjs/internal/ExecutionMiddleware.d.ts +21 -0
  270. package/dist/cjs/internal/ExecutionMiddleware.d.ts.map +1 -0
  271. package/dist/cjs/internal/ExecutionMiddleware.js +39 -0
  272. package/dist/cjs/internal/ExecutionMiddleware.js.map +7 -0
  273. package/dist/cjs/internal/JsonSchemaSubset.d.ts +133 -0
  274. package/dist/cjs/internal/JsonSchemaSubset.d.ts.map +1 -0
  275. package/dist/cjs/internal/JsonSchemaSubset.js +229 -0
  276. package/dist/cjs/internal/JsonSchemaSubset.js.map +7 -0
  277. package/dist/cjs/internal/OutcomeMarker.d.ts +18 -0
  278. package/dist/cjs/internal/OutcomeMarker.d.ts.map +1 -0
  279. package/dist/cjs/internal/OutcomeMarker.js +27 -0
  280. package/dist/cjs/internal/OutcomeMarker.js.map +7 -0
  281. package/dist/cjs/package.json +1 -0
  282. package/dist/esm/Action/Action.d.ts +250 -0
  283. package/dist/esm/Action/Action.d.ts.map +1 -0
  284. package/dist/esm/Action/Action.js +30 -0
  285. package/dist/esm/Action/Action.js.map +1 -0
  286. package/dist/esm/Action/AttemptTimedOut.d.ts +33 -0
  287. package/dist/esm/Action/AttemptTimedOut.d.ts.map +1 -0
  288. package/dist/esm/Action/AttemptTimedOut.js +32 -0
  289. package/dist/esm/Action/AttemptTimedOut.js.map +1 -0
  290. package/dist/esm/Action/BoundaryMode.d.ts +29 -0
  291. package/dist/esm/Action/BoundaryMode.d.ts.map +1 -0
  292. package/dist/esm/Action/BoundaryMode.js +23 -0
  293. package/dist/esm/Action/BoundaryMode.js.map +1 -0
  294. package/dist/esm/Action/CacheEnvironment.d.ts +158 -0
  295. package/dist/esm/Action/CacheEnvironment.d.ts.map +1 -0
  296. package/dist/esm/Action/CacheEnvironment.js +130 -0
  297. package/dist/esm/Action/CacheEnvironment.js.map +1 -0
  298. package/dist/esm/Action/ConcurrentKeylessDispatch.d.ts +34 -0
  299. package/dist/esm/Action/ConcurrentKeylessDispatch.d.ts.map +1 -0
  300. package/dist/esm/Action/ConcurrentKeylessDispatch.js +33 -0
  301. package/dist/esm/Action/ConcurrentKeylessDispatch.js.map +1 -0
  302. package/dist/esm/Action/Context.d.ts +131 -0
  303. package/dist/esm/Action/Context.d.ts.map +1 -0
  304. package/dist/esm/Action/Context.js +99 -0
  305. package/dist/esm/Action/Context.js.map +1 -0
  306. package/dist/esm/Action/DuplicateImplementation.d.ts +20 -0
  307. package/dist/esm/Action/DuplicateImplementation.d.ts.map +1 -0
  308. package/dist/esm/Action/DuplicateImplementation.js +20 -0
  309. package/dist/esm/Action/DuplicateImplementation.js.map +1 -0
  310. package/dist/esm/Action/FileBoundary.d.ts +59 -0
  311. package/dist/esm/Action/FileBoundary.d.ts.map +1 -0
  312. package/dist/esm/Action/FileBoundary.js +60 -0
  313. package/dist/esm/Action/FileBoundary.js.map +1 -0
  314. package/dist/esm/Action/FileInput.d.ts +28 -0
  315. package/dist/esm/Action/FileInput.d.ts.map +1 -0
  316. package/dist/esm/Action/FileInput.js +32 -0
  317. package/dist/esm/Action/FileInput.js.map +1 -0
  318. package/dist/esm/Action/Filegroup.d.ts +16 -0
  319. package/dist/esm/Action/Filegroup.d.ts.map +1 -0
  320. package/dist/esm/Action/Filegroup.js +15 -0
  321. package/dist/esm/Action/Filegroup.js.map +1 -0
  322. package/dist/esm/Action/Glob.d.ts +8 -0
  323. package/dist/esm/Action/Glob.d.ts.map +1 -0
  324. package/dist/esm/Action/Glob.js +7 -0
  325. package/dist/esm/Action/Glob.js.map +1 -0
  326. package/dist/esm/Action/ImplementationVersionMismatch.d.ts +26 -0
  327. package/dist/esm/Action/ImplementationVersionMismatch.d.ts.map +1 -0
  328. package/dist/esm/Action/ImplementationVersionMismatch.js +24 -0
  329. package/dist/esm/Action/ImplementationVersionMismatch.js.map +1 -0
  330. package/dist/esm/Action/Implementations.d.ts +114 -0
  331. package/dist/esm/Action/Implementations.d.ts.map +1 -0
  332. package/dist/esm/Action/Implementations.js +94 -0
  333. package/dist/esm/Action/Implementations.js.map +1 -0
  334. package/dist/esm/Action/InfraInterrupt.d.ts +26 -0
  335. package/dist/esm/Action/InfraInterrupt.d.ts.map +1 -0
  336. package/dist/esm/Action/InfraInterrupt.js +25 -0
  337. package/dist/esm/Action/InfraInterrupt.js.map +1 -0
  338. package/dist/esm/Action/InfraInterruptRetriesExhausted.d.ts +33 -0
  339. package/dist/esm/Action/InfraInterruptRetriesExhausted.d.ts.map +1 -0
  340. package/dist/esm/Action/InfraInterruptRetriesExhausted.js +32 -0
  341. package/dist/esm/Action/InfraInterruptRetriesExhausted.js.map +1 -0
  342. package/dist/esm/Action/IrreversibleRetryRequiresIdempotencyKey.d.ts +26 -0
  343. package/dist/esm/Action/IrreversibleRetryRequiresIdempotencyKey.d.ts.map +1 -0
  344. package/dist/esm/Action/IrreversibleRetryRequiresIdempotencyKey.js +25 -0
  345. package/dist/esm/Action/IrreversibleRetryRequiresIdempotencyKey.js.map +1 -0
  346. package/dist/esm/Action/StepIdentity.d.ts +153 -0
  347. package/dist/esm/Action/StepIdentity.d.ts.map +1 -0
  348. package/dist/esm/Action/StepIdentity.js +139 -0
  349. package/dist/esm/Action/StepIdentity.js.map +1 -0
  350. package/dist/esm/Action/TreeArtifact.d.ts +8 -0
  351. package/dist/esm/Action/TreeArtifact.d.ts.map +1 -0
  352. package/dist/esm/Action/TreeArtifact.js +7 -0
  353. package/dist/esm/Action/TreeArtifact.js.map +1 -0
  354. package/dist/esm/Action/TypeId.d.ts +8 -0
  355. package/dist/esm/Action/TypeId.d.ts.map +1 -0
  356. package/dist/esm/Action/TypeId.js +9 -0
  357. package/dist/esm/Action/TypeId.js.map +1 -0
  358. package/dist/esm/Action/UncanonicalIdempotencyKey.d.ts +32 -0
  359. package/dist/esm/Action/UncanonicalIdempotencyKey.d.ts.map +1 -0
  360. package/dist/esm/Action/UncanonicalIdempotencyKey.js +31 -0
  361. package/dist/esm/Action/UncanonicalIdempotencyKey.js.map +1 -0
  362. package/dist/esm/Action/idempotencyKey.d.ts +21 -0
  363. package/dist/esm/Action/idempotencyKey.d.ts.map +1 -0
  364. package/dist/esm/Action/idempotencyKey.js +50 -0
  365. package/dist/esm/Action/idempotencyKey.js.map +1 -0
  366. package/dist/esm/Action/index.d.ts +29 -0
  367. package/dist/esm/Action/index.d.ts.map +1 -0
  368. package/dist/esm/Action/index.js +30 -0
  369. package/dist/esm/Action/index.js.map +1 -0
  370. package/dist/esm/Action/make.d.ts +128 -0
  371. package/dist/esm/Action/make.d.ts.map +1 -0
  372. package/dist/esm/Action/make.js +413 -0
  373. package/dist/esm/Action/make.js.map +1 -0
  374. package/dist/esm/Action/raceAll.d.ts +21 -0
  375. package/dist/esm/Action/raceAll.d.ts.map +1 -0
  376. package/dist/esm/Action/raceAll.js +20 -0
  377. package/dist/esm/Action/raceAll.js.map +1 -0
  378. package/dist/esm/Action/retry.d.ts +19 -0
  379. package/dist/esm/Action/retry.d.ts.map +1 -0
  380. package/dist/esm/Action/retry.js +72 -0
  381. package/dist/esm/Action/retry.js.map +1 -0
  382. package/dist/esm/Deadline.d.ts +110 -0
  383. package/dist/esm/Deadline.d.ts.map +1 -0
  384. package/dist/esm/Deadline.js +164 -0
  385. package/dist/esm/Deadline.js.map +1 -0
  386. package/dist/esm/DurableClock.d.ts +60 -0
  387. package/dist/esm/DurableClock.d.ts.map +1 -0
  388. package/dist/esm/DurableClock.js +122 -0
  389. package/dist/esm/DurableClock.js.map +1 -0
  390. package/dist/esm/DurableDeferred.d.ts +311 -0
  391. package/dist/esm/DurableDeferred.d.ts.map +1 -0
  392. package/dist/esm/DurableDeferred.js +357 -0
  393. package/dist/esm/DurableDeferred.js.map +1 -0
  394. package/dist/esm/DurableQueue.d.ts +160 -0
  395. package/dist/esm/DurableQueue.d.ts.map +1 -0
  396. package/dist/esm/DurableQueue.js +303 -0
  397. package/dist/esm/DurableQueue.js.map +1 -0
  398. package/dist/esm/ExternalJob.d.ts +179 -0
  399. package/dist/esm/ExternalJob.d.ts.map +1 -0
  400. package/dist/esm/ExternalJob.js +296 -0
  401. package/dist/esm/ExternalJob.js.map +1 -0
  402. package/dist/esm/Fault.d.ts +131 -0
  403. package/dist/esm/Fault.d.ts.map +1 -0
  404. package/dist/esm/Fault.js +198 -0
  405. package/dist/esm/Fault.js.map +1 -0
  406. package/dist/esm/Flow/Annotations.d.ts +245 -0
  407. package/dist/esm/Flow/Annotations.d.ts.map +1 -0
  408. package/dist/esm/Flow/Annotations.js +110 -0
  409. package/dist/esm/Flow/Annotations.js.map +1 -0
  410. package/dist/esm/Flow/CapabilityCeiling.d.ts +21 -0
  411. package/dist/esm/Flow/CapabilityCeiling.d.ts.map +1 -0
  412. package/dist/esm/Flow/CapabilityCeiling.js +27 -0
  413. package/dist/esm/Flow/CapabilityCeiling.js.map +1 -0
  414. package/dist/esm/Flow/DeadlineExceeded.d.ts +33 -0
  415. package/dist/esm/Flow/DeadlineExceeded.d.ts.map +1 -0
  416. package/dist/esm/Flow/DeadlineExceeded.js +34 -0
  417. package/dist/esm/Flow/DeadlineExceeded.js.map +1 -0
  418. package/dist/esm/Flow/ExecutionIdRequired.d.ts +25 -0
  419. package/dist/esm/Flow/ExecutionIdRequired.d.ts.map +1 -0
  420. package/dist/esm/Flow/ExecutionIdRequired.js +30 -0
  421. package/dist/esm/Flow/ExecutionIdRequired.js.map +1 -0
  422. package/dist/esm/Flow/ExecutionIds.d.ts +80 -0
  423. package/dist/esm/Flow/ExecutionIds.d.ts.map +1 -0
  424. package/dist/esm/Flow/ExecutionIds.js +91 -0
  425. package/dist/esm/Flow/ExecutionIds.js.map +1 -0
  426. package/dist/esm/Flow/Flow.d.ts +422 -0
  427. package/dist/esm/Flow/Flow.d.ts.map +1 -0
  428. package/dist/esm/Flow/Flow.js +3 -0
  429. package/dist/esm/Flow/Flow.js.map +1 -0
  430. package/dist/esm/Flow/MaxRoundsExceeded.d.ts +35 -0
  431. package/dist/esm/Flow/MaxRoundsExceeded.d.ts.map +1 -0
  432. package/dist/esm/Flow/MaxRoundsExceeded.js +34 -0
  433. package/dist/esm/Flow/MaxRoundsExceeded.js.map +1 -0
  434. package/dist/esm/Flow/Outcome.d.ts +162 -0
  435. package/dist/esm/Flow/Outcome.d.ts.map +1 -0
  436. package/dist/esm/Flow/Outcome.js +115 -0
  437. package/dist/esm/Flow/Outcome.js.map +1 -0
  438. package/dist/esm/Flow/Result.d.ts +186 -0
  439. package/dist/esm/Flow/Result.d.ts.map +1 -0
  440. package/dist/esm/Flow/Result.js +171 -0
  441. package/dist/esm/Flow/Result.js.map +1 -0
  442. package/dist/esm/Flow/Runtime.d.ts +98 -0
  443. package/dist/esm/Flow/Runtime.d.ts.map +1 -0
  444. package/dist/esm/Flow/Runtime.js +252 -0
  445. package/dist/esm/Flow/Runtime.js.map +1 -0
  446. package/dist/esm/Flow/TypeId.d.ts +8 -0
  447. package/dist/esm/Flow/TypeId.d.ts.map +1 -0
  448. package/dist/esm/Flow/TypeId.js +9 -0
  449. package/dist/esm/Flow/TypeId.js.map +1 -0
  450. package/dist/esm/Flow/index.d.ts +17 -0
  451. package/dist/esm/Flow/index.d.ts.map +1 -0
  452. package/dist/esm/Flow/index.js +18 -0
  453. package/dist/esm/Flow/index.js.map +1 -0
  454. package/dist/esm/Flow/internal.d.ts +34 -0
  455. package/dist/esm/Flow/internal.d.ts.map +1 -0
  456. package/dist/esm/Flow/internal.js +149 -0
  457. package/dist/esm/Flow/internal.js.map +1 -0
  458. package/dist/esm/Flow/make.d.ts +135 -0
  459. package/dist/esm/Flow/make.d.ts.map +1 -0
  460. package/dist/esm/Flow/make.js +111 -0
  461. package/dist/esm/Flow/make.js.map +1 -0
  462. package/dist/esm/FlowRuntime/CancelRequestFailed.d.ts +55 -0
  463. package/dist/esm/FlowRuntime/CancelRequestFailed.d.ts.map +1 -0
  464. package/dist/esm/FlowRuntime/CancelRequestFailed.js +53 -0
  465. package/dist/esm/FlowRuntime/CancelRequestFailed.js.map +1 -0
  466. package/dist/esm/FlowRuntime/ExecutionIdentityConflict.d.ts +23 -0
  467. package/dist/esm/FlowRuntime/ExecutionIdentityConflict.d.ts.map +1 -0
  468. package/dist/esm/FlowRuntime/ExecutionIdentityConflict.js +24 -0
  469. package/dist/esm/FlowRuntime/ExecutionIdentityConflict.js.map +1 -0
  470. package/dist/esm/FlowRuntime/FlowCycleDetected.d.ts +28 -0
  471. package/dist/esm/FlowRuntime/FlowCycleDetected.d.ts.map +1 -0
  472. package/dist/esm/FlowRuntime/FlowCycleDetected.js +35 -0
  473. package/dist/esm/FlowRuntime/FlowCycleDetected.js.map +1 -0
  474. package/dist/esm/FlowRuntime/FlowExecutionNotFound.d.ts +38 -0
  475. package/dist/esm/FlowRuntime/FlowExecutionNotFound.d.ts.map +1 -0
  476. package/dist/esm/FlowRuntime/FlowExecutionNotFound.js +40 -0
  477. package/dist/esm/FlowRuntime/FlowExecutionNotFound.js.map +1 -0
  478. package/dist/esm/FlowRuntime/FlowInstance.d.ts +115 -0
  479. package/dist/esm/FlowRuntime/FlowInstance.d.ts.map +1 -0
  480. package/dist/esm/FlowRuntime/FlowInstance.js +24 -0
  481. package/dist/esm/FlowRuntime/FlowInstance.js.map +1 -0
  482. package/dist/esm/FlowRuntime/FlowRuntime.d.ts +204 -0
  483. package/dist/esm/FlowRuntime/FlowRuntime.d.ts.map +1 -0
  484. package/dist/esm/FlowRuntime/FlowRuntime.js +34 -0
  485. package/dist/esm/FlowRuntime/FlowRuntime.js.map +1 -0
  486. package/dist/esm/FlowRuntime/NodeRecord.d.ts +177 -0
  487. package/dist/esm/FlowRuntime/NodeRecord.d.ts.map +1 -0
  488. package/dist/esm/FlowRuntime/NodeRecord.js +21 -0
  489. package/dist/esm/FlowRuntime/NodeRecord.js.map +1 -0
  490. package/dist/esm/FlowRuntime/WaitingAnnotation.d.ts +69 -0
  491. package/dist/esm/FlowRuntime/WaitingAnnotation.d.ts.map +1 -0
  492. package/dist/esm/FlowRuntime/WaitingAnnotation.js +53 -0
  493. package/dist/esm/FlowRuntime/WaitingAnnotation.js.map +1 -0
  494. package/dist/esm/FlowRuntime/index.d.ts +14 -0
  495. package/dist/esm/FlowRuntime/index.d.ts.map +1 -0
  496. package/dist/esm/FlowRuntime/index.js +15 -0
  497. package/dist/esm/FlowRuntime/index.js.map +1 -0
  498. package/dist/esm/Graph.d.ts +325 -0
  499. package/dist/esm/Graph.d.ts.map +1 -0
  500. package/dist/esm/Graph.js +1485 -0
  501. package/dist/esm/Graph.js.map +1 -0
  502. package/dist/esm/HumanTask.d.ts +442 -0
  503. package/dist/esm/HumanTask.d.ts.map +1 -0
  504. package/dist/esm/HumanTask.js +798 -0
  505. package/dist/esm/HumanTask.js.map +1 -0
  506. package/dist/esm/Interpreter.d.ts +223 -0
  507. package/dist/esm/Interpreter.d.ts.map +1 -0
  508. package/dist/esm/Interpreter.js +1289 -0
  509. package/dist/esm/Interpreter.js.map +1 -0
  510. package/dist/esm/Poll.d.ts +240 -0
  511. package/dist/esm/Poll.d.ts.map +1 -0
  512. package/dist/esm/Poll.js +289 -0
  513. package/dist/esm/Poll.js.map +1 -0
  514. package/dist/esm/RetryPolicy.d.ts +273 -0
  515. package/dist/esm/RetryPolicy.d.ts.map +1 -0
  516. package/dist/esm/RetryPolicy.js +429 -0
  517. package/dist/esm/RetryPolicy.js.map +1 -0
  518. package/dist/esm/Sleep.d.ts +98 -0
  519. package/dist/esm/Sleep.d.ts.map +1 -0
  520. package/dist/esm/Sleep.js +256 -0
  521. package/dist/esm/Sleep.js.map +1 -0
  522. package/dist/esm/Stall.d.ts +173 -0
  523. package/dist/esm/Stall.d.ts.map +1 -0
  524. package/dist/esm/Stall.js +138 -0
  525. package/dist/esm/Stall.js.map +1 -0
  526. package/dist/esm/WaitFor.d.ts +109 -0
  527. package/dist/esm/WaitFor.d.ts.map +1 -0
  528. package/dist/esm/WaitFor.js +184 -0
  529. package/dist/esm/WaitFor.js.map +1 -0
  530. package/dist/esm/index.d.ts +107 -0
  531. package/dist/esm/index.d.ts.map +1 -0
  532. package/dist/esm/index.js +108 -0
  533. package/dist/esm/index.js.map +1 -0
  534. package/dist/esm/internal/BoundedJson.d.ts +48 -0
  535. package/dist/esm/internal/BoundedJson.d.ts.map +1 -0
  536. package/dist/esm/internal/BoundedJson.js +107 -0
  537. package/dist/esm/internal/BoundedJson.js.map +1 -0
  538. package/dist/esm/internal/DeclarationSite.d.ts +135 -0
  539. package/dist/esm/internal/DeclarationSite.d.ts.map +1 -0
  540. package/dist/esm/internal/DeclarationSite.js +196 -0
  541. package/dist/esm/internal/DeclarationSite.js.map +1 -0
  542. package/dist/esm/internal/Declarations.d.ts +35 -0
  543. package/dist/esm/internal/Declarations.d.ts.map +1 -0
  544. package/dist/esm/internal/Declarations.js +43 -0
  545. package/dist/esm/internal/Declarations.js.map +1 -0
  546. package/dist/esm/internal/ExecutionMiddleware.d.ts +21 -0
  547. package/dist/esm/internal/ExecutionMiddleware.d.ts.map +1 -0
  548. package/dist/esm/internal/ExecutionMiddleware.js +13 -0
  549. package/dist/esm/internal/ExecutionMiddleware.js.map +1 -0
  550. package/dist/esm/internal/JsonSchemaSubset.d.ts +133 -0
  551. package/dist/esm/internal/JsonSchemaSubset.d.ts.map +1 -0
  552. package/dist/esm/internal/JsonSchemaSubset.js +363 -0
  553. package/dist/esm/internal/JsonSchemaSubset.js.map +1 -0
  554. package/dist/esm/internal/OutcomeMarker.d.ts +18 -0
  555. package/dist/esm/internal/OutcomeMarker.d.ts.map +1 -0
  556. package/dist/esm/internal/OutcomeMarker.js +18 -0
  557. package/dist/esm/internal/OutcomeMarker.js.map +1 -0
  558. package/docs/README.md +152 -0
  559. package/docs/api.md +440 -0
  560. package/docs/concepts/bodies-and-plans.md +167 -0
  561. package/docs/concepts/execution-identity.md +142 -0
  562. package/docs/concepts/flows-and-actions.md +129 -0
  563. package/docs/concepts/suspension-and-replay.md +130 -0
  564. package/docs/concepts/the-runtime-port.md +110 -0
  565. package/docs/concepts/trampoline-rounds.md +151 -0
  566. package/docs/guides/ask-a-person.md +184 -0
  567. package/docs/guides/build-a-body.md +164 -0
  568. package/docs/guides/cancel-and-roll-back.md +112 -0
  569. package/docs/guides/implement-an-action.md +155 -0
  570. package/docs/guides/inspect-the-plan.md +235 -0
  571. package/docs/guides/poll-until-ready.md +169 -0
  572. package/docs/guides/queue-work-to-a-worker.md +122 -0
  573. package/docs/guides/retry-a-failing-action.md +243 -0
  574. package/docs/guides/reuse-a-recorded-result.md +129 -0
  575. package/docs/guides/run-a-child-flow.md +108 -0
  576. package/docs/guides/run-an-external-job.md +86 -0
  577. package/docs/guides/use-a-prompt.md +38 -0
  578. package/docs/guides/wait-for-a-deadline.md +141 -0
  579. package/docs/guides/wait-for-an-external-signal.md +151 -0
  580. package/docs/installation.md +87 -0
  581. package/docs/quickstart.md +164 -0
  582. package/docs/reference/flow.md +1835 -0
  583. package/docs/testing.md +157 -0
  584. package/docs/troubleshooting.md +286 -0
  585. package/package.json +327 -3
  586. package/src/Action/Action.ts +333 -0
  587. package/src/Action/AttemptTimedOut.ts +37 -0
  588. package/src/Action/BoundaryMode.ts +33 -0
  589. package/src/Action/CacheEnvironment.ts +170 -0
  590. package/src/Action/ConcurrentKeylessDispatch.ts +38 -0
  591. package/src/Action/Context.ts +156 -0
  592. package/src/Action/DuplicateImplementation.ts +24 -0
  593. package/src/Action/FileBoundary.ts +77 -0
  594. package/src/Action/FileInput.ts +42 -0
  595. package/src/Action/Filegroup.ts +16 -0
  596. package/src/Action/Glob.ts +8 -0
  597. package/src/Action/ImplementationVersionMismatch.ts +27 -0
  598. package/src/Action/Implementations.ts +149 -0
  599. package/src/Action/InfraInterrupt.ts +30 -0
  600. package/src/Action/InfraInterruptRetriesExhausted.ts +37 -0
  601. package/src/Action/IrreversibleRetryRequiresIdempotencyKey.ts +32 -0
  602. package/src/Action/StepIdentity.ts +219 -0
  603. package/src/Action/TreeArtifact.ts +8 -0
  604. package/src/Action/TypeId.ts +9 -0
  605. package/src/Action/UncanonicalIdempotencyKey.ts +36 -0
  606. package/src/Action/idempotencyKey.ts +67 -0
  607. package/src/Action/index.ts +31 -0
  608. package/src/Action/make.ts +671 -0
  609. package/src/Action/raceAll.ts +48 -0
  610. package/src/Action/retry.ts +92 -0
  611. package/src/Deadline.ts +201 -0
  612. package/src/DurableClock.ts +176 -0
  613. package/src/DurableDeferred.ts +753 -0
  614. package/src/DurableQueue.ts +519 -0
  615. package/src/ExternalJob.ts +437 -0
  616. package/src/Fault.ts +279 -0
  617. package/src/Flow/Annotations.ts +151 -0
  618. package/src/Flow/CapabilityCeiling.ts +37 -0
  619. package/src/Flow/DeadlineExceeded.ts +40 -0
  620. package/src/Flow/ExecutionIdRequired.ts +36 -0
  621. package/src/Flow/ExecutionIds.ts +133 -0
  622. package/src/Flow/Flow.ts +576 -0
  623. package/src/Flow/MaxRoundsExceeded.ts +39 -0
  624. package/src/Flow/Outcome.ts +179 -0
  625. package/src/Flow/Result.ts +284 -0
  626. package/src/Flow/Runtime.ts +339 -0
  627. package/src/Flow/TypeId.ts +9 -0
  628. package/src/Flow/index.ts +19 -0
  629. package/src/Flow/internal.ts +237 -0
  630. package/src/Flow/make.ts +285 -0
  631. package/src/FlowRuntime/CancelRequestFailed.ts +56 -0
  632. package/src/FlowRuntime/ExecutionIdentityConflict.ts +32 -0
  633. package/src/FlowRuntime/FlowCycleDetected.ts +41 -0
  634. package/src/FlowRuntime/FlowExecutionNotFound.ts +46 -0
  635. package/src/FlowRuntime/FlowInstance.ts +128 -0
  636. package/src/FlowRuntime/FlowRuntime.ts +339 -0
  637. package/src/FlowRuntime/NodeRecord.ts +185 -0
  638. package/src/FlowRuntime/WaitingAnnotation.ts +85 -0
  639. package/src/FlowRuntime/index.ts +17 -0
  640. package/src/Graph.ts +1865 -0
  641. package/src/HumanTask.ts +1007 -0
  642. package/src/Interpreter.ts +1620 -0
  643. package/src/Poll.ts +420 -0
  644. package/src/RetryPolicy.ts +550 -0
  645. package/src/Sleep.ts +296 -0
  646. package/src/Stall.ts +217 -0
  647. package/src/WaitFor.ts +228 -0
  648. package/src/index.ts +125 -0
  649. package/src/internal/BoundedJson.ts +109 -0
  650. package/src/internal/DeclarationSite.ts +214 -0
  651. package/src/internal/Declarations.ts +49 -0
  652. package/src/internal/ExecutionMiddleware.ts +29 -0
  653. package/src/internal/JsonSchemaSubset.ts +392 -0
  654. package/src/internal/OutcomeMarker.ts +19 -0
package/src/Graph.ts ADDED
@@ -0,0 +1,1865 @@
1
+ /**
2
+ * Graph building: a flow declaration plus a real payload, walked once into the
3
+ * drafts a plan is compiled from.
4
+ *
5
+ * This is the plan phase of `docs/concepts/bodies-and-plans.md` and nothing
6
+ * else — no I/O, no execution, no elaboration. {@link build} evaluates the
7
+ * body with the REAL payload, because a body's own payload is data
8
+ * (`docs/concepts/flows-and-actions.md`), and evaluates every
9
+ * continuation and branch arm exactly once against a STRICT
10
+ * {@link module:Planned.Planned} placeholder, because a step result does not
11
+ * exist yet. A body that computes on a placeholder therefore fails here,
12
+ * naming the node, rather than baking `NaN` into a plan.
13
+ *
14
+ * Fatal refusals stay fatal. Computing on a planned value throws its
15
+ * `GraphBuildError` immediately, as does recursively expanding an inline
16
+ * `.call()`, because either case makes the requested graph invalid. Recoverable
17
+ * topology loss, such as a missing or invalid continuation, is reported in
18
+ * {@link Graph.diagnostics} so the remaining graph can still be inspected.
19
+ *
20
+ * The walk is stack-safe by construction: topology and payloads are traversed
21
+ * with explicit stacks, never native recursion, and each carries a nesting
22
+ * bound. Topology past the bound is a typed `graph_too_deep` refusal, a
23
+ * payload past it is `payload_too_deep`, and a payload containing itself is
24
+ * `cyclic_payload` — all three fail loudly where unbounded recursion would
25
+ * overflow the native stack without a typed error.
26
+ *
27
+ * Composition follows the same note: `Other.call(payload)` splices the
28
+ * callee's body into this graph with the capabilities of the two declarations
29
+ * intersected, and a flow that calls itself inline throws with the trampoline
30
+ * (`docs/concepts/trampoline-rounds.md`) and the child boundary as the two
31
+ * ways out. A callee whose declared placement the enclosing flow does not carry
32
+ * throws for the same reason and names the same way out, because inline
33
+ * expansion is the claim that these steps run in the caller's execution.
34
+ * `Other.child(payload)` is that boundary: one leaf node here, its own
35
+ * execution when the graph is driven. A branch expands BOTH arms, so the
36
+ * exit condition and the handoff site are visible topology before anything
37
+ * runs, and the predicate rides the branch's key material as a digest.
38
+ *
39
+ * What comes out is `@smthrs/plan` shaped: {@link Graph.drafts} feeds
40
+ * `Plan.compile` and `Plan.append` unchanged, and the `Ref`/`Pending` inputs a
41
+ * node's key material names are what the compiler turns into dependency
42
+ * digests and the plan turns into edges. Structural node ids are derived only
43
+ * from traversal positions and do not enter a plan draft's hashed value, so
44
+ * editing a mapper re-keys exactly what reads it. The interpreter does use an
45
+ * `ActionCall` id as durable dispatch-site material: it is the replay-stable
46
+ * discriminator that keeps identical declarations at distinct graph
47
+ * positions out of one arrival-ordered ordinal scope.
48
+ *
49
+ * Adapted from the agent repo's `@smthrs/core` `Graph.ts`. The lenient
50
+ * placeholder, the model-shaped `Dynamic` node, the lane-merge elaboration,
51
+ * and the conflict pass do not come across: the placeholder is strict here,
52
+ * a model call is an ordinary action, and write overlap is resolved by
53
+ * `Plan.compile`, which owns that verdict.
54
+ *
55
+ * @since 0.1.0
56
+ */
57
+
58
+ import * as Capability from "@smthrs/capability/Capability"
59
+ import * as Effects from "@smthrs/plan/Effects"
60
+ import { GraphBuildError, isFatalDiagnostic } from "@smthrs/plan/GraphBuildError"
61
+ import * as KeyMaterial from "@smthrs/plan/KeyMaterial"
62
+ import * as Node from "@smthrs/plan/Node"
63
+ import type * as Plan from "@smthrs/plan/Plan"
64
+ import * as Planned from "@smthrs/plan/Planned"
65
+ import * as Context from "effect/Context"
66
+ import * as Option from "effect/Option"
67
+ import * as Predicate from "effect/Predicate"
68
+ import type * as Schema from "effect/Schema"
69
+ import type * as Action from "./Action/Action.ts"
70
+ import { TypeId as ActionTypeId } from "./Action/TypeId.ts"
71
+ import * as Annotations from "./Flow/Annotations.ts"
72
+ import { capabilityCeilings } from "./Flow/CapabilityCeiling.ts"
73
+ import type * as Flow from "./Flow/Flow.ts"
74
+ import { TypeId as FlowTypeId } from "./Flow/TypeId.ts"
75
+ import * as DeclarationSite from "./internal/DeclarationSite.ts"
76
+ import { OutcomeNodeTypeId, OutcomeValueTypeId } from "./internal/OutcomeMarker.ts"
77
+
78
+ type OutcomeTag = "Done" | "To" | "Park"
79
+
80
+ /**
81
+ * Why one node depends on another: `value` consumes a result, `continuation`
82
+ * is the sequencing edge a builder or a branch arm records against the
83
+ * upstream node it was evaluated with.
84
+ *
85
+ * @since 0.1.0
86
+ * @category models
87
+ */
88
+ export type EdgeReason = "value" | "continuation" | "failure"
89
+
90
+ /**
91
+ * A dependency edge, pointing from the node that produces to the node that
92
+ * consumes.
93
+ *
94
+ * @since 0.1.0
95
+ * @category models
96
+ */
97
+ export interface Edge {
98
+ readonly from: string
99
+ readonly to: string
100
+ readonly reason: EdgeReason
101
+ }
102
+
103
+ /**
104
+ * One observed node: its structural address, the AST variant it came from, and
105
+ * the {@link module:Plan.NodeDraft} the plan is compiled from.
106
+ *
107
+ * `kind` is the authoring variant, not the plan's node kind — every draft a
108
+ * graph produces is a plan `step`.
109
+ *
110
+ * @since 0.1.0
111
+ * @category models
112
+ */
113
+ export interface GraphNode {
114
+ readonly id: string
115
+ readonly kind: Node.Ast["_tag"]
116
+ readonly dependencies: ReadonlyArray<string>
117
+ readonly capabilities: ReadonlyArray<string>
118
+ readonly capabilityCeilings: ReadonlyArray<ReadonlyArray<string>>
119
+ readonly placement: unknown
120
+ readonly draft: Plan.NodeDraft
121
+ /**
122
+ * The authoring node this graph node was observed at.
123
+ *
124
+ * Key material digests a mapper, a predicate, and a callee's schemas; it
125
+ * cannot carry them, because a digest is not a function. A driver that has
126
+ * the real values in hand needs the functions themselves, so the AST rides
127
+ * along: {@link module:Node.mapper}, {@link module:Node.predicate}, and the
128
+ * member names of an `All` are read off it. The entry node a flow is entered
129
+ * as is the call the graph synthesized, carrying the real payload rather than
130
+ * the inert form an authored call records.
131
+ */
132
+ readonly ast: Node.Ast
133
+ /**
134
+ * What this node passes on, hydrated: real data where the author wrote data,
135
+ * and a {@link module:Planned.Planned} placeholder where a step result goes.
136
+ * It is the call payload of an `ActionCall` or a `FlowCall`, the value of a
137
+ * `Succeed`, the error of a `Fail`, and `undefined` for every other variant,
138
+ * which passes nothing of its own.
139
+ */
140
+ readonly payload: unknown
141
+ /**
142
+ * Where the declaration this node was observed at was written, when the
143
+ * runtime's stack format says so.
144
+ *
145
+ * Provenance, never identity: it is carried on the declaration as a
146
+ * non-enumerable property, so it is outside key material and a node keyed
147
+ * with it is byte-identical to the same node keyed without it. A node with
148
+ * no declaration to read — a map, a combination, an untagged succeed — has
149
+ * none, and so does a declaration made inside the framework itself.
150
+ */
151
+ readonly declaredAt?: DeclarationSite.DeclaredAt | undefined
152
+ }
153
+
154
+ /**
155
+ * What a pure per-node layer resolver is told. It is the identity of the
156
+ * implementation a node would run against — hosts, services, permission
157
+ * implementations — never a layer value or a runtime handle.
158
+ *
159
+ * @since 0.1.0
160
+ * @category models
161
+ */
162
+ export interface LayerRequest {
163
+ readonly nodeId: string
164
+ readonly kind: Node.Ast["_tag"]
165
+ readonly capabilities: ReadonlyArray<string>
166
+ readonly effects: Annotations.Effects | undefined
167
+ readonly placement: unknown
168
+ }
169
+
170
+ /**
171
+ * The knobs {@link build} takes. `resolveLayers` is invoked once per node and
172
+ * must be pure: planning performs no I/O, and a resolver that read the world
173
+ * would make a plan a function of more than its declarations.
174
+ *
175
+ * @since 0.1.0
176
+ * @category models
177
+ */
178
+ export interface BuildOptions {
179
+ readonly resolveLayers?: ((request: LayerRequest) => Iterable<string>) | undefined
180
+ /**
181
+ * `stable` refuses process-local flow bodies, mappers, predicates, and
182
+ * continuation builders before drafts can be compiled or actions driven.
183
+ * Declare every semantic capture with `Node.capture`; include an explicit
184
+ * implementation version for imported behavior not represented by the
185
+ * callback source. The default, `process-local`, supports inspection and
186
+ * experimentation but does not promise reproducible plan identity.
187
+ *
188
+ * This validates callback identity only. Action implementations and resolved
189
+ * layers still need their own semantic identity contract.
190
+ */
191
+ readonly callbackIdentity?: "stable" | "process-local" | undefined
192
+ /**
193
+ * The address the entry node is recorded under, `root` by default. A plan
194
+ * grows by appending, and `Plan.append` refuses an id it already holds, so an
195
+ * elaboration built for an existing plan names its own root.
196
+ */
197
+ readonly root?: string | undefined
198
+ }
199
+
200
+ /**
201
+ * A built graph: the nodes in dependency order, the edges between them, and the
202
+ * refusals that were recoverable enough to report rather than throw.
203
+ *
204
+ * DECIDED: the drafts are NOT a field here. They
205
+ * are derived from `nodes`, and the derivation carries a refusal — a graph with
206
+ * diagnostics is inspectable but not compilable — so {@link drafts} is the only
207
+ * way to ask for them. A field beside the accessor was a second, silent answer
208
+ * to the same question: reading `graph.drafts` handed back the truncated drafts
209
+ * of an incomplete graph while `Graph.drafts(graph)` threw on it. Effect keeps
210
+ * data interfaces plain and puts the policy in the accessor, so the field goes
211
+ * rather than growing a getter that throws from inside a `readonly` record.
212
+ *
213
+ * @since 0.1.0
214
+ * @category models
215
+ */
216
+ export interface Graph {
217
+ readonly nodes: ReadonlyArray<GraphNode>
218
+ readonly edges: ReadonlyArray<Edge>
219
+ readonly diagnostics: ReadonlyArray<GraphBuildError>
220
+ }
221
+
222
+ /**
223
+ * The declared-action fields graph building reads. Only `Action.make`'s
224
+ * declared form produces an `ActionCall`, so a call node's declaration is
225
+ * one of these whenever the side table still holds it.
226
+ *
227
+ * @private
228
+ */
229
+ interface ActionDeclaration {
230
+ readonly name: string
231
+ readonly implementationVersion?: string | undefined
232
+ readonly payloadSchema: Schema.Top
233
+ readonly successSchema: Schema.Top
234
+ readonly errorSchema: Schema.Top
235
+ readonly tier: Action.Tier
236
+ readonly nondeterministic: true | undefined
237
+ readonly annotations: Context.Context<never>
238
+ }
239
+
240
+ /**
241
+ * The inert record `Node`'s AST keeps where a planned placeholder was.
242
+ *
243
+ * @private
244
+ */
245
+ interface PlannedRecord {
246
+ readonly node: string
247
+ readonly path: ReadonlyArray<string>
248
+ }
249
+
250
+ /**
251
+ * What a node that declared nothing contributes: it claims no path either way,
252
+ * so `expected` is the honest default.
253
+ *
254
+ * @private
255
+ */
256
+ const emptyEffects: Plan.NodeEffects = { reads: [], writes: [], boundaryMode: "expected" }
257
+
258
+ /** @private */
259
+ const sorted = (values: Iterable<string>): ReadonlyArray<string> => [...new Set(values)].sort()
260
+
261
+ /** @private */
262
+ const flowDeclaration = (value: unknown): Flow.Any | undefined =>
263
+ Predicate.hasProperty(value, FlowTypeId) ? value as unknown as Flow.Any : undefined
264
+
265
+ /** @private */
266
+ const actionDeclaration = (value: unknown): ActionDeclaration | undefined =>
267
+ Predicate.hasProperty(value, ActionTypeId) ? value as unknown as ActionDeclaration : undefined
268
+
269
+ /** @private */
270
+ const declaredEffects = (annotations: Context.Context<never>): Annotations.Effects | undefined =>
271
+ Option.getOrUndefined(Context.getOption(annotations, Annotations.EffectsDeclaration))
272
+
273
+ /** @private */
274
+ const declaredPlacement = (annotations: Context.Context<never>): unknown =>
275
+ Option.getOrUndefined(Context.getOption(annotations, Annotations.Placement))
276
+
277
+ /** @private */
278
+ const declaredEnvelope = (annotations: Context.Context<never>): Effects.Declaration | undefined =>
279
+ Option.getOrUndefined(Context.getOption(annotations, Annotations.EffectEnvelope))
280
+
281
+ /**
282
+ * What an author reads when a declaration claimed more authority than the one
283
+ * enclosing it granted. Each of the three states the claim, names the flow
284
+ * whose envelope it escaped, and says the one thing that fixes it, because an
285
+ * author who widened an envelope by accident needs the narrowing, not a
286
+ * classification.
287
+ *
288
+ * @private
289
+ */
290
+ const refusalMessage = (
291
+ narrowed: Extract<Effects.NarrowResult, { readonly ok: false }>,
292
+ site: { readonly id: string; readonly flow: string }
293
+ ): string => {
294
+ const at = `"${site.flow}" at "${site.id}"`
295
+ switch (narrowed.code) {
296
+ case "effect_outside_envelope":
297
+ return `Flow ${at} declares ${narrowed.paths.length === 1 ? "a path" : "paths"} its caller's effect ` +
298
+ `envelope does not cover: ${narrowed.paths.join(", ")}. Widen the caller's declared effects to cover ` +
299
+ "them, or narrow this declaration to what the envelope already allows."
300
+ case "effect_mode_widening":
301
+ return `Flow ${at} declares an expected effect mode inside a hermetic envelope. A hermetic envelope is ` +
302
+ "the claim that every effect beneath it is declared, so the callee has to declare a hermetic mode too."
303
+ case "effect_tier_widening":
304
+ return `Flow ${at} declares an effect tier its caller's envelope does not permit. A tier may only narrow ` +
305
+ "from irreversible to compensable to sealed, so raise the caller's tier or lower this one."
306
+ }
307
+ }
308
+
309
+ /**
310
+ * The declaration identity that enters a call node's hashed body: what the
311
+ * callee accepts and produces, so a schema change re-keys the call.
312
+ *
313
+ * The identity is JSON-Schema-SHAPED, and that is its stated limit. Two
314
+ * schemas whose decoders disagree can serialize to the same document, because
315
+ * an effect transformation carries behaviour a JSON Schema document does not
316
+ * describe. Changing only a codec's behaviour therefore does NOT re-key the
317
+ * call, and a cached or recorded result computed under the old codec is
318
+ * replayed under the new one. No fingerprint can close this automatically:
319
+ * effect codecs are not serializable, so the identity would have to be
320
+ * author-supplied. Until a declaration carries one, an author who changes a
321
+ * transformation and needs the call re-keyed renames the declaration.
322
+ * `@smthrs/core` carries the same algorithm and the same limit.
323
+ *
324
+ * `Node.schemaIdentity` prints the document, not the installed `effect`: a
325
+ * release that prints a schema differently must not move the identity.
326
+ *
327
+ * @private
328
+ */
329
+ const schemaIdentity = (schema: Schema.Top): unknown => {
330
+ const cached = schemaDocuments.get(schema)
331
+ if (cached !== undefined) return cached
332
+ const document = Node.schemaIdentity(schema, { onExcessProperty: "error" })
333
+ schemaDocuments.set(schema, document)
334
+ return document
335
+ }
336
+
337
+ /**
338
+ * One document per schema object. A document is a pure function of the
339
+ * schema's AST and schemas are immutable, so every node and every build that
340
+ * names the same declaration reuses the document rather than regenerating it.
341
+ *
342
+ * @private
343
+ */
344
+ const schemaDocuments = new WeakMap<Schema.Top, unknown>()
345
+
346
+ /** @private */
347
+ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
348
+ typeof value === "object" && value !== null &&
349
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)
350
+
351
+ /**
352
+ * The reference a value stands for, whether it is a live placeholder — a real
353
+ * payload may carry one, naming a node an earlier generation already planned —
354
+ * or the inert record an AST payload kept.
355
+ *
356
+ * @private
357
+ */
358
+ const referenceOf = (value: unknown): PlannedRecord | undefined => {
359
+ const live = Planned.reference(value)
360
+ if (live !== undefined) return live
361
+ return Node.plannedReference(value)
362
+ }
363
+
364
+ /** @private */
365
+ const outcomeTagOf = (ast: Node.Ast): OutcomeTag | undefined => {
366
+ const descriptor = Object.getOwnPropertyDescriptor(ast, OutcomeNodeTypeId)
367
+ if (descriptor === undefined || !("value" in descriptor)) return undefined
368
+ const value = descriptor.value
369
+ return value === "Done" || value === "To" || value === "Park" ? value : undefined
370
+ }
371
+
372
+ /**
373
+ * Rebuilds the strict placeholder a reference names, so a spliced body sees
374
+ * the same refusals an inline body does.
375
+ *
376
+ * @private
377
+ */
378
+ const placeholder = (reference: PlannedRecord, node: string): unknown =>
379
+ reference.path.reduce<unknown>(
380
+ (value, key) => (value as Record<string, unknown>)[key],
381
+ Planned.make<unknown>(node)
382
+ )
383
+
384
+ /**
385
+ * The nesting bound the build enforces on authored topology. The walk itself
386
+ * is iterative and cannot overflow the native stack, so the bound is policy:
387
+ * topology a thousand combinators deep is a generated artifact, and refusing
388
+ * it with a typed `graph_too_deep` names the fix — `.child()` boundaries or
389
+ * trampoline handoffs — where unbounded acceptance would only defer the
390
+ * failure to whatever walks the plan next.
391
+ *
392
+ * It is EXPORTED so a composition that unrolls its own topology, as
393
+ * `@smthrs/patterns` does for a bounded loop, refuses at the same number the
394
+ * build enforces instead of restating it and drifting.
395
+ *
396
+ * @since 0.1.0
397
+ * @category models
398
+ */
399
+ export const maximumGraphDepth = 1_000
400
+
401
+ /**
402
+ * The nesting bound the build enforces on payload data, for the same reason
403
+ * {@link maximumGraphDepth} bounds topology: hydration, hashing, and reference
404
+ * scanning walk iteratively, and the bound turns a pathologically deep payload
405
+ * into a typed `payload_too_deep` refusal instead of an unbounded walk over
406
+ * data a plan would have to hash and store level by level.
407
+ *
408
+ * @private
409
+ */
410
+ const maximumPayloadDepth = 1_000
411
+
412
+ /**
413
+ * How one payload walk behaves: where it is walking, for refusal attribution;
414
+ * how a placeholder-like leaf resolves; and the member order of a plain
415
+ * object.
416
+ *
417
+ * @private
418
+ */
419
+ interface PayloadWalk {
420
+ readonly at: string
421
+ readonly resolve: (value: unknown) => { readonly value: unknown } | undefined
422
+ readonly keysOf: (keys: ReadonlyArray<string>) => ReadonlyArray<string>
423
+ }
424
+
425
+ /**
426
+ * One container being walked: the source, the copy being filled, and the
427
+ * member position reached. `keys` is `undefined` for an
428
+ * array, whose members are positional.
429
+ *
430
+ * @private
431
+ */
432
+ interface PayloadFrame {
433
+ readonly source: Record<string, unknown> | ReadonlyArray<unknown>
434
+ readonly keys: ReadonlyArray<string> | undefined
435
+ readonly members: ReadonlyArray<unknown>
436
+ readonly output: Record<string, unknown> | Array<unknown>
437
+ index: number
438
+ }
439
+
440
+ /** @private */
441
+ const container = (
442
+ value: unknown,
443
+ at: string
444
+ ): Record<string, unknown> | ReadonlyArray<unknown> | undefined => {
445
+ if (Array.isArray(value)) return value
446
+ if (typeof value !== "object" || value === null) return undefined
447
+ try {
448
+ return isPlainObject(value) ? value : undefined
449
+ } catch {
450
+ throw new GraphBuildError({
451
+ code: "invalid_payload",
452
+ node: at,
453
+ path: [],
454
+ message: `Payload at "${at}" does not expose an inert object prototype.`
455
+ })
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Walks one payload with an explicit stack. Real recursion would let a deep
461
+ * payload overflow the native stack instead of failing loudly, so depth is a
462
+ * typed refusal here: a value already on the ancestor chain is
463
+ * `cyclic_payload`, and nesting past {@link maximumPayloadDepth} is
464
+ * `payload_too_deep`. Values that are neither placeholders nor plain data
465
+ * pass through untraversed to the key canonicalizer, which is the one
466
+ * component entitled to refuse them.
467
+ *
468
+ * Rebuilding writes members with a null-prototype target and
469
+ * `defineProperty`, never `output[key] = …`: `__proto__` routes through
470
+ * `Object.prototype`'s accessor, so an own `__proto__` field whose value is a
471
+ * primitive would be silently DROPPED and one whose value is an object would
472
+ * reparent the copy instead of being recorded on it. A payload may carry that
473
+ * key — `JSON.parse` produces it as an own data property — and both outcomes
474
+ * are wrong here: a hydrated payload would be planned and run with a field
475
+ * its author wrote missing, and two payloads that differ only in that field
476
+ * would hash to one key. `@smthrs/plan`'s AST cloner answers this the
477
+ * same way.
478
+ *
479
+ * @private
480
+ */
481
+ const walkPayload = (root: unknown, walk: PayloadWalk): unknown => {
482
+ const resolvedRoot = walk.resolve(root)
483
+ if (resolvedRoot !== undefined) return resolvedRoot.value
484
+ const rootContainer = container(root, walk.at)
485
+ if (rootContainer === undefined) return root
486
+ const open = (source: Record<string, unknown> | ReadonlyArray<unknown>): PayloadFrame => {
487
+ const descriptors = (() => {
488
+ try {
489
+ return Object.getOwnPropertyDescriptors(source)
490
+ } catch {
491
+ throw new GraphBuildError({
492
+ code: "invalid_payload",
493
+ node: walk.at,
494
+ path: [],
495
+ message: `Payload at "${walk.at}" does not expose inert own properties.`
496
+ })
497
+ }
498
+ })()
499
+ if (Array.isArray(source)) {
500
+ const members = Array.from({ length: source.length }, (_, index) => {
501
+ const descriptor = descriptors[String(index)]
502
+ if (descriptor === undefined) return undefined
503
+ if (!("value" in descriptor)) {
504
+ throw new GraphBuildError({
505
+ code: "invalid_payload",
506
+ node: walk.at,
507
+ path: [String(index)],
508
+ message: `Payload at "${walk.at}" contains an accessor at index ${index}. Pass inert data.`
509
+ })
510
+ }
511
+ return descriptor.value
512
+ })
513
+ return { source, keys: undefined, members, output: [], index: 0 }
514
+ }
515
+ const keys = walk.keysOf(
516
+ Object.keys(descriptors).filter((key) => descriptors[key]?.enumerable === true)
517
+ )
518
+ const members = keys.map((key) => {
519
+ const descriptor = descriptors[key]!
520
+ if (!("value" in descriptor)) {
521
+ throw new GraphBuildError({
522
+ code: "invalid_payload",
523
+ node: walk.at,
524
+ path: [key],
525
+ message: `Payload at "${walk.at}" contains accessor property "${key}". Pass inert data.`
526
+ })
527
+ }
528
+ return descriptor.value
529
+ })
530
+ return {
531
+ source,
532
+ keys,
533
+ members,
534
+ output: Object.create(null) as Record<string, unknown>,
535
+ index: 0
536
+ }
537
+ }
538
+ const rootFrame = open(rootContainer)
539
+ const frames: Array<PayloadFrame> = [rootFrame]
540
+ const ancestors = new Set<object>([rootContainer])
541
+ while (frames.length > 0) {
542
+ const frame = frames[frames.length - 1]!
543
+ if (frame.index >= frame.members.length) {
544
+ frames.pop()
545
+ ancestors.delete(frame.source)
546
+ continue
547
+ }
548
+ const position = frame.index
549
+ frame.index = position + 1
550
+ const key = frame.keys?.[position]
551
+ const member = frame.members[position]
552
+ const place = (produced: unknown): void => {
553
+ if (key === undefined) {
554
+ const members = frame.output as Array<unknown>
555
+ members.push(produced)
556
+ return
557
+ }
558
+ Object.defineProperty(frame.output, key, {
559
+ configurable: true,
560
+ enumerable: true,
561
+ value: produced,
562
+ writable: true
563
+ })
564
+ }
565
+ const resolved = walk.resolve(member)
566
+ if (resolved !== undefined) {
567
+ place(resolved.value)
568
+ continue
569
+ }
570
+ const nested = container(member, walk.at)
571
+ if (nested === undefined) {
572
+ place(member)
573
+ continue
574
+ }
575
+ if (ancestors.has(nested)) {
576
+ throw new GraphBuildError({
577
+ code: "cyclic_payload",
578
+ node: walk.at,
579
+ path: [],
580
+ message: `Payload at "${walk.at}" contains itself, so no plan can hash or serialize it. Pass acyclic data.`
581
+ })
582
+ }
583
+ if (frames.length >= maximumPayloadDepth) {
584
+ throw new GraphBuildError({
585
+ code: "payload_too_deep",
586
+ node: walk.at,
587
+ path: [],
588
+ message: `Payload at "${walk.at}" nests more than ${maximumPayloadDepth} levels deep. ` +
589
+ "Flatten the payload: a plan hashes and stores every level of it."
590
+ })
591
+ }
592
+ const opened = open(nested)
593
+ place(opened.output)
594
+ ancestors.add(nested)
595
+ frames.push(opened)
596
+ }
597
+ return rootFrame.output
598
+ }
599
+
600
+ /**
601
+ * Turns an AST payload back into what a body must see: real data where the
602
+ * caller wrote data, strict placeholders where it passed a step result. The
603
+ * substitution rewrites a branch's own subject token to its upstream node, and
604
+ * a catch's own subject token to the node it protects, which are those arms'
605
+ * subjects by construction.
606
+ *
607
+ * @private
608
+ */
609
+ const hydrate = (value: unknown, substitutions: ReadonlyMap<string, string>, at: string): unknown =>
610
+ walkPayload(value, {
611
+ at,
612
+ keysOf: (keys) => keys,
613
+ resolve: (member) => {
614
+ const reference = referenceOf(member)
615
+ return reference === undefined
616
+ ? undefined
617
+ : { value: placeholder(reference, substitutions.get(reference.node) ?? reference.node) }
618
+ }
619
+ })
620
+
621
+ /**
622
+ * The hashed form of a payload: placeholders keep their property path and drop
623
+ * the node they came from, because the node id is a lookup address and the
624
+ * dependency itself is named by a separate `Ref`. The same walk collects every
625
+ * upstream result the payload reads into `found`, in sorted-key order, so a
626
+ * payload is canonicalized once rather than walked again for its references.
627
+ *
628
+ * @private
629
+ */
630
+ const literal = (value: unknown, found: Array<PlannedRecord>, at: string): unknown =>
631
+ walkPayload(value, {
632
+ at,
633
+ keysOf: (keys) => [...keys].sort(),
634
+ resolve: (member) => {
635
+ const reference = Planned.reference(member)
636
+ if (reference === undefined) return undefined
637
+ found.push(reference)
638
+ return { value: { _tag: "PlannedInput", path: [...reference.path] } }
639
+ }
640
+ })
641
+
642
+ /**
643
+ * Whether an inline call would place the callee somewhere the enclosing flow
644
+ * cannot run it.
645
+ *
646
+ * A placement directive is opaque to this package — `Annotations.Placement` is
647
+ * `Schema.Unknown`, and interpreting it is a scheduler's job — so the only
648
+ * verdict available here is identity: the same directive is satisfiable, a
649
+ * different one is not. The local comparison has the structural semantics of a
650
+ * canonicalized literal without serializing it: arrays compare by length and
651
+ * element, objects by their own enumerable keys independent of key order, and
652
+ * primitives by `Object.is`. That makes `NaN` equal to itself and cheaply keeps
653
+ * `-0` distinct from `0`. A visited-pair map terminates cyclic object graphs.
654
+ *
655
+ * Identity is claimed only over data the comparison can read inertly. Two
656
+ * distinct objects are identical only when both are plain — an ordinary object
657
+ * literal or a null-prototype object, or two arrays — and only through own data
658
+ * properties: a `Date`, `Map`, `RegExp`, class instance, or function is
659
+ * identical to itself by reference and to nothing else, and an accessor-backed
660
+ * key is identical to nothing at all, because reading it would run author code
661
+ * during graph building. Every one of those is a REFUSAL to prove identity, so
662
+ * the call is pushed to an explicit `.child()` boundary rather than admitted
663
+ * inline against a placement nobody checked. A `Proxy` over a plain object
664
+ * still observes the key and descriptor reads; making the directive inert
665
+ * before it is compared is the remaining half of that hardening.
666
+ *
667
+ * Identity over a CYCLIC directive is bisimulation: the visited-pair map makes
668
+ * two directives identical when they unfold to the same infinite tree, so a
669
+ * self-cycle and a two-node cycle of the same shape are the same directive.
670
+ * Cycle lengths that share no factor pair off against each other for their
671
+ * least common multiple of comparisons, which is what
672
+ * {@link maxComparisonPairs} bounds.
673
+ *
674
+ * DECIDED: an enclosing flow that declares NO
675
+ * placement is unconstrained and satisfies any callee, rather than satisfying
676
+ * only a callee that declares none. Inline expansion runs the callee's steps in
677
+ * the caller's execution, so the constraint that matters is the one the caller
678
+ * already carries; refusing an undeclared caller would make `Flow.Placement`
679
+ * mandatory on every flow in a call chain to keep `.call()` usable.
680
+ *
681
+ * @private
682
+ */
683
+ const structuralIdentity = (left: unknown, right: unknown): boolean => {
684
+ const pending: Array<readonly [unknown, unknown]> = [[left, right]]
685
+ const visited = new WeakMap<object, WeakSet<object>>()
686
+ let examined = 0
687
+ try {
688
+ while (pending.length > 0) {
689
+ const [left, right] = pending.pop()!
690
+ if (++examined > maxComparisonPairs) return false
691
+ if (Object.is(left, right)) continue
692
+ if (left !== Object(left)) return false
693
+ if (right !== Object(right)) return false
694
+ const leftObject = left as object
695
+ const rightObject = right as object
696
+ const leftIsArray = Array.isArray(leftObject)
697
+ if (leftIsArray !== Array.isArray(rightObject)) return false
698
+ if (
699
+ leftIsArray && (leftObject as ReadonlyArray<unknown>).length !== (rightObject as ReadonlyArray<unknown>).length
700
+ ) {
701
+ return false
702
+ }
703
+ const seen = visited.get(leftObject) ?? new WeakSet<object>()
704
+ if (seen.has(rightObject)) continue
705
+ seen.add(rightObject)
706
+ visited.set(leftObject, seen)
707
+ if (!leftIsArray && !(isPlainObject(leftObject) && isPlainObject(rightObject))) return false
708
+ const leftKeys = Reflect.ownKeys(leftObject).filter((key) =>
709
+ Object.prototype.propertyIsEnumerable.call(leftObject, key)
710
+ )
711
+ const rightKeys = Reflect.ownKeys(rightObject).filter((key) =>
712
+ Object.prototype.propertyIsEnumerable.call(rightObject, key)
713
+ )
714
+ if (leftKeys.length !== rightKeys.length) return false
715
+ for (const key of leftKeys) {
716
+ if (!Object.prototype.propertyIsEnumerable.call(rightObject, key)) return false
717
+ // Descriptors rather than `Reflect.get`: a getter is author code, and
718
+ // graph building must not run it.
719
+ const leftMember = Object.getOwnPropertyDescriptor(leftObject, key)!
720
+ const rightMember = Object.getOwnPropertyDescriptor(rightObject, key)!
721
+ if (!("value" in leftMember) || !("value" in rightMember)) return false
722
+ pending.push([leftMember.value, rightMember.value])
723
+ }
724
+ }
725
+ } catch {
726
+ // Every reflection above is a Proxy trap, and a trap is author code that
727
+ // may throw. Graph building must not fail with whatever it threw, so an
728
+ // unreadable directive gets the verdict every other one gets: identity is
729
+ // not proved, and the call goes to an explicit `.child()` boundary.
730
+ return false
731
+ }
732
+ return true
733
+ }
734
+
735
+ /**
736
+ * The most value pairs one placement comparison examines before giving up.
737
+ *
738
+ * A directive is author data walked at planning time, so the comparison needs a
739
+ * ceiling that does not depend on how large or how cyclic that data is.
740
+ * Exhausting the ceiling is a refusal to prove identity, not a claim that the
741
+ * two directives differ.
742
+ *
743
+ * @private
744
+ */
745
+ const maxComparisonPairs = 100_000
746
+
747
+ /** @private */
748
+ const placementConflicts = (enclosing: unknown, callee: unknown): boolean =>
749
+ enclosing !== undefined && callee !== undefined && !structuralIdentity(enclosing, callee)
750
+
751
+ /** The most characters one rendered placement contributes to a diagnostic. */
752
+ const maxPlacementChars = 240
753
+
754
+ /** How many levels of a placement a diagnostic renders before eliding. */
755
+ const maxPlacementDepth = 4
756
+
757
+ /**
758
+ * How many members of one array or object a diagnostic renders.
759
+ *
760
+ * An array's cost is its LENGTH, not its member count: `new Array(100_000)` owns
761
+ * no properties and still renders a hundred thousand separators without this
762
+ * bound, so a directive a byte long becomes a diagnostic megabytes long.
763
+ */
764
+ const maxPlacementMembers = 32
765
+
766
+ /**
767
+ * A bounded, inert rendering of a placement directive, for the refusal that
768
+ * names it.
769
+ *
770
+ * An author who declared two placements needs to see WHICH two the build
771
+ * compared and how they differ, so the refusal prints both. The directive is
772
+ * arbitrary author data, so this holds to exactly the discipline
773
+ * {@link structuralIdentity} holds to: own enumerable data properties only,
774
+ * never an accessor, plain objects and arrays walked and everything else named
775
+ * by kind, under a depth bound, a cycle guard, and a length bound so a large
776
+ * directive cannot become the whole message. Array indices are read as
777
+ * descriptors rather than iterated, because iteration invokes an index getter;
778
+ * a hole renders as `<hole>`. Both arrays and objects render at most
779
+ * {@link maxPlacementMembers} members and mark the rest `<more>`, so the work
780
+ * is bounded before the string is truncated rather than after. Object members
781
+ * are sorted, so two directives that differ only in key order render
782
+ * identically, matching the comparison that admitted them. It never throws.
783
+ *
784
+ * @private
785
+ */
786
+ const renderPlacement = (value: unknown): string => {
787
+ const render = (value: unknown, depth: number, seen: ReadonlyArray<object>): string => {
788
+ if (typeof value === "string") return JSON.stringify(value)
789
+ if (typeof value === "bigint") return `${value}n`
790
+ if (Object(value) !== value) return String(value)
791
+ const object = value as object
792
+ if (seen.includes(object)) return "<cycle>"
793
+ if (depth === maxPlacementDepth) return "<elided>"
794
+ const nested = [...seen, object]
795
+ if (Array.isArray(object)) {
796
+ const length = (object as ReadonlyArray<unknown>).length
797
+ const shown = Math.min(length, maxPlacementMembers)
798
+ const members: Array<string> = []
799
+ for (let index = 0; index < shown; index++) {
800
+ // `Array.prototype.map` invokes an index getter, so indices are read as
801
+ // descriptors here for the same reason keys are below.
802
+ const member = Object.getOwnPropertyDescriptor(object, String(index))
803
+ members.push(
804
+ member === undefined
805
+ ? "<hole>"
806
+ : "value" in member
807
+ ? render(member.value, depth + 1, nested)
808
+ : "<accessor>"
809
+ )
810
+ }
811
+ if (length > shown) members.push("<more>")
812
+ return `[${members.join(",")}]`
813
+ }
814
+ if (!isPlainObject(object)) return Object.prototype.toString.call(object)
815
+ const keys = Reflect.ownKeys(object)
816
+ .filter((key) => Object.prototype.propertyIsEnumerable.call(object, key))
817
+ .sort((left, right) => String(left).localeCompare(String(right)))
818
+ // Select the bounded prefix before rendering values. Rendering every
819
+ // member and slicing afterward still traverses the entire omitted forest.
820
+ const members = keys.slice(0, maxPlacementMembers)
821
+ .map((key) => {
822
+ const member = Object.getOwnPropertyDescriptor(object, key)!
823
+ const rendered = "value" in member ? render(member.value, depth + 1, nested) : "<accessor>"
824
+ return `${String(key)}:${rendered}`
825
+ })
826
+ .sort()
827
+ const shown = keys.length > maxPlacementMembers
828
+ ? [...members, "<more>"]
829
+ : members
830
+ return `{${shown.join(",")}}`
831
+ }
832
+
833
+ try {
834
+ const rendered = render(value, 0, [])
835
+ return rendered.length <= maxPlacementChars
836
+ ? rendered
837
+ : `${rendered.slice(0, maxPlacementChars)} [${rendered.length - maxPlacementChars} characters dropped]`
838
+ } catch {
839
+ return "<unrenderable>"
840
+ }
841
+ }
842
+
843
+ /**
844
+ * The `Literal` a payload hashes as, followed by one `Ref` per distinct
845
+ * upstream result it reads.
846
+ *
847
+ * @private
848
+ */
849
+ const payloadInputs = (payload: unknown, at: string): ReadonlyArray<KeyMaterial.InputRef> => {
850
+ const found: Array<PlannedRecord> = []
851
+ const inputs: Array<KeyMaterial.InputRef> = [{ _tag: "Literal", value: literal(payload, found, at) }]
852
+ const seen = new Set<string>()
853
+ for (const reference of found) {
854
+ const identity = JSON.stringify([reference.node, ...reference.path])
855
+ if (seen.has(identity)) continue
856
+ seen.add(identity)
857
+ inputs.push({ _tag: "Ref", from: reference.node, path: [...reference.path] })
858
+ }
859
+ return inputs
860
+ }
861
+
862
+ /**
863
+ * What one visit needs to know: where it is, how deeply it is nested, what it
864
+ * may do, where it runs, which node the enclosing branch's subject stands
865
+ * for, which flows are already being spliced, and the upstream node it
866
+ * continues from.
867
+ *
868
+ * @private
869
+ */
870
+ interface Visit {
871
+ readonly ast: Node.Ast
872
+ readonly id: string
873
+ /**
874
+ * How many visits enclose this one, checked against
875
+ * {@link maximumGraphDepth}. The walk itself is an explicit operation stack
876
+ * and cannot overflow, so the count exists to refuse pathological topology
877
+ * with a typed error rather than accept it unbounded.
878
+ */
879
+ readonly depth: number
880
+ readonly capabilities: ReadonlyArray<string>
881
+ /**
882
+ * The placement of the flow whose body this visit is inside, which an inline
883
+ * call has to be satisfiable under.
884
+ */
885
+ readonly placement: unknown
886
+ /**
887
+ * The effect authority in force here: the nearest enclosing declaration that
888
+ * was ACCEPTED, so a refused one does not widen what its siblings are checked
889
+ * against. `undefined` means nothing above this node declared an envelope,
890
+ * which is unconstrained rather than empty.
891
+ */
892
+ readonly envelope: Effects.Declaration | undefined
893
+ /**
894
+ * The scheduling priority in force here: the nearest enclosing node's, unless
895
+ * this node states its own. It rides the walk rather than the AST because
896
+ * inheritance is lexical — annotating a container prioritizes everything
897
+ * beneath it that stayed silent.
898
+ */
899
+ readonly priority: number | undefined
900
+ readonly substitutions: ReadonlyMap<string, string>
901
+ readonly stack: ReadonlyArray<Flow.Any>
902
+ readonly prerequisite: string | undefined
903
+ /** Explicit andThen success barriers inherited by the entire subtree. */
904
+ readonly barriers: ReadonlyArray<string>
905
+ }
906
+
907
+ /**
908
+ * Builds the graph of a flow declaration, or of a bare node, by walking the
909
+ * AST once.
910
+ *
911
+ * A flow is entered as a call to itself: the returned graph's `root` node is
912
+ * that call, carrying the flow's declared effects, placement, and
913
+ * capabilities, and its body spliced beneath it. Every flow has a body, so the
914
+ * only inline call this leaves as a leaf is one whose declaration did not
915
+ * survive serialization beside its AST.
916
+ *
917
+ * @since 0.1.0
918
+ * @category constructors
919
+ */
920
+ export const build = (
921
+ flowOrNode: Flow.Any | Node.Any,
922
+ payload?: unknown,
923
+ options: BuildOptions = {}
924
+ ): Graph => {
925
+ const observed: Array<GraphNode> = []
926
+ const observedIds = new Set<string>()
927
+ const observedEdges: Array<Edge> = []
928
+ const observedDiagnostics: Array<GraphBuildError> = []
929
+
930
+ const observeIdentity = (node: string, field: string, identity: Node.FunctionIdentity | undefined): void => {
931
+ if (
932
+ options.callbackIdentity !== "stable" ||
933
+ (identity !== undefined && identity.algorithm !== "sha256-source-ephemeral/v4")
934
+ ) return
935
+ observedDiagnostics.push(
936
+ new GraphBuildError({
937
+ code: "unstable_callback",
938
+ node,
939
+ path: [field],
940
+ message:
941
+ `Callback ${field} at "${node}" has ${identity === undefined ? "unavailable" : "process-local"} identity ` +
942
+ "and cannot enter a stable graph. " +
943
+ "Use Node.capture with every semantic capture, including the version of imported implementation behavior."
944
+ })
945
+ )
946
+ }
947
+
948
+ /**
949
+ * The explicit walk stack. Every step is a thunk; expanding a node pushes
950
+ * its steps in reverse so they pop in authoring order, which keeps the
951
+ * observed nodes, edges, and diagnostics in exactly the order a recursive
952
+ * walk would produce while making depth a data structure instead of native
953
+ * stack frames a deep graph could exhaust.
954
+ */
955
+ const operations: Array<() => void> = []
956
+
957
+ /** Stacks steps to run in the order written, ahead of everything stacked. */
958
+ const sequence = (steps: ReadonlyArray<() => void>): void => {
959
+ for (let index = steps.length - 1; index >= 0; index--) operations.push(steps[index]!)
960
+ }
961
+
962
+ const record = (entry: {
963
+ readonly id: string
964
+ readonly kind: Node.Ast["_tag"]
965
+ readonly dependencies: ReadonlyArray<string>
966
+ readonly capabilities: ReadonlyArray<string>
967
+ readonly effects: Annotations.Effects | undefined
968
+ readonly placement: unknown
969
+ readonly priority: number | undefined
970
+ readonly tier: KeyMaterial.KeyMaterial["kind"]
971
+ readonly nondeterministic?: true | undefined
972
+ readonly body: unknown
973
+ readonly capabilityCeilings?: ReadonlyArray<ReadonlyArray<string>>
974
+ readonly inputs: ReadonlyArray<KeyMaterial.InputRef>
975
+ readonly ast: Node.Ast
976
+ readonly payload: unknown
977
+ readonly declaredAt?: DeclarationSite.DeclaredAt | undefined
978
+ }): void => {
979
+ if (observedIds.has(entry.id)) {
980
+ throw new GraphBuildError({
981
+ code: "duplicate_node",
982
+ node: entry.id,
983
+ path: [],
984
+ message: `Node id "${entry.id}" is durable dispatch identity, so two nodes may not share one. ` +
985
+ "Two structural addresses in this graph collided, most often because a Node.all member name " +
986
+ "contains the separator a nested address also produces. Rename one of them."
987
+ })
988
+ }
989
+ observedIds.add(entry.id)
990
+ const material: KeyMaterial.KeyMaterial = {
991
+ version: KeyMaterial.version,
992
+ kind: entry.tier,
993
+ body: entry.capabilityCeilings?.length
994
+ ? { ...entry.body as object, capabilityCeilings: entry.capabilityCeilings }
995
+ : entry.body,
996
+ inputs: entry.inputs,
997
+ layers: sorted(
998
+ options.resolveLayers?.({
999
+ nodeId: entry.id,
1000
+ kind: entry.kind,
1001
+ capabilities: entry.capabilities,
1002
+ effects: entry.effects,
1003
+ placement: entry.placement
1004
+ }) ?? []
1005
+ ),
1006
+ capabilities: entry.capabilities,
1007
+ ...(entry.nondeterministic === undefined ? {} : { nondeterministic: entry.nondeterministic }),
1008
+ ...(entry.effects === undefined ? {} : { effects: entry.effects }),
1009
+ ...(entry.placement === undefined ? {} : { placement: entry.placement })
1010
+ }
1011
+ observed.push({
1012
+ id: entry.id,
1013
+ kind: entry.kind,
1014
+ dependencies: entry.dependencies,
1015
+ capabilities: entry.capabilities,
1016
+ capabilityCeilings: entry.capabilityCeilings ?? [],
1017
+ placement: entry.placement,
1018
+ // Priority reaches the compiled plan through the draft and stays OUT of
1019
+ // `material`: it orders ready work without changing what the work
1020
+ // produces, so raising it must not re-key a step.
1021
+ draft: {
1022
+ id: entry.id,
1023
+ material,
1024
+ effects: entry.effects ?? emptyEffects,
1025
+ ...(entry.priority === undefined ? {} : { priority: entry.priority })
1026
+ },
1027
+ ast: entry.ast,
1028
+ payload: entry.payload,
1029
+ ...(entry.declaredAt === undefined ? {} : { declaredAt: entry.declaredAt })
1030
+ })
1031
+ }
1032
+
1033
+ /**
1034
+ * An envelope is graph-owned and reaches every node it encloses as the same
1035
+ * object, so it is read into its prepared form once and each enclosed
1036
+ * declaration is checked against that: a wide envelope costs its size once
1037
+ * per build rather than once per node that narrows it.
1038
+ */
1039
+ const preparedEnvelopes = new Map<Effects.Declaration, Effects.PreparedEnvelope>()
1040
+
1041
+ /**
1042
+ * Checks one declaration against the envelope enclosing it, recording the
1043
+ * refusal and answering whether the declaration may become the envelope for
1044
+ * what it encloses in turn. A refused declaration must NOT replace the last
1045
+ * accepted one: widening beneath it would then be measured against the very
1046
+ * claim that was just rejected.
1047
+ */
1048
+ const admitEnvelope = (
1049
+ enclosing: Effects.Declaration | undefined,
1050
+ declared: Effects.Declaration | undefined,
1051
+ site: { readonly id: string; readonly flow: string }
1052
+ ): Effects.Declaration | undefined => {
1053
+ if (declared === undefined) return enclosing
1054
+ if (enclosing === undefined) return declared
1055
+ let prepared = preparedEnvelopes.get(enclosing)
1056
+ if (prepared === undefined) {
1057
+ prepared = Effects.prepareEnvelope(enclosing)
1058
+ preparedEnvelopes.set(enclosing, prepared)
1059
+ }
1060
+ const narrowed = Effects.narrowPrepared(prepared, declared)
1061
+ if (narrowed.ok) return declared
1062
+ observedDiagnostics.push(
1063
+ new GraphBuildError({
1064
+ code: narrowed.code,
1065
+ node: site.id,
1066
+ path: [...narrowed.paths],
1067
+ message: refusalMessage(narrowed, site)
1068
+ })
1069
+ )
1070
+ return enclosing
1071
+ }
1072
+
1073
+ /**
1074
+ * Expands the node a flow call becomes, shared by the entry point and by
1075
+ * every `FlowCall` in a body.
1076
+ */
1077
+ const coveredCapability = (stack: ReadonlyArray<Flow.Any>, text: string): boolean => {
1078
+ const groups = stack.flatMap((flow) => capabilityCeilings(flow.annotations))
1079
+ if (groups.length === 0) return true
1080
+ const candidate = Capability.parsePattern(text)
1081
+ if (Option.isNone(candidate)) return false
1082
+ return groups.every((group) =>
1083
+ group.some((text) => {
1084
+ const grant = Capability.parsePattern(text)
1085
+ return Option.isSome(grant) && Capability.subsumes(grant.value, candidate.value)
1086
+ })
1087
+ )
1088
+ }
1089
+
1090
+ const expandFlowCall = (call: {
1091
+ readonly id: string
1092
+ readonly depth: number
1093
+ readonly ast: Node.Ast
1094
+ readonly flow: string
1095
+ readonly mode: Node.CallMode
1096
+ readonly declaration: Flow.Any | undefined
1097
+ readonly payload: unknown
1098
+ readonly capabilities: ReadonlyArray<string>
1099
+ /** The placement of the flow this call is written inside. */
1100
+ readonly placement: unknown
1101
+ /** The effect authority in force at this call, which the callee must narrow. */
1102
+ readonly envelope: Effects.Declaration | undefined
1103
+ /** The priority in force at this call, already including the call's own. */
1104
+ readonly priority: number | undefined
1105
+ readonly substitutions: ReadonlyMap<string, string>
1106
+ readonly stack: ReadonlyArray<Flow.Any>
1107
+ readonly barriers: ReadonlyArray<string>
1108
+ readonly dependencies: Array<string>
1109
+ readonly inputs: ReadonlyArray<KeyMaterial.InputRef>
1110
+ }): void => {
1111
+ const annotations = call.declaration?.annotations ?? Context.empty()
1112
+ const dependencies = call.dependencies
1113
+ const inputs: Array<KeyMaterial.InputRef> = [...payloadInputs(call.payload, call.id), ...call.inputs]
1114
+ const target = call.mode === "inline" ? call.declaration : undefined
1115
+ const ceiling = sorted(capabilityCeilings(annotations)[0] ?? call.capabilities)
1116
+ const placement = declaredPlacement(annotations)
1117
+ const envelope = declaredEnvelope(annotations)
1118
+ // The authority checks are recorded, not thrown: a graph whose callee
1119
+ // claimed too much is still worth inspecting, and the author needs every
1120
+ // over-claim in the composition at once rather than the first one.
1121
+ //
1122
+ // They run for EVERY call mode. An explicit `.child()` boundary and a
1123
+ // trampoline handoff give the callee its own execution, not its own
1124
+ // authority: whoever writes the call is still the one granting it, which is
1125
+ // the rule `@smthrs/core`'s graph applies to a call whose body it does not
1126
+ // splice.
1127
+ const calleeEnvelope = admitEnvelope(call.envelope, envelope, { id: call.id, flow: call.flow })
1128
+ const dropped = ceiling.filter((capability) => !coveredCapability(call.stack, capability))
1129
+ if (dropped.length > 0) {
1130
+ observedDiagnostics.push(
1131
+ new GraphBuildError({
1132
+ code: "capability_outside_grant",
1133
+ node: call.id,
1134
+ path: dropped,
1135
+ message: `Flow "${call.flow}" at "${call.id}" requires ${dropped.join(", ")}, which the calling flow ` +
1136
+ "does not provably grant. Execution intersects this declaration with every caller ceiling."
1137
+ })
1138
+ )
1139
+ }
1140
+ // The placement refusal precedes the recursion one only in position: an
1141
+ // inline call the caller cannot host is invalid whether or not the callee's
1142
+ // declaration survived to be spliced, because inline expansion is the claim
1143
+ // that these steps run in the caller's execution.
1144
+ if (call.mode === "inline" && placementConflicts(call.placement, placement)) {
1145
+ throw new GraphBuildError({
1146
+ code: "placement_requires_boundary",
1147
+ node: call.id,
1148
+ path: [],
1149
+ message: `Flow "${call.flow}" is called inline at "${call.id}", but its declared placement ` +
1150
+ `${renderPlacement(placement)} is not structurally identical to the enclosing flow's ` +
1151
+ `${renderPlacement(call.placement)}. An inline .call() runs in the caller's execution, so use ` +
1152
+ `${call.flow}.child(payload) to give it its own.`
1153
+ })
1154
+ }
1155
+ const recordCall = (): void => {
1156
+ const bodyIdentity = call.declaration === undefined ? undefined : Node.functionIdentity(call.declaration.body)
1157
+ observeIdentity(call.id, "body", bodyIdentity)
1158
+ record({
1159
+ id: call.id,
1160
+ kind: "FlowCall",
1161
+ dependencies,
1162
+ capabilities: call.capabilities,
1163
+ capabilityCeilings: [
1164
+ ...call.stack.flatMap((flow) => capabilityCeilings(flow.annotations)),
1165
+ ...capabilityCeilings(annotations)
1166
+ ],
1167
+ effects: declaredEffects(annotations),
1168
+ placement,
1169
+ priority: call.priority,
1170
+ // A declared envelope states how reversible the flow beneath it is, so
1171
+ // it is the call's tier. A flow that declared none keeps the sealed
1172
+ // default every call has always carried, which is what keeps every
1173
+ // existing flow's key exactly where it was.
1174
+ tier: envelope?.tier ?? "sealed",
1175
+ body: {
1176
+ _tag: "FlowCall",
1177
+ flow: call.flow,
1178
+ mode: call.mode,
1179
+ declaration: call.declaration === undefined ? undefined : {
1180
+ payload: schemaIdentity(call.declaration.payloadSchema),
1181
+ success: schemaIdentity(call.declaration.successSchema),
1182
+ error: schemaIdentity(call.declaration.errorSchema),
1183
+ capabilities: ceiling,
1184
+ // A spliced body re-keys this call through the `Ref` on the node it
1185
+ // produced. A call the graph keeps as a LEAF — an explicit boundary,
1186
+ // a handoff — has no such node, and its own digest is the only thing
1187
+ // an edited body can move.
1188
+ body: bodyIdentity
1189
+ }
1190
+ },
1191
+ inputs,
1192
+ ast: call.ast,
1193
+ payload: call.payload,
1194
+ declaredAt: DeclarationSite.declaredAt(call.declaration)
1195
+ })
1196
+ }
1197
+ if (target === undefined) {
1198
+ recordCall()
1199
+ return
1200
+ }
1201
+ if (call.stack.includes(target)) {
1202
+ throw new GraphBuildError({
1203
+ code: "recursion_requires_boundary",
1204
+ node: call.id,
1205
+ path: [],
1206
+ message: `Flow "${call.flow}" cannot call itself inline at "${call.id}". ` +
1207
+ `Use ${call.flow}.to(payload) to hand off to the next round, or .child(payload) for an explicit boundary.`
1208
+ })
1209
+ }
1210
+ const built = target.body(call.payload)
1211
+ const spliced = `${call.id}.flow`
1212
+ sequence([
1213
+ () =>
1214
+ expand({
1215
+ ast: built.ast,
1216
+ id: spliced,
1217
+ depth: call.depth + 1,
1218
+ capabilities: ceiling.filter((capability) => coveredCapability(call.stack, capability)),
1219
+ // Inside the spliced body the callee's own placement is the one to
1220
+ // satisfy; a callee that declared none keeps running under the
1221
+ // caller's.
1222
+ placement: placement ?? call.placement,
1223
+ envelope: calleeEnvelope,
1224
+ priority: call.priority,
1225
+ substitutions: call.substitutions,
1226
+ stack: [...call.stack, target],
1227
+ prerequisite: undefined,
1228
+ barriers: call.barriers
1229
+ }),
1230
+ () => {
1231
+ dependencies.push(spliced)
1232
+ observedEdges.push({ from: spliced, to: call.id, reason: "value" })
1233
+ inputs.push({ _tag: "Ref", from: spliced, path: [] })
1234
+ },
1235
+ recordCall
1236
+ ])
1237
+ }
1238
+
1239
+ /**
1240
+ * The continuation of an `AndThen`, evaluated exactly once against the
1241
+ * placeholder standing for its upstream result. A continuation that is gone
1242
+ * — an AST that crossed a serialization boundary — or that answered with
1243
+ * something other than a node leaves the remainder out and says so, because
1244
+ * a graph missing that recoverable step is still worth inspecting. Computing
1245
+ * on the placeholder is a fatal build refusal and propagates unchanged.
1246
+ */
1247
+ const continuation = (
1248
+ ast: Extract<Node.Ast, { readonly _tag: "AndThen" }>,
1249
+ id: string,
1250
+ subject: string
1251
+ ): Node.Ast | undefined => {
1252
+ const builder = Node.continuation(ast)
1253
+ if (builder === undefined) {
1254
+ observedDiagnostics.push(
1255
+ new GraphBuildError({
1256
+ code: "invalid_continuation",
1257
+ node: id,
1258
+ path: [],
1259
+ message: `Node.bindPlanned at "${id}" no longer holds its continuation builder, so its continuation is ` +
1260
+ "missing from the graph. An AST that crossed a serialization boundary left that side table behind."
1261
+ })
1262
+ )
1263
+ return undefined
1264
+ }
1265
+ const built = builder(Planned.make<unknown>(subject))
1266
+ if (Node.isNode(built)) return built.ast
1267
+ observedDiagnostics.push(
1268
+ new GraphBuildError({
1269
+ code: "invalid_continuation",
1270
+ node: id,
1271
+ path: [],
1272
+ message: `Node.bindPlanned at "${id}" did not produce a Node, so its continuation is missing from the graph.`
1273
+ })
1274
+ )
1275
+ return undefined
1276
+ }
1277
+
1278
+ const expand = (request: Visit): void => {
1279
+ if (request.depth > maximumGraphDepth) {
1280
+ throw new GraphBuildError({
1281
+ code: "graph_too_deep",
1282
+ node: request.id,
1283
+ path: [],
1284
+ message: `The graph nests more than ${maximumGraphDepth} levels deep. ` +
1285
+ "Split the flow with .child() boundaries or trampoline handoffs so one execution plans a bounded graph."
1286
+ })
1287
+ }
1288
+ const { ast, capabilities, depth, envelope, id, placement, stack, substitutions } = request
1289
+ // A node's own priority wins; otherwise it inherits the enclosing one.
1290
+ const priority = ast.priority ?? request.priority
1291
+ const dependencies: Array<string> = []
1292
+ const inputs: Array<KeyMaterial.InputRef> = []
1293
+ const depend = (from: string, reason: EdgeReason): void => {
1294
+ dependencies.push(from)
1295
+ observedEdges.push({ from, to: id, reason })
1296
+ inputs.push(reason === "continuation" ? { _tag: "Pending", from } : { _tag: "Ref", from, path: [] })
1297
+ }
1298
+ const child = (childAst: Node.Ast, childId: string, options: {
1299
+ readonly substitutions?: ReadonlyMap<string, string>
1300
+ readonly prerequisite?: string | undefined
1301
+ readonly barriers?: ReadonlyArray<string>
1302
+ } = {}): Visit => ({
1303
+ ast: childAst,
1304
+ id: childId,
1305
+ depth: depth + 1,
1306
+ capabilities,
1307
+ placement,
1308
+ envelope,
1309
+ priority,
1310
+ substitutions: options.substitutions ?? substitutions,
1311
+ stack,
1312
+ prerequisite: options.prerequisite,
1313
+ barriers: options.barriers ?? request.barriers
1314
+ })
1315
+
1316
+ if (request.prerequisite !== undefined) depend(request.prerequisite, "continuation")
1317
+ for (const barrier of request.barriers) {
1318
+ if (barrier !== request.prerequisite) depend(barrier, "continuation")
1319
+ }
1320
+
1321
+ switch (ast._tag) {
1322
+ case "FlowCall": {
1323
+ expandFlowCall({
1324
+ id,
1325
+ depth,
1326
+ ast,
1327
+ flow: ast.flow,
1328
+ mode: ast.mode,
1329
+ declaration: flowDeclaration(Node.declaration(ast)),
1330
+ payload: hydrate(ast.payload, substitutions, id),
1331
+ capabilities,
1332
+ placement,
1333
+ envelope,
1334
+ priority,
1335
+ substitutions,
1336
+ stack,
1337
+ barriers: request.barriers,
1338
+ dependencies,
1339
+ inputs
1340
+ })
1341
+ return
1342
+ }
1343
+ case "ActionCall": {
1344
+ const declared = actionDeclaration(Node.declaration(ast))
1345
+ const annotations = declared?.annotations ?? Context.empty()
1346
+ const payload = hydrate(ast.payload, substitutions, id)
1347
+ // An action is a leaf, so its declaration cannot become an envelope for
1348
+ // anything; it is only checked against the one it sits inside.
1349
+ admitEnvelope(envelope, declaredEnvelope(annotations), { id, flow: ast.action })
1350
+ // The ceiling the declaration named, read off its bag exactly as a
1351
+ // called flow's is. An action that named none keeps the enclosing grant
1352
+ // and adds nothing to its key, so every declaration written before the
1353
+ // literal existed keys exactly where it did.
1354
+ const ceiling = sorted(Context.get(annotations, Annotations.Capabilities))
1355
+ const dropped = ceiling.filter((capability) => !coveredCapability(stack, capability))
1356
+ if (dropped.length > 0) {
1357
+ observedDiagnostics.push(
1358
+ new GraphBuildError({
1359
+ code: "capability_outside_grant",
1360
+ node: id,
1361
+ path: dropped,
1362
+ message: `Action "${ast.action}" at "${id}" requires ${dropped.join(", ")}, which the calling ` +
1363
+ "flow does not provably grant. Execution intersects this declaration with every caller ceiling."
1364
+ })
1365
+ )
1366
+ }
1367
+ record({
1368
+ id,
1369
+ kind: ast._tag,
1370
+ dependencies,
1371
+ capabilities,
1372
+ capabilityCeilings: [
1373
+ ...stack.flatMap((flow) => capabilityCeilings(flow.annotations)),
1374
+ ...capabilityCeilings(annotations)
1375
+ ],
1376
+ effects: declaredEffects(annotations),
1377
+ placement: declaredPlacement(annotations),
1378
+ priority,
1379
+ tier: declared?.tier ?? "sealed",
1380
+ nondeterministic: declared?.nondeterministic,
1381
+ body: {
1382
+ _tag: "ActionCall",
1383
+ action: ast.action,
1384
+ tier: declared?.tier ?? "sealed",
1385
+ declaration: declared === undefined ? undefined : {
1386
+ payload: schemaIdentity(declared.payloadSchema),
1387
+ success: schemaIdentity(declared.successSchema),
1388
+ error: schemaIdentity(declared.errorSchema),
1389
+ ...(ceiling.length === 0 ? {} : { capabilities: ceiling }),
1390
+ ...(declared.implementationVersion === undefined
1391
+ ? {}
1392
+ : { implementationVersion: declared.implementationVersion })
1393
+ }
1394
+ },
1395
+ inputs: [...payloadInputs(payload, id), ...inputs],
1396
+ ast,
1397
+ payload,
1398
+ declaredAt: DeclarationSite.declaredAt(declared)
1399
+ })
1400
+ return
1401
+ }
1402
+ case "Succeed": {
1403
+ const value = hydrate(ast.value, substitutions, id)
1404
+ const outcome = outcomeTagOf(ast)
1405
+ if (outcome !== undefined) {
1406
+ const tag = typeof value === "object" && value !== null
1407
+ ? Object.getOwnPropertyDescriptor(value, "_tag")
1408
+ : undefined
1409
+ if (tag === undefined || !("value" in tag) || tag.value !== outcome) {
1410
+ throw new GraphBuildError({
1411
+ code: "invalid_payload",
1412
+ node: id,
1413
+ path: [],
1414
+ message: `Flow outcome node at "${id}" lost its ${outcome} payload.`
1415
+ })
1416
+ }
1417
+ Object.defineProperty(value, OutcomeValueTypeId, {
1418
+ configurable: false,
1419
+ enumerable: false,
1420
+ value: outcome,
1421
+ writable: false
1422
+ })
1423
+ }
1424
+ record({
1425
+ id,
1426
+ kind: ast._tag,
1427
+ dependencies,
1428
+ capabilities,
1429
+ effects: undefined,
1430
+ placement: undefined,
1431
+ priority,
1432
+ tier: "sealed",
1433
+ body: outcome === undefined ? { _tag: ast._tag } : { _tag: ast._tag, outcome },
1434
+ inputs: [...payloadInputs(value, id), ...inputs],
1435
+ ast,
1436
+ payload: value
1437
+ })
1438
+ return
1439
+ }
1440
+ case "Fail": {
1441
+ // A constant failure is a leaf exactly as `Succeed` is, and its error
1442
+ // is hydrated and keyed the same way: two different refusals are two
1443
+ // different steps, and one that reads an upstream result depends on it.
1444
+ const error = hydrate(ast.error, substitutions, id)
1445
+ record({
1446
+ id,
1447
+ kind: ast._tag,
1448
+ dependencies,
1449
+ capabilities,
1450
+ effects: undefined,
1451
+ placement: undefined,
1452
+ priority,
1453
+ tier: "sealed",
1454
+ body: { _tag: ast._tag },
1455
+ inputs: [...payloadInputs(error, id), ...inputs],
1456
+ ast,
1457
+ payload: error
1458
+ })
1459
+ return
1460
+ }
1461
+ case "All": {
1462
+ const members = Object.keys(ast.nodes)
1463
+ const steps: Array<() => void> = []
1464
+ for (const member of members) {
1465
+ // This durable address cannot be re-keyed here without coordinating
1466
+ // the plan and engine stores. The record-time duplicate check guards
1467
+ // arbitrary member names instead.
1468
+ const memberId = `${id}.all.${member}`
1469
+ steps.push(() => expand(child(ast.nodes[member]!, memberId)))
1470
+ steps.push(() => depend(memberId, "value"))
1471
+ }
1472
+ steps.push(() =>
1473
+ record({
1474
+ id,
1475
+ kind: ast._tag,
1476
+ dependencies,
1477
+ capabilities,
1478
+ effects: undefined,
1479
+ placement: undefined,
1480
+ priority,
1481
+ tier: "sealed",
1482
+ body: { _tag: ast._tag, members },
1483
+ inputs,
1484
+ ast,
1485
+ payload: undefined
1486
+ })
1487
+ )
1488
+ sequence(steps)
1489
+ return
1490
+ }
1491
+ case "Race": {
1492
+ // A join that settles early is expanded exactly as `All` is: every
1493
+ // member is topology the plan shows, and the interpreter decides at
1494
+ // run time which of them it waits for.
1495
+ const members = Object.keys(ast.nodes)
1496
+ const steps: Array<() => void> = []
1497
+ for (const member of members) {
1498
+ const memberId = `${id}.race.${member}`
1499
+ steps.push(() => expand(child(ast.nodes[member]!, memberId)))
1500
+ steps.push(() => depend(memberId, "value"))
1501
+ }
1502
+ steps.push(() =>
1503
+ record({
1504
+ id,
1505
+ kind: ast._tag,
1506
+ dependencies,
1507
+ capabilities,
1508
+ effects: undefined,
1509
+ placement: undefined,
1510
+ priority,
1511
+ tier: "sealed",
1512
+ body: { _tag: ast._tag, mode: ast.mode, count: ast.count, members },
1513
+ inputs,
1514
+ ast,
1515
+ payload: undefined
1516
+ })
1517
+ )
1518
+ sequence(steps)
1519
+ return
1520
+ }
1521
+ case "Map": {
1522
+ observeIdentity(id, "mapper", ast.mapper)
1523
+ const first = `${id}.map`
1524
+ sequence([
1525
+ () => expand(child(ast.first, first)),
1526
+ () => depend(first, "value"),
1527
+ () =>
1528
+ record({
1529
+ id,
1530
+ kind: ast._tag,
1531
+ dependencies,
1532
+ capabilities,
1533
+ effects: undefined,
1534
+ placement: undefined,
1535
+ priority,
1536
+ tier: "sealed",
1537
+ body: { _tag: ast._tag, mapper: ast.mapper },
1538
+ inputs,
1539
+ ast,
1540
+ payload: undefined
1541
+ })
1542
+ ])
1543
+ return
1544
+ }
1545
+ case "AndThen": {
1546
+ observeIdentity(id, "continuation", ast.continuation)
1547
+ const first = `${id}.andThen`
1548
+ sequence([
1549
+ () => expand(child(ast.first, first)),
1550
+ () => depend(first, "value"),
1551
+ () => {
1552
+ // The continuation builder runs only after the first subtree is
1553
+ // fully observed, exactly where the recursive walk evaluated it.
1554
+ const next = ast.next ?? continuation(ast, id, first)
1555
+ if (next === undefined) return
1556
+ const thenId = `${id}.then`
1557
+ sequence([
1558
+ () =>
1559
+ expand(child(next, thenId, {
1560
+ prerequisite: first,
1561
+ // Explicit andThen sequences every descendant, not only the
1562
+ // result/join node. bindPlanned remains a dependency builder:
1563
+ // independent members can run while a referenced producer runs.
1564
+ // The first subtree already carries the outer barriers, so
1565
+ // its success transitively gates every nested descendant.
1566
+ barriers: ast.next === undefined ? request.barriers : [first]
1567
+ })),
1568
+ () => depend(thenId, "value")
1569
+ ])
1570
+ },
1571
+ () =>
1572
+ record({
1573
+ id,
1574
+ kind: ast._tag,
1575
+ dependencies,
1576
+ capabilities,
1577
+ effects: undefined,
1578
+ placement: undefined,
1579
+ priority,
1580
+ tier: "sealed",
1581
+ body: { _tag: ast._tag, continuation: ast.continuation, static: ast.next !== undefined },
1582
+ inputs,
1583
+ ast,
1584
+ payload: undefined
1585
+ })
1586
+ ])
1587
+ return
1588
+ }
1589
+ case "Branch": {
1590
+ observeIdentity(id, "predicate", ast.predicate)
1591
+ const first = `${id}.branch`
1592
+ // DECIDED: each Branch AST carries its own
1593
+ // subject token. An outer subject captured inside a nested arm must
1594
+ // retain the outer binding; one shared token silently rebound it to
1595
+ // the inner branch's subject.
1596
+ const arms = new Map([...substitutions, [ast.subject, first]])
1597
+ sequence([
1598
+ () => expand(child(ast.first, first)),
1599
+ () => depend(first, "value"),
1600
+ () => expand(child(ast.then, `${id}.then`, { substitutions: arms, prerequisite: first })),
1601
+ () => depend(`${id}.then`, "value"),
1602
+ () => expand(child(ast.else, `${id}.else`, { substitutions: arms, prerequisite: first })),
1603
+ () => depend(`${id}.else`, "value"),
1604
+ () =>
1605
+ record({
1606
+ id,
1607
+ kind: ast._tag,
1608
+ dependencies,
1609
+ capabilities,
1610
+ effects: undefined,
1611
+ placement: undefined,
1612
+ priority,
1613
+ tier: "sealed",
1614
+ body: { _tag: ast._tag, predicate: ast.predicate },
1615
+ inputs,
1616
+ ast,
1617
+ payload: undefined
1618
+ })
1619
+ ])
1620
+ return
1621
+ }
1622
+ case "Catch": {
1623
+ if (ast.filter !== undefined) observeIdentity(id, "filter", ast.filterIdentity)
1624
+ const protectedId = `${id}.protected`
1625
+ const failureId = `${id}.failure`
1626
+ // DECIDED: each Catch AST carries its own
1627
+ // subject token, exactly as each Branch does. An outer error captured
1628
+ // inside a nested failure arm must retain the outer binding; one shared
1629
+ // token silently rebound it to the inner catch's protected node.
1630
+ const recovery = new Map([...substitutions, [ast.subject, protectedId]])
1631
+ sequence([
1632
+ () => expand(child(ast.protected, protectedId)),
1633
+ () => depend(protectedId, "value"),
1634
+ () => expand(child(ast.failure, failureId, { substitutions: recovery, prerequisite: protectedId })),
1635
+ () => {
1636
+ dependencies.push(failureId)
1637
+ observedEdges.push({ from: protectedId, to: failureId, reason: "failure" })
1638
+ observedEdges.push({ from: failureId, to: id, reason: "value" })
1639
+ inputs.push({ _tag: "Ref", from: failureId, path: [] })
1640
+ },
1641
+ () =>
1642
+ record({
1643
+ id,
1644
+ kind: ast._tag,
1645
+ dependencies,
1646
+ capabilities,
1647
+ effects: undefined,
1648
+ placement: undefined,
1649
+ priority,
1650
+ tier: "sealed",
1651
+ body: { _tag: ast._tag, filter: ast.filter, filterIdentity: ast.filterIdentity },
1652
+ inputs,
1653
+ ast,
1654
+ payload: undefined
1655
+ })
1656
+ ])
1657
+ return
1658
+ }
1659
+ }
1660
+ }
1661
+
1662
+ const declaration = flowDeclaration(flowOrNode)
1663
+ const root = options.root ?? "root"
1664
+ if (declaration === undefined) {
1665
+ expand({
1666
+ ast: (flowOrNode as Node.Any).ast,
1667
+ id: root,
1668
+ depth: 0,
1669
+ capabilities: [],
1670
+ placement: undefined,
1671
+ envelope: undefined,
1672
+ priority: undefined,
1673
+ substitutions: new Map(),
1674
+ stack: [],
1675
+ prerequisite: undefined,
1676
+ barriers: []
1677
+ })
1678
+ } else {
1679
+ const entry = hydrate(payload, new Map(), root)
1680
+ expandFlowCall({
1681
+ id: root,
1682
+ depth: 0,
1683
+ // The entry is a call to the flow itself that no author wrote, so its
1684
+ // AST is synthesized here rather than recorded by `Node.flowCall`, which
1685
+ // would replace a payload's non-JSON values with their plain-object
1686
+ // copies. What a body was planned with stays what the entry node shows.
1687
+ ast: { _tag: "FlowCall", flow: declaration._tag, mode: "inline", payload: entry },
1688
+ flow: declaration._tag,
1689
+ mode: "inline",
1690
+ declaration,
1691
+ payload: entry,
1692
+ capabilities: sorted(Context.get(declaration.annotations, Annotations.Capabilities)),
1693
+ // Nothing encloses the entry, so its own declared placement is what the
1694
+ // body it splices has to be satisfiable under, not a constraint on it.
1695
+ // Its declared envelope reads the same way: the entry is the top of the
1696
+ // authority chain, so it grants rather than narrows.
1697
+ placement: undefined,
1698
+ envelope: undefined,
1699
+ priority: undefined,
1700
+ substitutions: new Map(),
1701
+ stack: [],
1702
+ barriers: [],
1703
+ dependencies: [],
1704
+ inputs: []
1705
+ })
1706
+ }
1707
+ // The walk itself: pop until every stacked step has run. A refusal thrown
1708
+ // by any step propagates out of the build unchanged.
1709
+ while (operations.length > 0) operations.pop()!()
1710
+
1711
+ return {
1712
+ nodes: observed,
1713
+ edges: observedEdges,
1714
+ diagnostics: observedDiagnostics
1715
+ }
1716
+ }
1717
+
1718
+ /**
1719
+ * The observed nodes, children before the parents that consume them.
1720
+ *
1721
+ * @since 0.1.0
1722
+ * @category accessors
1723
+ */
1724
+ export const nodes = (graph: Graph): ReadonlyArray<GraphNode> => graph.nodes
1725
+
1726
+ /**
1727
+ * The dependency edges, in the order they were observed.
1728
+ *
1729
+ * @since 0.1.0
1730
+ * @category accessors
1731
+ */
1732
+ export const edges = (graph: Graph): ReadonlyArray<Edge> => graph.edges
1733
+
1734
+ /**
1735
+ * States that a file this runtime is about to evaluate holds bytes read from
1736
+ * another, so the declarations inside it are reported at the file an author
1737
+ * can open.
1738
+ *
1739
+ * A host that verifies a flow's source evaluates THE BYTES IT MEASURED, and
1740
+ * the only way to evaluate bytes here is to write them somewhere and import
1741
+ * that path: `@smthrs/registry` `Executable` writes them as a scratch sibling
1742
+ * of the entry and removes it when the load is over. Without this, every node
1743
+ * of an agent-authored flow names that scratch file, and a reader offered its
1744
+ * path is offered a file nothing holds (D-068).
1745
+ *
1746
+ * The loader states this BEFORE it imports, because a declaration captures its
1747
+ * site while the module is evaluated and never rewrites it. Both sides hold
1748
+ * the same module: a loaded flow is recognized by this package's own type id,
1749
+ * so a graph file resolving a second copy of `@smthrs/flow` is refused as not
1750
+ * a flow at all rather than registered with a site nothing corrected.
1751
+ *
1752
+ * @since 0.1.0
1753
+ * @category constructors
1754
+ */
1755
+ export const evaluatedFrom = (evaluated: string, entry: string): void => DeclarationSite.evaluatedFrom(evaluated, entry)
1756
+
1757
+ /**
1758
+ * The drafts, in node order, ready for `Plan.compile` or `Plan.append`
1759
+ * unchanged.
1760
+ *
1761
+ * A graph with a FATAL diagnostic is inspectable but intentionally not
1762
+ * compilable: returning its partial drafts would turn missing topology into a
1763
+ * valid plan. This accessor therefore throws the first fatal build refusal, and
1764
+ * it is the ONLY way to reach the drafts. The built graph holds nodes, and a
1765
+ * draft is the node's own {@link GraphNode.draft}.
1766
+ *
1767
+ * An advisory refusal does not withhold the drafts. The one advisory code is
1768
+ * `capability_outside_grant`, where the callee runs with LESS authority than it
1769
+ * asked for: the author needs to see it, and the graph it describes is exactly
1770
+ * the graph that will run.
1771
+ *
1772
+ * @since 0.1.0
1773
+ * @category accessors
1774
+ */
1775
+ export const drafts = (graph: Graph): ReadonlyArray<Plan.NodeDraft> => {
1776
+ const refusal = graph.diagnostics.find(isFatalDiagnostic)
1777
+ if (refusal !== undefined) throw refusal
1778
+ return graph.nodes.map((node) => node.draft)
1779
+ }
1780
+
1781
+ /**
1782
+ * Recoverable topology issues recorded during the build, such as a missing
1783
+ * continuation builder or a continuation that did not produce a node. Fatal
1784
+ * refusals, including computing on a planned value and recursive inline
1785
+ * `.call()`, throw from {@link build} and never appear here.
1786
+ *
1787
+ * @since 0.1.0
1788
+ * @category accessors
1789
+ */
1790
+ export const diagnostics = (graph: Graph): ReadonlyArray<GraphBuildError> => graph.diagnostics
1791
+
1792
+ /** Serializable declaration inspection. No action or prompt renderer runs.
1793
+ * Payload-dependent JavaScript can refuse symbolic inspection; that is a
1794
+ * diagnostic, not evidence that the flow itself cannot execute.
1795
+ * @category models
1796
+ * @since 1.0.0
1797
+ */
1798
+ export interface Inspection {
1799
+ readonly nodes: ReadonlyArray<Pick<GraphNode, "id" | "kind" | "dependencies"> & { readonly label?: string }>
1800
+ readonly edges: ReadonlyArray<Edge>
1801
+ readonly steps: ReadonlyArray<{ readonly id: string; readonly label: string }>
1802
+ readonly diagnostics: ReadonlyArray<{ readonly code: string; readonly message: string }>
1803
+ /** The renderer's source, never rendered with an invented input. */
1804
+ readonly prompt?: string
1805
+ }
1806
+
1807
+ /** Inspect the existing graph model with an unresolved input reference.
1808
+ * Run this only in the same guest that loads the declaration: plan-time body
1809
+ * code is repository code too. An input-dependent body is reported explicitly
1810
+ * without choosing a sample payload. As with Planned values in bodies, use
1811
+ * node combinators for branching; JavaScript truthiness is not inspectable.
1812
+ * @category constructors
1813
+ * @since 1.0.0
1814
+ */
1815
+ export const inspect = (flow: Flow.Any): Inspection => {
1816
+ const prompt = "prompt" in flow && typeof flow.prompt === "function"
1817
+ ? Function.prototype.toString.call(flow.prompt)
1818
+ : undefined
1819
+ // The prompt source belongs to the declaration, so it rides on every
1820
+ // inspection, whether or not the graph could be built.
1821
+ const withPrompt = (inspection: Omit<Inspection, "prompt">): Inspection =>
1822
+ prompt === undefined ? inspection : { ...inspection, prompt }
1823
+ try {
1824
+ const graph = build(flow, Planned.make("input"))
1825
+ const nodes = graph.nodes.map((node) => ({
1826
+ id: node.id,
1827
+ kind: node.kind,
1828
+ ...(node.ast._tag === "FlowCall" ?
1829
+ { label: node.ast.flow }
1830
+ : node.ast._tag === "ActionCall"
1831
+ ? { label: node.ast.action }
1832
+ : {}),
1833
+ dependencies: node.dependencies
1834
+ }))
1835
+ // Inline flow calls group the actions beneath them; only boundary calls
1836
+ // and actions are steps, so the same work is never displayed twice.
1837
+ const steps = graph.nodes.flatMap((node) =>
1838
+ node.ast._tag === "ActionCall" ?
1839
+ [{ id: node.id, label: node.ast.action }]
1840
+ : node.ast._tag === "FlowCall" && node.ast.mode !== "inline"
1841
+ ? [{ id: node.id, label: node.ast.flow }]
1842
+ : []
1843
+ )
1844
+ return withPrompt({
1845
+ nodes,
1846
+ edges: graph.edges,
1847
+ steps,
1848
+ diagnostics: graph.diagnostics.map(({ code, message }) => ({ code, message }))
1849
+ })
1850
+ } catch (error) {
1851
+ return withPrompt({
1852
+ // The declaration itself is a known boundary even when its internals
1853
+ // require real input. Do not publish a fictitious empty execution.
1854
+ nodes: [{ id: "root", kind: "FlowCall", label: flow._tag, dependencies: [] }],
1855
+ edges: [],
1856
+ steps: [{ id: "root", label: flow._tag }],
1857
+ diagnostics: [{
1858
+ code: error instanceof GraphBuildError
1859
+ ? error.code === "planned_value_computed" ? "declaration_requires_input" : error.code
1860
+ : "declaration_inspection_failed",
1861
+ message: error instanceof Error ? error.message : String(error)
1862
+ }]
1863
+ })
1864
+ }
1865
+ }