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

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