@plumbus/core 0.7.1 → 0.8.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (507) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +2 -1
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/admission/client.d.ts +9 -0
  5. package/dist/admission/client.d.ts.map +1 -0
  6. package/dist/admission/client.js +34 -0
  7. package/dist/admission/client.js.map +1 -0
  8. package/dist/admission/index.d.ts +5 -0
  9. package/dist/admission/index.d.ts.map +1 -0
  10. package/dist/admission/index.js +4 -0
  11. package/dist/admission/index.js.map +1 -0
  12. package/dist/admission/request-admission.d.ts +26 -0
  13. package/dist/admission/request-admission.d.ts.map +1 -0
  14. package/dist/admission/request-admission.js +85 -0
  15. package/dist/admission/request-admission.js.map +1 -0
  16. package/dist/admission/shared.d.ts +31 -0
  17. package/dist/admission/shared.d.ts.map +1 -0
  18. package/dist/admission/shared.js +30 -0
  19. package/dist/admission/shared.js.map +1 -0
  20. package/dist/ai/ai/provider.d.ts +5 -7
  21. package/dist/ai/ai-service.d.ts +8 -1
  22. package/dist/ai/ai-service.d.ts.map +1 -1
  23. package/dist/ai/ai-service.js +23 -13
  24. package/dist/ai/ai-service.js.map +1 -1
  25. package/dist/ai/errors/data-errors.d.ts +6 -0
  26. package/dist/ai/errors/index.d.ts +4 -1
  27. package/dist/ai/fill-prompt-template.d.ts +12 -0
  28. package/dist/ai/fill-prompt-template.d.ts.map +1 -0
  29. package/dist/ai/fill-prompt-template.js +19 -0
  30. package/dist/ai/fill-prompt-template.js.map +1 -0
  31. package/dist/ai/governed-artifacts.d.ts +49 -0
  32. package/dist/ai/governed-artifacts.d.ts.map +1 -0
  33. package/dist/ai/governed-artifacts.js +277 -0
  34. package/dist/ai/governed-artifacts.js.map +1 -0
  35. package/dist/ai/governed-budget.d.ts +17 -0
  36. package/dist/ai/governed-budget.d.ts.map +1 -0
  37. package/dist/ai/governed-budget.js +52 -0
  38. package/dist/ai/governed-budget.js.map +1 -0
  39. package/dist/ai/governed-host.d.ts +54 -0
  40. package/dist/ai/governed-host.d.ts.map +1 -0
  41. package/dist/ai/governed-host.js +8 -0
  42. package/dist/ai/governed-host.js.map +1 -0
  43. package/dist/ai/governed-invoke.d.ts +41 -0
  44. package/dist/ai/governed-invoke.d.ts.map +1 -0
  45. package/dist/ai/governed-invoke.js +102 -0
  46. package/dist/ai/governed-invoke.js.map +1 -0
  47. package/dist/ai/governed-runtime.d.ts +103 -0
  48. package/dist/ai/governed-runtime.d.ts.map +1 -0
  49. package/dist/ai/governed-runtime.js +151 -0
  50. package/dist/ai/governed-runtime.js.map +1 -0
  51. package/dist/ai/index.d.ts +12 -2
  52. package/dist/ai/index.d.ts.map +1 -1
  53. package/dist/ai/index.js +8 -2
  54. package/dist/ai/index.js.map +1 -1
  55. package/dist/ai/prompt-registry.d.ts +6 -0
  56. package/dist/ai/prompt-registry.d.ts.map +1 -1
  57. package/dist/ai/prompt-registry.js +12 -0
  58. package/dist/ai/prompt-registry.js.map +1 -1
  59. package/dist/ai/provider.d.ts +5 -7
  60. package/dist/ai/provider.d.ts.map +1 -1
  61. package/dist/ai/provider.js +29 -0
  62. package/dist/ai/provider.js.map +1 -1
  63. package/dist/ai/types/enums.d.ts +2 -0
  64. package/dist/ai/types/fields.d.ts +7 -0
  65. package/dist/ai/types/prompt.d.ts +8 -0
  66. package/dist/ai/validation.d.ts.map +1 -1
  67. package/dist/ai/validation.js +10 -1
  68. package/dist/ai/validation.js.map +1 -1
  69. package/dist/api/exposure.d.ts +14 -0
  70. package/dist/api/exposure.d.ts.map +1 -1
  71. package/dist/api/exposure.js +16 -0
  72. package/dist/api/exposure.js.map +1 -1
  73. package/dist/api/idempotency.d.ts +67 -0
  74. package/dist/api/idempotency.d.ts.map +1 -0
  75. package/dist/api/idempotency.js +176 -0
  76. package/dist/api/idempotency.js.map +1 -0
  77. package/dist/api/index.d.ts +3 -1
  78. package/dist/api/index.d.ts.map +1 -1
  79. package/dist/api/index.js +4 -2
  80. package/dist/api/index.js.map +1 -1
  81. package/dist/api/route-generator.d.ts +50 -6
  82. package/dist/api/route-generator.d.ts.map +1 -1
  83. package/dist/api/route-generator.js +254 -12
  84. package/dist/api/route-generator.js.map +1 -1
  85. package/dist/approvals/action-risk.d.ts +41 -0
  86. package/dist/approvals/action-risk.d.ts.map +1 -0
  87. package/dist/approvals/action-risk.js +56 -0
  88. package/dist/approvals/action-risk.js.map +1 -0
  89. package/dist/approvals/actors.d.ts +8 -0
  90. package/dist/approvals/actors.d.ts.map +1 -0
  91. package/dist/approvals/actors.js +26 -0
  92. package/dist/approvals/actors.js.map +1 -0
  93. package/dist/approvals/authorization.d.ts +5 -0
  94. package/dist/approvals/authorization.d.ts.map +1 -0
  95. package/dist/approvals/authorization.js +16 -0
  96. package/dist/approvals/authorization.js.map +1 -0
  97. package/dist/approvals/digest.d.ts +3 -0
  98. package/dist/approvals/digest.d.ts.map +1 -0
  99. package/dist/approvals/digest.js +21 -0
  100. package/dist/approvals/digest.js.map +1 -0
  101. package/dist/approvals/gate.d.ts +15 -0
  102. package/dist/approvals/gate.d.ts.map +1 -0
  103. package/dist/approvals/gate.js +102 -0
  104. package/dist/approvals/gate.js.map +1 -0
  105. package/dist/approvals/host-runtime.d.ts +16 -0
  106. package/dist/approvals/host-runtime.d.ts.map +1 -0
  107. package/dist/approvals/host-runtime.js +12 -0
  108. package/dist/approvals/host-runtime.js.map +1 -0
  109. package/dist/approvals/index.d.ts +19 -0
  110. package/dist/approvals/index.d.ts.map +1 -0
  111. package/dist/approvals/index.js +13 -0
  112. package/dist/approvals/index.js.map +1 -0
  113. package/dist/approvals/memory-store.d.ts +3 -0
  114. package/dist/approvals/memory-store.d.ts.map +1 -0
  115. package/dist/approvals/memory-store.js +33 -0
  116. package/dist/approvals/memory-store.js.map +1 -0
  117. package/dist/approvals/schema.d.ts +484 -0
  118. package/dist/approvals/schema.d.ts.map +1 -0
  119. package/dist/approvals/schema.js +55 -0
  120. package/dist/approvals/schema.js.map +1 -0
  121. package/dist/approvals/service.d.ts +17 -0
  122. package/dist/approvals/service.d.ts.map +1 -0
  123. package/dist/approvals/service.js +240 -0
  124. package/dist/approvals/service.js.map +1 -0
  125. package/dist/approvals/sql-store.d.ts +9 -0
  126. package/dist/approvals/sql-store.d.ts.map +1 -0
  127. package/dist/approvals/sql-store.js +179 -0
  128. package/dist/approvals/sql-store.js.map +1 -0
  129. package/dist/approvals/types.d.ts +156 -0
  130. package/dist/approvals/types.d.ts.map +1 -0
  131. package/dist/approvals/types.js +32 -0
  132. package/dist/approvals/types.js.map +1 -0
  133. package/dist/approvals/wait.d.ts +10 -0
  134. package/dist/approvals/wait.d.ts.map +1 -0
  135. package/dist/approvals/wait.js +11 -0
  136. package/dist/approvals/wait.js.map +1 -0
  137. package/dist/cli/cli.d.ts.map +1 -1
  138. package/dist/cli/cli.js +2 -1
  139. package/dist/cli/cli.js.map +1 -1
  140. package/dist/cli/commands/api.d.ts +65 -0
  141. package/dist/cli/commands/api.d.ts.map +1 -1
  142. package/dist/cli/commands/api.js +79 -4
  143. package/dist/cli/commands/api.js.map +1 -1
  144. package/dist/cli/commands/compile-flows.d.ts +9 -0
  145. package/dist/cli/commands/compile-flows.d.ts.map +1 -0
  146. package/dist/cli/commands/compile-flows.js +55 -0
  147. package/dist/cli/commands/compile-flows.js.map +1 -0
  148. package/dist/cli/commands/dev.d.ts +6 -0
  149. package/dist/cli/commands/dev.d.ts.map +1 -1
  150. package/dist/cli/commands/dev.js +22 -5
  151. package/dist/cli/commands/dev.js.map +1 -1
  152. package/dist/cli/commands/e2e.d.ts +5 -0
  153. package/dist/cli/commands/e2e.d.ts.map +1 -1
  154. package/dist/cli/commands/e2e.js +42 -3
  155. package/dist/cli/commands/e2e.js.map +1 -1
  156. package/dist/cli/commands/flow.d.ts.map +1 -1
  157. package/dist/cli/commands/flow.js +12 -3
  158. package/dist/cli/commands/flow.js.map +1 -1
  159. package/dist/cli/commands/generate-openapi-from-zod.d.ts +26 -0
  160. package/dist/cli/commands/generate-openapi-from-zod.d.ts.map +1 -0
  161. package/dist/cli/commands/generate-openapi-from-zod.js +189 -0
  162. package/dist/cli/commands/generate-openapi-from-zod.js.map +1 -0
  163. package/dist/cli/commands/generate.d.ts +10 -4
  164. package/dist/cli/commands/generate.d.ts.map +1 -1
  165. package/dist/cli/commands/generate.js +27 -70
  166. package/dist/cli/commands/generate.js.map +1 -1
  167. package/dist/cli/commands/index.d.ts +1 -0
  168. package/dist/cli/commands/index.d.ts.map +1 -1
  169. package/dist/cli/commands/index.js +2 -0
  170. package/dist/cli/commands/index.js.map +1 -1
  171. package/dist/cli/commands/mcp.d.ts +5 -0
  172. package/dist/cli/commands/mcp.d.ts.map +1 -1
  173. package/dist/cli/commands/mcp.js +28 -2
  174. package/dist/cli/commands/mcp.js.map +1 -1
  175. package/dist/cli/commands/migrate.d.ts +1 -0
  176. package/dist/cli/commands/migrate.d.ts.map +1 -1
  177. package/dist/cli/commands/migrate.js +145 -34
  178. package/dist/cli/commands/migrate.js.map +1 -1
  179. package/dist/cli/commands/openapi-3-1.d.ts +34 -0
  180. package/dist/cli/commands/openapi-3-1.d.ts.map +1 -0
  181. package/dist/cli/commands/openapi-3-1.js +333 -0
  182. package/dist/cli/commands/openapi-3-1.js.map +1 -0
  183. package/dist/cli/commands/start.d.ts +5 -0
  184. package/dist/cli/commands/start.d.ts.map +1 -1
  185. package/dist/cli/commands/start.js +20 -2
  186. package/dist/cli/commands/start.js.map +1 -1
  187. package/dist/cli/commands/ui.d.ts +11 -0
  188. package/dist/cli/commands/ui.d.ts.map +1 -1
  189. package/dist/cli/commands/ui.js +105 -20
  190. package/dist/cli/commands/ui.js.map +1 -1
  191. package/dist/cli/commands/verify.d.ts.map +1 -1
  192. package/dist/cli/commands/verify.js +2 -0
  193. package/dist/cli/commands/verify.js.map +1 -1
  194. package/dist/cli/commands/worker.d.ts +5 -0
  195. package/dist/cli/commands/worker.d.ts.map +1 -1
  196. package/dist/cli/commands/worker.js +18 -2
  197. package/dist/cli/commands/worker.js.map +1 -1
  198. package/dist/cli/index.d.ts +1 -1
  199. package/dist/cli/index.d.ts.map +1 -1
  200. package/dist/cli/index.js +1 -1
  201. package/dist/cli/index.js.map +1 -1
  202. package/dist/credentials/catalog.d.ts +71 -0
  203. package/dist/credentials/catalog.d.ts.map +1 -0
  204. package/dist/credentials/catalog.js +293 -0
  205. package/dist/credentials/catalog.js.map +1 -0
  206. package/dist/credentials/index.d.ts +3 -0
  207. package/dist/credentials/index.d.ts.map +1 -0
  208. package/dist/credentials/index.js +2 -0
  209. package/dist/credentials/index.js.map +1 -0
  210. package/dist/data/drift-inspector.d.ts +1 -4
  211. package/dist/data/drift-inspector.d.ts.map +1 -1
  212. package/dist/data/drift-inspector.js +39 -5
  213. package/dist/data/drift-inspector.js.map +1 -1
  214. package/dist/data/field-encryption.d.ts +7 -0
  215. package/dist/data/field-encryption.d.ts.map +1 -1
  216. package/dist/data/field-encryption.js +35 -17
  217. package/dist/data/field-encryption.js.map +1 -1
  218. package/dist/data/framework-migration-rewriter.js +1 -1
  219. package/dist/data/framework-migration-rewriter.js.map +1 -1
  220. package/dist/data/index.d.ts +1 -1
  221. package/dist/data/index.d.ts.map +1 -1
  222. package/dist/data/index.js +1 -1
  223. package/dist/data/index.js.map +1 -1
  224. package/dist/data/mask-fields.d.ts +2 -0
  225. package/dist/data/mask-fields.d.ts.map +1 -1
  226. package/dist/data/mask-fields.js +6 -0
  227. package/dist/data/mask-fields.js.map +1 -1
  228. package/dist/data/migration.d.ts.map +1 -1
  229. package/dist/data/migration.js +25 -1
  230. package/dist/data/migration.js.map +1 -1
  231. package/dist/data/repository.d.ts.map +1 -1
  232. package/dist/data/repository.js +15 -3
  233. package/dist/data/repository.js.map +1 -1
  234. package/dist/data/schema-generator.d.ts +95 -3
  235. package/dist/data/schema-generator.d.ts.map +1 -1
  236. package/dist/data/schema-generator.js +122 -6
  237. package/dist/data/schema-generator.js.map +1 -1
  238. package/dist/define/defineCapability.d.ts +7 -0
  239. package/dist/define/defineCapability.d.ts.map +1 -1
  240. package/dist/define/defineCapability.js +15 -0
  241. package/dist/define/defineCapability.js.map +1 -1
  242. package/dist/define/defineFlow.d.ts +3 -1
  243. package/dist/define/defineFlow.d.ts.map +1 -1
  244. package/dist/define/defineFlow.js.map +1 -1
  245. package/dist/durable/apply-ddl.d.ts +13 -0
  246. package/dist/durable/apply-ddl.d.ts.map +1 -0
  247. package/dist/durable/apply-ddl.js +278 -0
  248. package/dist/durable/apply-ddl.js.map +1 -0
  249. package/dist/durable/harness.d.ts +31 -0
  250. package/dist/durable/harness.d.ts.map +1 -0
  251. package/dist/durable/harness.js +136 -0
  252. package/dist/durable/harness.js.map +1 -0
  253. package/dist/durable/index.d.ts +18 -0
  254. package/dist/durable/index.d.ts.map +1 -0
  255. package/dist/durable/index.js +16 -0
  256. package/dist/durable/index.js.map +1 -0
  257. package/dist/durable/memory-store.d.ts +48 -0
  258. package/dist/durable/memory-store.d.ts.map +1 -0
  259. package/dist/durable/memory-store.js +260 -0
  260. package/dist/durable/memory-store.js.map +1 -0
  261. package/dist/durable/migrations-path.d.ts +5 -0
  262. package/dist/durable/migrations-path.d.ts.map +1 -0
  263. package/dist/durable/migrations-path.js +8 -0
  264. package/dist/durable/migrations-path.js.map +1 -0
  265. package/dist/durable/opaque-dispatch.d.ts +7 -0
  266. package/dist/durable/opaque-dispatch.d.ts.map +1 -0
  267. package/dist/durable/opaque-dispatch.js +67 -0
  268. package/dist/durable/opaque-dispatch.js.map +1 -0
  269. package/dist/durable/persist-before-ack.d.ts +52 -0
  270. package/dist/durable/persist-before-ack.d.ts.map +1 -0
  271. package/dist/durable/persist-before-ack.js +153 -0
  272. package/dist/durable/persist-before-ack.js.map +1 -0
  273. package/dist/durable/pg-env.d.ts +9 -0
  274. package/dist/durable/pg-env.d.ts.map +1 -0
  275. package/dist/durable/pg-env.js +20 -0
  276. package/dist/durable/pg-env.js.map +1 -0
  277. package/dist/durable/postgres-persist.d.ts +33 -0
  278. package/dist/durable/postgres-persist.d.ts.map +1 -0
  279. package/dist/durable/postgres-persist.js +239 -0
  280. package/dist/durable/postgres-persist.js.map +1 -0
  281. package/dist/durable/reconciliation.d.ts +20 -0
  282. package/dist/durable/reconciliation.d.ts.map +1 -0
  283. package/dist/durable/reconciliation.js +91 -0
  284. package/dist/durable/reconciliation.js.map +1 -0
  285. package/dist/durable/schema.d.ts +1448 -0
  286. package/dist/durable/schema.d.ts.map +1 -0
  287. package/dist/durable/schema.js +138 -0
  288. package/dist/durable/schema.js.map +1 -0
  289. package/dist/durable/spine-claim.d.ts +16 -0
  290. package/dist/durable/spine-claim.d.ts.map +1 -0
  291. package/dist/durable/spine-claim.js +131 -0
  292. package/dist/durable/spine-claim.js.map +1 -0
  293. package/dist/durable/types.d.ts +153 -0
  294. package/dist/durable/types.d.ts.map +1 -0
  295. package/dist/durable/types.js +114 -0
  296. package/dist/durable/types.js.map +1 -0
  297. package/dist/errors/data-errors.d.ts +6 -0
  298. package/dist/errors/data-errors.d.ts.map +1 -1
  299. package/dist/errors/data-errors.js +9 -0
  300. package/dist/errors/data-errors.js.map +1 -1
  301. package/dist/errors/index.d.ts +4 -1
  302. package/dist/errors/index.d.ts.map +1 -1
  303. package/dist/errors/index.js +11 -1
  304. package/dist/errors/index.js.map +1 -1
  305. package/dist/errors-entry.d.ts +1 -1
  306. package/dist/errors-entry.d.ts.map +1 -1
  307. package/dist/errors-entry.js +1 -1
  308. package/dist/errors-entry.js.map +1 -1
  309. package/dist/events/consumer-registry.d.ts +6 -0
  310. package/dist/events/consumer-registry.d.ts.map +1 -1
  311. package/dist/events/consumer-registry.js.map +1 -1
  312. package/dist/events/dispatcher.d.ts +19 -0
  313. package/dist/events/dispatcher.d.ts.map +1 -1
  314. package/dist/events/dispatcher.js +161 -114
  315. package/dist/events/dispatcher.js.map +1 -1
  316. package/dist/events/idempotency.d.ts +3 -2
  317. package/dist/events/idempotency.d.ts.map +1 -1
  318. package/dist/events/idempotency.js +3 -2
  319. package/dist/events/idempotency.js.map +1 -1
  320. package/dist/events/index.d.ts +2 -0
  321. package/dist/events/index.d.ts.map +1 -1
  322. package/dist/events/index.js +1 -0
  323. package/dist/events/index.js.map +1 -1
  324. package/dist/events/subscription.d.ts +29 -0
  325. package/dist/events/subscription.d.ts.map +1 -0
  326. package/dist/events/subscription.js +19 -0
  327. package/dist/events/subscription.js.map +1 -0
  328. package/dist/events/worker.d.ts +8 -1
  329. package/dist/events/worker.d.ts.map +1 -1
  330. package/dist/events/worker.js +32 -5
  331. package/dist/events/worker.js.map +1 -1
  332. package/dist/execution/capability-executor.d.ts +6 -3
  333. package/dist/execution/capability-executor.d.ts.map +1 -1
  334. package/dist/execution/capability-executor.js +34 -6
  335. package/dist/execution/capability-executor.js.map +1 -1
  336. package/dist/execution/context-deps.d.ts +2 -0
  337. package/dist/execution/context-deps.d.ts.map +1 -1
  338. package/dist/execution/context-deps.js +2 -1
  339. package/dist/execution/context-deps.js.map +1 -1
  340. package/dist/execution/context-factory.d.ts +38 -1
  341. package/dist/execution/context-factory.d.ts.map +1 -1
  342. package/dist/execution/context-factory.js +102 -4
  343. package/dist/execution/context-factory.js.map +1 -1
  344. package/dist/execution/index.d.ts +2 -2
  345. package/dist/execution/index.d.ts.map +1 -1
  346. package/dist/execution/index.js +1 -1
  347. package/dist/execution/index.js.map +1 -1
  348. package/dist/execution/transactional-outbox.d.ts +10 -0
  349. package/dist/execution/transactional-outbox.d.ts.map +1 -1
  350. package/dist/execution/transactional-outbox.js +41 -2
  351. package/dist/execution/transactional-outbox.js.map +1 -1
  352. package/dist/flows/budget.d.ts +44 -0
  353. package/dist/flows/budget.d.ts.map +1 -0
  354. package/dist/flows/budget.js +81 -0
  355. package/dist/flows/budget.js.map +1 -0
  356. package/dist/flows/compile-flow.d.ts +23 -0
  357. package/dist/flows/compile-flow.d.ts.map +1 -0
  358. package/dist/flows/compile-flow.js +176 -0
  359. package/dist/flows/compile-flow.js.map +1 -0
  360. package/dist/flows/compiled-registry.d.ts +43 -0
  361. package/dist/flows/compiled-registry.d.ts.map +1 -0
  362. package/dist/flows/compiled-registry.js +198 -0
  363. package/dist/flows/compiled-registry.js.map +1 -0
  364. package/dist/flows/dead-letter.d.ts +16 -0
  365. package/dist/flows/dead-letter.d.ts.map +1 -1
  366. package/dist/flows/dead-letter.js +67 -0
  367. package/dist/flows/dead-letter.js.map +1 -1
  368. package/dist/flows/definition-strategy.d.ts +13 -0
  369. package/dist/flows/definition-strategy.d.ts.map +1 -0
  370. package/dist/flows/definition-strategy.js +30 -0
  371. package/dist/flows/definition-strategy.js.map +1 -0
  372. package/dist/flows/engine.d.ts +38 -1
  373. package/dist/flows/engine.d.ts.map +1 -1
  374. package/dist/flows/engine.js +375 -32
  375. package/dist/flows/engine.js.map +1 -1
  376. package/dist/flows/index.d.ts +12 -4
  377. package/dist/flows/index.d.ts.map +1 -1
  378. package/dist/flows/index.js +7 -2
  379. package/dist/flows/index.js.map +1 -1
  380. package/dist/flows/scheduler.d.ts +26 -0
  381. package/dist/flows/scheduler.d.ts.map +1 -1
  382. package/dist/flows/scheduler.js +157 -37
  383. package/dist/flows/scheduler.js.map +1 -1
  384. package/dist/flows/schema.d.ts +34 -0
  385. package/dist/flows/schema.d.ts.map +1 -1
  386. package/dist/flows/schema.js +4 -0
  387. package/dist/flows/schema.js.map +1 -1
  388. package/dist/flows/step-executor.d.ts +3 -0
  389. package/dist/flows/step-executor.d.ts.map +1 -1
  390. package/dist/flows/step-executor.js +33 -0
  391. package/dist/flows/step-executor.js.map +1 -1
  392. package/dist/governance/capability-source-scan.d.ts.map +1 -1
  393. package/dist/governance/capability-source-scan.js +2 -29
  394. package/dist/governance/capability-source-scan.js.map +1 -1
  395. package/dist/governance/forbidden-path-scan.d.ts +7 -0
  396. package/dist/governance/forbidden-path-scan.d.ts.map +1 -0
  397. package/dist/governance/forbidden-path-scan.js +60 -0
  398. package/dist/governance/forbidden-path-scan.js.map +1 -0
  399. package/dist/governance/index.d.ts +2 -0
  400. package/dist/governance/index.d.ts.map +1 -1
  401. package/dist/governance/index.js +2 -0
  402. package/dist/governance/index.js.map +1 -1
  403. package/dist/governance/source-walk.d.ts +3 -0
  404. package/dist/governance/source-walk.d.ts.map +1 -0
  405. package/dist/governance/source-walk.js +33 -0
  406. package/dist/governance/source-walk.js.map +1 -0
  407. package/dist/index.d.ts +21 -12
  408. package/dist/index.d.ts.map +1 -1
  409. package/dist/index.js +22 -9
  410. package/dist/index.js.map +1 -1
  411. package/dist/runtime/bootstrap.d.ts +3 -1
  412. package/dist/runtime/bootstrap.d.ts.map +1 -1
  413. package/dist/runtime/load-extensions.d.ts.map +1 -1
  414. package/dist/runtime/load-extensions.js +11 -0
  415. package/dist/runtime/load-extensions.js.map +1 -1
  416. package/dist/runtime/register-consumers.d.ts +3 -0
  417. package/dist/runtime/register-consumers.d.ts.map +1 -1
  418. package/dist/runtime/register-consumers.js +4 -0
  419. package/dist/runtime/register-consumers.js.map +1 -1
  420. package/dist/runtime/start-worker-pool.d.ts.map +1 -1
  421. package/dist/runtime/start-worker-pool.js +1 -0
  422. package/dist/runtime/start-worker-pool.js.map +1 -1
  423. package/dist/runtime-entry.d.ts +4 -0
  424. package/dist/runtime-entry.d.ts.map +1 -0
  425. package/dist/runtime-entry.js +30 -0
  426. package/dist/runtime-entry.js.map +1 -0
  427. package/dist/server/bootstrap.d.ts +116 -8
  428. package/dist/server/bootstrap.d.ts.map +1 -1
  429. package/dist/server/bootstrap.js +139 -62
  430. package/dist/server/bootstrap.js.map +1 -1
  431. package/dist/tenancy/data-plane-connection.d.ts +108 -0
  432. package/dist/tenancy/data-plane-connection.d.ts.map +1 -0
  433. package/dist/tenancy/data-plane-connection.js +320 -0
  434. package/dist/tenancy/data-plane-connection.js.map +1 -0
  435. package/dist/tenancy/data-plane-migrate.d.ts +30 -0
  436. package/dist/tenancy/data-plane-migrate.d.ts.map +1 -0
  437. package/dist/tenancy/data-plane-migrate.js +44 -0
  438. package/dist/tenancy/data-plane-migrate.js.map +1 -0
  439. package/dist/tenancy/data-plane-provisioning.d.ts +135 -0
  440. package/dist/tenancy/data-plane-provisioning.d.ts.map +1 -0
  441. package/dist/tenancy/data-plane-provisioning.js +639 -0
  442. package/dist/tenancy/data-plane-provisioning.js.map +1 -0
  443. package/dist/tenancy/data-plane-resolver.d.ts +37 -0
  444. package/dist/tenancy/data-plane-resolver.d.ts.map +1 -0
  445. package/dist/tenancy/data-plane-resolver.js +302 -0
  446. package/dist/tenancy/data-plane-resolver.js.map +1 -0
  447. package/dist/tenancy/index.d.ts +9 -0
  448. package/dist/tenancy/index.d.ts.map +1 -0
  449. package/dist/tenancy/index.js +22 -0
  450. package/dist/tenancy/index.js.map +1 -0
  451. package/dist/tenancy/types.d.ts +117 -0
  452. package/dist/tenancy/types.d.ts.map +1 -0
  453. package/dist/tenancy/types.js +8 -0
  454. package/dist/tenancy/types.js.map +1 -0
  455. package/dist/testing/context.d.ts.map +1 -1
  456. package/dist/testing/context.js +6 -1
  457. package/dist/testing/context.js.map +1 -1
  458. package/dist/testing/crash-matrix-simulation.d.ts +51 -0
  459. package/dist/testing/crash-matrix-simulation.d.ts.map +1 -0
  460. package/dist/testing/crash-matrix-simulation.js +350 -0
  461. package/dist/testing/crash-matrix-simulation.js.map +1 -0
  462. package/dist/testing/scaffolding.d.ts.map +1 -1
  463. package/dist/testing/scaffolding.js +4 -1
  464. package/dist/testing/scaffolding.js.map +1 -1
  465. package/dist/types/capability.d.ts +16 -0
  466. package/dist/types/capability.d.ts.map +1 -1
  467. package/dist/types/capability.js.map +1 -1
  468. package/dist/types/context.d.ts +62 -0
  469. package/dist/types/context.d.ts.map +1 -1
  470. package/dist/types/enums.d.ts +2 -0
  471. package/dist/types/enums.d.ts.map +1 -1
  472. package/dist/types/enums.js +2 -0
  473. package/dist/types/enums.js.map +1 -1
  474. package/dist/types/fields.d.ts +7 -0
  475. package/dist/types/fields.d.ts.map +1 -1
  476. package/dist/types/flow.d.ts +96 -1
  477. package/dist/types/flow.d.ts.map +1 -1
  478. package/dist/types/flow.js +7 -1
  479. package/dist/types/flow.js.map +1 -1
  480. package/dist/types/index.d.ts +9 -6
  481. package/dist/types/index.d.ts.map +1 -1
  482. package/dist/types/index.js +5 -1
  483. package/dist/types/index.js.map +1 -1
  484. package/dist/types/prompt.d.ts +8 -0
  485. package/dist/types/prompt.d.ts.map +1 -1
  486. package/dist/types/prompt.js +7 -0
  487. package/dist/types/prompt.js.map +1 -1
  488. package/dist/worker/bootstrap.d.ts +100 -1
  489. package/dist/worker/bootstrap.d.ts.map +1 -1
  490. package/dist/worker/bootstrap.js +155 -14
  491. package/dist/worker/bootstrap.js.map +1 -1
  492. package/instructions/api.md +21 -0
  493. package/instructions/cli.md +15 -9
  494. package/instructions/deployment.md +27 -25
  495. package/instructions/flows.md +8 -1
  496. package/instructions/framework.md +2 -1
  497. package/instructions/mcp.md +1 -1
  498. package/instructions/peer-dependencies.md +16 -14
  499. package/instructions/prompts.md +3 -0
  500. package/instructions/security.md +4 -0
  501. package/instructions/testing.md +2 -2
  502. package/migrations/durable-tenant/0000_durable_tenant.sql +92 -0
  503. package/migrations/durable-tenant/0001_human_task.sql +45 -0
  504. package/migrations/durable-tenant/meta/_journal.json +20 -0
  505. package/migrations/spine/0000_opaque_dispatch.sql +26 -0
  506. package/migrations/spine/meta/_journal.json +13 -0
  507. package/package.json +26 -8
@@ -3,8 +3,13 @@ import { and, eq, inArray, isNull, lte, or, sql } from 'drizzle-orm';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { hostname } from 'node:os';
5
5
  import { parseDurationToMs } from '../config/duration.js';
6
- import { FlowCancelledError, LeaseLostError } from '../errors/index.js';
7
- import { BackoffStrategy } from '../types/enums.js';
6
+ import { FlowCancelledError, LeaseLostError, PlumbusError } from '../errors/index.js';
7
+ import { BUDGET_EXHAUSTED, consumeExecutionBudget, createExecutionBudgetLedger, writeExecutionBudget, } from './budget.js';
8
+ import { BackoffStrategy, ErrorCode, FlowStepType } from '../types/enums.js';
9
+ import { FRAMEWORK_SCHEMA } from '../data/schema-generator.js';
10
+ import { ackSpineDispatch, casAdvanceExecution, claimSpineDispatch, insertDispatchOutbox, loadExecutionState, persistAcceptanceOnDb, publishOutboxToSpine, } from '../durable/index.js';
11
+ import { flowDefinitionId, hydrateCompiledFlow } from './compile-flow.js';
12
+ import { assertSupportedDefinitionStrategy, DefinitionInFlightStrategy, } from './definition-strategy.js';
8
13
  import { flowExecutionsTable } from './schema.js';
9
14
  import { assertTransition, FlowStatus, isTerminal, StepStatus, } from './state-machine.js';
10
15
  import { buildHistoryEntry, executeStep, } from './step-executor.js';
@@ -68,13 +73,71 @@ export function generateWorkerId() {
68
73
  * across multiple workers sharing the same database.
69
74
  */
70
75
  export function createFlowEngine(config) {
71
- const { db, registry, stepDeps, audit, onFlowError, createEventService } = config;
76
+ const { db, registry, audit, onFlowError, createEventService } = config;
77
+ const compiledRegistry = config.compiledRegistry;
78
+ const approvals = config.approvals;
79
+ const stepDeps = {
80
+ ...config.stepDeps,
81
+ findApprovalForExecution: config.stepDeps.findApprovalForExecution ??
82
+ (approvals ? (executionId) => approvals.findByExecutionId(executionId) : undefined),
83
+ };
72
84
  const workerId = config.workerId ?? generateWorkerId();
73
85
  const leaseDurationMs = config.flowLeaseDurationMs ?? DEFAULT_LEASE_DURATION_MS;
74
86
  const heartbeatIntervalMs = config.flowHeartbeatIntervalMs ?? Math.floor(leaseDurationMs / 3);
75
87
  const claimBatchSize = config.flowClaimBatchSize ?? DEFAULT_CLAIM_BATCH_SIZE;
76
88
  const logger = config.logger;
77
89
  const onFlowStepEnqueue = config.onFlowStepEnqueue;
90
+ const spineDispatch = config.spineDispatch;
91
+ const durableSchema = spineDispatch?.coreSchema ?? FRAMEWORK_SCHEMA;
92
+ const executionStores = new Map();
93
+ const claimedHints = new Map();
94
+ const executionPins = new Map();
95
+ function restorePinFromRow(row) {
96
+ if (executionPins.has(row.id))
97
+ return;
98
+ if (!row.definitionVersion)
99
+ return;
100
+ executionPins.set(row.id, {
101
+ flowDefinitionId: flowDefinitionId({ domain: row.domain, name: row.flowName }),
102
+ definitionVersion: row.definitionVersion,
103
+ definitionDigest: row.definitionDigest ?? undefined,
104
+ });
105
+ }
106
+ function resolveCompiledForStart(flow) {
107
+ if (!compiledRegistry)
108
+ return undefined;
109
+ const id = flowDefinitionId(flow);
110
+ const compiled = compiledRegistry.getLatest(id);
111
+ if (!compiled) {
112
+ throw new PlumbusError(ErrorCode.NotFound, `compiled definition required for "${id}"`, {
113
+ reason: 'compiled-definition-required',
114
+ });
115
+ }
116
+ return compiled;
117
+ }
118
+ function resolveExecutionFlow(executionId, flowName) {
119
+ const authoring = registry.get(flowName);
120
+ if (!authoring) {
121
+ throw new Error(`Flow "${flowName}" is not registered`);
122
+ }
123
+ if (!compiledRegistry)
124
+ return authoring;
125
+ const pin = executionPins.get(executionId);
126
+ if (!pin) {
127
+ throw new PlumbusError(ErrorCode.NotFound, `Flow "${flowName}" has no pinned compiled definition`, { reason: 'compiled-definition-missing', executionId });
128
+ }
129
+ const compiled = compiledRegistry.get(pin.flowDefinitionId, pin.definitionVersion);
130
+ if (!compiled) {
131
+ throw new PlumbusError(ErrorCode.NotFound, `compiled definition "${pin.flowDefinitionId}@${pin.definitionVersion}" is not in the registry`, { reason: 'compiled-definition-missing' });
132
+ }
133
+ if (pin.definitionDigest && compiled.definitionDigest !== pin.definitionDigest) {
134
+ throw new PlumbusError(ErrorCode.Conflict, `compiled definition digest mismatch for "${pin.flowDefinitionId}@${pin.definitionVersion}"`, { reason: 'compiled-definition-digest-mismatch' });
135
+ }
136
+ return hydrateCompiledFlow(compiled, authoring);
137
+ }
138
+ function storeFor(executionId) {
139
+ return executionStores.get(executionId) ?? db;
140
+ }
78
141
  const onFlowDelayedSchedule = config.onFlowDelayedSchedule;
79
142
  async function maybeEnqueueFlowStep(executionId, correlationId) {
80
143
  if (onFlowStepEnqueue) {
@@ -103,26 +166,56 @@ export function createFlowEngine(config) {
103
166
  throw new Error(`Flow "${flowName}": invalid input — ${parseResult.error.message}`);
104
167
  }
105
168
  const executionId = opts?.executionId ?? randomUUID();
106
- const initialState = flow.state
169
+ const compiled = resolveCompiledForStart(flow);
170
+ const executionFlow = compiled ? hydrateCompiledFlow(compiled, flow) : flow;
171
+ const definitionVersion = compiled?.definitionVersion ?? '0.0.0';
172
+ if (compiled) {
173
+ executionPins.set(executionId, {
174
+ flowDefinitionId: compiled.flowDefinitionId,
175
+ definitionVersion: compiled.definitionVersion,
176
+ definitionDigest: compiled.definitionDigest,
177
+ });
178
+ }
179
+ let initialState = flow.state
107
180
  ? (() => {
108
181
  const parsed = flow.state.safeParse({});
109
182
  return parsed.success ? parsed.data : {};
110
183
  })()
111
184
  : null;
112
- await db.insert(flowExecutionsTable).values({
185
+ if (flow.budget) {
186
+ initialState = writeExecutionBudget(initialState, createExecutionBudgetLedger({
187
+ executionId,
188
+ profileId: flow.budget.profileId,
189
+ allocated: flow.budget.allocated,
190
+ unitId: flow.budget.unitId,
191
+ dimensionId: flow.budget.dimensionId,
192
+ }));
193
+ }
194
+ let writeDb = db;
195
+ if (spineDispatch) {
196
+ if (!auth.tenantId) {
197
+ throw new PlumbusError(ErrorCode.Forbidden, 'spine dispatch requires a tenant reference on flow start', { reason: 'untenanted-flow-start' });
198
+ }
199
+ const handle = await spineDispatch.resolver.resolve(auth.tenantId);
200
+ writeDb = handle.db;
201
+ executionStores.set(executionId, writeDb);
202
+ }
203
+ await writeDb.insert(flowExecutionsTable).values({
113
204
  id: executionId,
114
205
  flowName,
115
206
  domain: flow.domain,
116
207
  status: FlowStatus.Created,
117
208
  input: parseResult.data,
118
209
  state: initialState,
119
- currentStep: flow.steps[0]?.name ?? null,
210
+ currentStep: executionFlow.steps[0]?.name ?? null,
120
211
  stepHistory: [],
121
212
  actor: auth.userId ?? 'system',
122
213
  tenantId: auth.tenantId ?? null,
123
214
  authSnapshotJson: flowAuthSchema.parse(auth),
124
215
  correlationId: opts?.correlationId ?? null,
125
216
  triggerEventId: opts?.triggerEventId ?? null,
217
+ definitionVersion: compiled?.definitionVersion ?? null,
218
+ definitionDigest: compiled?.definitionDigest ?? null,
126
219
  });
127
220
  if (audit) {
128
221
  await audit.record(`flow.started.${flowName}`, {
@@ -134,6 +227,20 @@ export function createFlowEngine(config) {
134
227
  });
135
228
  }
136
229
  await maybeEnqueueFlowStep(executionId, opts?.correlationId);
230
+ if (spineDispatch && auth.tenantId) {
231
+ const nowIso = new Date().toISOString();
232
+ const firstStepId = executionFlow.steps[0]?.name ?? 'terminal';
233
+ const accepted = await persistAcceptanceOnDb(writeDb, {
234
+ executionId,
235
+ tenantRef: auth.tenantId,
236
+ definitionId: flowName,
237
+ definitionVersion,
238
+ firstStepId,
239
+ correlationId: opts?.correlationId ?? executionId,
240
+ nowIso,
241
+ }, durableSchema);
242
+ await publishOutboxToSpine(writeDb, spineDispatch.db, accepted.outbox, `disp:${executionId}:1`, nowIso, durableSchema);
243
+ }
137
244
  return {
138
245
  id: executionId,
139
246
  flowName,
@@ -145,7 +252,67 @@ export function createFlowEngine(config) {
145
252
  * Uses FOR UPDATE SKIP LOCKED so concurrent workers each get different rows.
146
253
  * Also reclaims rows whose lease has expired (crash recovery).
147
254
  */
255
+ async function claimNextFromSpine(batchSize) {
256
+ if (!spineDispatch)
257
+ return [];
258
+ const hints = await claimSpineDispatch(spineDispatch.db, {
259
+ workerId,
260
+ leaseDurationMs,
261
+ limit: batchSize ?? claimBatchSize,
262
+ });
263
+ const rows = [];
264
+ for (const hint of hints) {
265
+ try {
266
+ const handle = await spineDispatch.resolver.resolve(hint.tenantRouteId);
267
+ const tenantState = await loadExecutionState(handle.db, hint.executionId, durableSchema);
268
+ if (!tenantState ||
269
+ tenantState.terminal ||
270
+ tenantState.revision !== hint.expectedRevision ||
271
+ tenantState.tenantEpoch !== hint.tenantEpoch) {
272
+ await ackSpineDispatch(spineDispatch.db, hint.dispatchId);
273
+ continue;
274
+ }
275
+ const execRows = await handle.db
276
+ .select()
277
+ .from(flowExecutionsTable)
278
+ .where(eq(flowExecutionsTable.id, hint.executionId))
279
+ .limit(1);
280
+ const row = execRows[0];
281
+ if (!row) {
282
+ await ackSpineDispatch(spineDispatch.db, hint.dispatchId);
283
+ continue;
284
+ }
285
+ executionStores.set(hint.executionId, handle.db);
286
+ claimedHints.set(hint.executionId, hint);
287
+ await handle.db
288
+ .update(flowExecutionsTable)
289
+ .set({
290
+ status: FlowStatus.Running,
291
+ leaseOwner: workerId,
292
+ leaseExpiresAt: new Date(Date.now() + leaseDurationMs),
293
+ updatedAt: new Date(),
294
+ })
295
+ .where(eq(flowExecutionsTable.id, hint.executionId));
296
+ rows.push({
297
+ ...row,
298
+ status: FlowStatus.Running,
299
+ leaseOwner: workerId,
300
+ });
301
+ }
302
+ catch (error) {
303
+ logger?.error('spine claim failed closed for one dispatch', {
304
+ dispatchId: hint.dispatchId,
305
+ executionId: hint.executionId,
306
+ error: error instanceof Error ? error.message : String(error),
307
+ });
308
+ }
309
+ }
310
+ return rows;
311
+ }
148
312
  async function claimNext(batchSize) {
313
+ if (spineDispatch) {
314
+ return claimNextFromSpine(batchSize);
315
+ }
149
316
  const limit = batchSize ?? claimBatchSize;
150
317
  const leaseDurationInterval = `${leaseDurationMs} milliseconds`;
151
318
  const result = await db.execute(sql `
@@ -235,7 +402,7 @@ export function createFlowEngine(config) {
235
402
  */
236
403
  async function extendLease(executionId) {
237
404
  const leaseDurationInterval = `${leaseDurationMs} milliseconds`;
238
- const result = await db
405
+ const result = await storeFor(executionId)
239
406
  .update(flowExecutionsTable)
240
407
  .set({
241
408
  leaseExpiresAt: sql `now() + ${leaseDurationInterval}::interval`,
@@ -251,7 +418,7 @@ export function createFlowEngine(config) {
251
418
  // tell at a glance which dimension diverged. Best-effort: a failure
252
419
  // here must not break the heartbeat.
253
420
  try {
254
- const rows = await db
421
+ const rows = await storeFor(executionId)
255
422
  .select({
256
423
  status: flowExecutionsTable.status,
257
424
  leaseOwner: flowExecutionsTable.leaseOwner,
@@ -294,7 +461,12 @@ export function createFlowEngine(config) {
294
461
  * The execution must have been claimed by this worker (via claimNext) first.
295
462
  */
296
463
  async function runNext(executionId, ctx) {
297
- const rows = await db
464
+ const result = await runNextUnsynced(executionId, ctx);
465
+ await syncSpineAfterRun(executionId, result);
466
+ return result;
467
+ }
468
+ async function runNextUnsynced(executionId, ctx) {
469
+ const rows = await storeFor(executionId)
298
470
  .select()
299
471
  .from(flowExecutionsTable)
300
472
  .where(eq(flowExecutionsTable.id, executionId))
@@ -306,10 +478,8 @@ export function createFlowEngine(config) {
306
478
  if (isTerminal(row.status)) {
307
479
  return { id: row.id, flowName: row.flowName, status: row.status };
308
480
  }
309
- const flow = registry.get(row.flowName);
310
- if (!flow) {
311
- throw new Error(`Flow "${row.flowName}" is not registered`);
312
- }
481
+ restorePinFromRow(row);
482
+ const flow = resolveExecutionFlow(executionId, row.flowName);
313
483
  // Do not auto-run event waits, and do not run delayed waits until due.
314
484
  if (row.status === FlowStatus.Waiting && row.waitingForEvent) {
315
485
  return { id: row.id, flowName: row.flowName, status: FlowStatus.Waiting };
@@ -340,6 +510,19 @@ export function createFlowEngine(config) {
340
510
  await failFlow(executionId, row.flowName, `Step "${currentStepName}" not found in flow definition`, row);
341
511
  return { id: executionId, flowName: row.flowName, status: FlowStatus.Failed };
342
512
  }
513
+ if (step.type === FlowStepType.Capability) {
514
+ const charged = consumeExecutionBudget(row.state, 1);
515
+ if (charged) {
516
+ row.state = charged.state;
517
+ if (charged.exhausted) {
518
+ const history = Array.isArray(row.stepHistory)
519
+ ? [...row.stepHistory]
520
+ : [];
521
+ return handleStepFailure(executionId, row, flow, history, BUDGET_EXHAUSTED);
522
+ }
523
+ await guardedUpdate(executionId, { state: charged.state });
524
+ }
525
+ }
343
526
  // Build flow-scoped context from stored auth snapshot (not worker systemAuth roles)
344
527
  const flowAuth = resolveFlowStepAuth(row);
345
528
  if (!flowAuth) {
@@ -641,7 +824,7 @@ export function createFlowEngine(config) {
641
824
  * Resume a waiting flow (e.g., after an event arrives or approval granted).
642
825
  */
643
826
  async function resume(executionId, signal) {
644
- const rows = await db
827
+ const rows = await storeFor(executionId)
645
828
  .select()
646
829
  .from(flowExecutionsTable)
647
830
  .where(eq(flowExecutionsTable.id, executionId))
@@ -652,9 +835,8 @@ export function createFlowEngine(config) {
652
835
  if (row.status !== FlowStatus.Waiting) {
653
836
  throw new Error(`Cannot resume flow "${executionId}" — current status is "${row.status}"`);
654
837
  }
655
- const flow = registry.get(row.flowName);
656
- if (!flow)
657
- throw new Error(`Flow "${row.flowName}" not registered`);
838
+ restorePinFromRow(row);
839
+ const flow = resolveExecutionFlow(executionId, row.flowName);
658
840
  // Advance past the wait/delay step — set to created so claimNext picks it up
659
841
  const nextStep = getNextStepName(flow.steps, row.currentStep ?? '');
660
842
  await updateExecution(executionId, {
@@ -716,10 +898,11 @@ export function createFlowEngine(config) {
716
898
  return rows.map((row) => row.id);
717
899
  }
718
900
  /**
719
- * Cancel a running or waiting flow.
901
+ * Cancel a running or waiting flow. Completed steps with `compensate`
902
+ * run their compensation capability first (recovery CompensationState v1).
720
903
  */
721
- async function cancel(executionId) {
722
- const rows = await db
904
+ async function cancel(executionId, opts) {
905
+ const rows = await storeFor(executionId)
723
906
  .select()
724
907
  .from(flowExecutionsTable)
725
908
  .where(eq(flowExecutionsTable.id, executionId))
@@ -730,30 +913,124 @@ export function createFlowEngine(config) {
730
913
  if (isTerminal(row.status)) {
731
914
  throw new Error(`Cannot cancel flow "${executionId}" — already in terminal state "${row.status}"`);
732
915
  }
916
+ restorePinFromRow(row);
917
+ const flow = resolveExecutionFlow(executionId, row.flowName);
918
+ const history = Array.isArray(row.stepHistory)
919
+ ? [...row.stepHistory]
920
+ : [];
921
+ // Abort the in-flight step before compensation so it cannot commit.
922
+ const activeAc = activeStepAborts.get(executionId);
923
+ if (activeAc && !activeAc.signal.aborted) {
924
+ activeAc.abort(new FlowCancelledError(executionId));
925
+ }
926
+ if (!opts?.skipCompensation) {
927
+ await runCompensations(executionId, row, flow, history);
928
+ }
733
929
  assertTransition(row.status, FlowStatus.Cancelled);
734
930
  await updateExecution(executionId, {
735
931
  status: FlowStatus.Cancelled,
932
+ stepHistory: history,
736
933
  completedAt: new Date(),
737
934
  });
738
- // Synchronously abort the step's AbortController if it is running in THIS
739
- // engine instance. Cross-process cancellations still converge via the next
740
- // heartbeat (which fails because status is no longer 'running').
741
- const activeAc = activeStepAborts.get(executionId);
742
- if (activeAc && !activeAc.signal.aborted) {
743
- activeAc.abort(new FlowCancelledError(executionId));
744
- }
745
935
  if (audit) {
746
936
  await audit.record(`flow.cancelled.${row.flowName}`, {
747
937
  executionId,
748
938
  flowName: row.flowName,
939
+ actor: opts?.actor ?? row.actor,
940
+ compensated: history.some((entry) => entry.step.startsWith('compensate:')),
749
941
  });
750
942
  }
751
943
  }
944
+ async function terminate(executionId, opts) {
945
+ return cancel(executionId, { actor: opts?.actor, skipCompensation: true });
946
+ }
947
+ async function runCompensations(executionId, row, flow, history) {
948
+ const completed = new Set(history.filter((entry) => entry.status === StepStatus.Completed).map((entry) => entry.step));
949
+ const compensable = [...flow.steps]
950
+ .reverse()
951
+ .filter((step) => {
952
+ return (step.type === FlowStepType.Capability &&
953
+ typeof step.compensate === 'string' &&
954
+ step.compensate.length > 0 &&
955
+ completed.has(step.name));
956
+ });
957
+ if (compensable.length === 0)
958
+ return;
959
+ // Compensations run as the verified initiating identity, never as the
960
+ // worker. A missing or invalid snapshot is recorded and left for operator
961
+ // review, matching the step path above.
962
+ const flowAuth = resolveFlowStepAuth(row);
963
+ if (!flowAuth) {
964
+ const at = new Date();
965
+ history.push(buildHistoryEntry('compensate', {
966
+ status: StepStatus.Failed,
967
+ error: 'Missing or invalid flow auth snapshot; compensations require a verified caller context',
968
+ }, at, at));
969
+ if (audit) {
970
+ await audit.record('flow.compensation_skipped', {
971
+ executionId,
972
+ flowName: row.flowName,
973
+ reason: 'invalid-auth-snapshot',
974
+ steps: compensable.map((step) => step.name),
975
+ });
976
+ }
977
+ return;
978
+ }
979
+ const ctx = {
980
+ auth: flowAuth,
981
+ data: config.createDataService ? config.createDataService(flowAuth) : {},
982
+ events: createEventService
983
+ ? createEventService(flowAuth)
984
+ : { emit: async () => undefined, emitMany: async () => undefined },
985
+ flows: {
986
+ start: async () => ({
987
+ id: executionId,
988
+ flowName: row.flowName,
989
+ status: FlowStatus.Cancelled,
990
+ }),
991
+ resume: async () => undefined,
992
+ cancel: async () => undefined,
993
+ status: async () => ({ id: executionId, flowName: row.flowName, status: row.status }),
994
+ },
995
+ ai: {},
996
+ audit: audit ?? { record: async () => undefined },
997
+ errors: {},
998
+ logger: logger ?? { debug() { }, info() { }, warn() { }, error() { } },
999
+ time: { now: () => new Date() },
1000
+ config: {},
1001
+ security: {},
1002
+ translations: { locale: 'en', t: (key) => key },
1003
+ state: row.state,
1004
+ flowId: executionId,
1005
+ };
1006
+ for (const step of compensable) {
1007
+ const startedAt = new Date();
1008
+ const result = await stepDeps.executeCapability(step.compensate, ctx, row.input);
1009
+ const completedAt = new Date();
1010
+ history.push(buildHistoryEntry(`compensate:${step.name}`, {
1011
+ status: result.success ? StepStatus.Completed : StepStatus.Failed,
1012
+ error: result.success
1013
+ ? undefined
1014
+ : typeof result.error === 'object' && result.error !== null
1015
+ ? JSON.stringify(result.error)
1016
+ : String(result.error),
1017
+ }, startedAt, completedAt));
1018
+ if (audit) {
1019
+ await audit.record(`flow.compensated.${step.name}`, {
1020
+ executionId,
1021
+ flowName: row.flowName,
1022
+ sourceStep: step.name,
1023
+ compensation: step.compensate,
1024
+ outcome: result.success ? 'succeeded' : 'failed',
1025
+ });
1026
+ }
1027
+ }
1028
+ }
752
1029
  /**
753
1030
  * Get the current status of a flow execution.
754
1031
  */
755
1032
  async function status(executionId) {
756
- const rows = await db
1033
+ const rows = await storeFor(executionId)
757
1034
  .select()
758
1035
  .from(flowExecutionsTable)
759
1036
  .where(eq(flowExecutionsTable.id, executionId))
@@ -820,7 +1097,7 @@ export function createFlowEngine(config) {
820
1097
  error: error ?? 'Unknown error',
821
1098
  tenantId: row?.tenantId,
822
1099
  actor: row?.actor,
823
- db,
1100
+ db: storeFor(executionId),
824
1101
  });
825
1102
  }
826
1103
  catch {
@@ -829,7 +1106,7 @@ export function createFlowEngine(config) {
829
1106
  }
830
1107
  }
831
1108
  async function updateExecution(id, updates) {
832
- await db
1109
+ await storeFor(id)
833
1110
  .update(flowExecutionsTable)
834
1111
  .set({ ...updates, updatedAt: new Date() })
835
1112
  .where(eq(flowExecutionsTable.id, id));
@@ -853,7 +1130,7 @@ export function createFlowEngine(config) {
853
1130
  */
854
1131
  async function markFailedFromRunner(executionId, error) {
855
1132
  const message = error instanceof Error ? error.message : typeof error === 'string' ? error : 'Unknown error';
856
- const result = await db
1133
+ const result = await storeFor(executionId)
857
1134
  .update(flowExecutionsTable)
858
1135
  .set({
859
1136
  status: FlowStatus.Failed,
@@ -884,13 +1161,77 @@ export function createFlowEngine(config) {
884
1161
  * false if the lease was lost (0 rows affected).
885
1162
  */
886
1163
  async function guardedUpdate(id, updates) {
887
- const result = await db
1164
+ const result = await storeFor(id)
888
1165
  .update(flowExecutionsTable)
889
1166
  .set({ ...updates, updatedAt: new Date() })
890
1167
  .where(and(eq(flowExecutionsTable.id, id), eq(flowExecutionsTable.leaseOwner, workerId)));
891
1168
  const rowsAffected = getRowsAffected(result);
892
1169
  return rowsAffected > 0;
893
1170
  }
1171
+ async function syncSpineAfterRun(executionId, result) {
1172
+ if (!spineDispatch)
1173
+ return;
1174
+ const hint = claimedHints.get(executionId);
1175
+ const tenantDb = executionStores.get(executionId);
1176
+ if (!hint || !tenantDb)
1177
+ return;
1178
+ const nowIso = new Date().toISOString();
1179
+ const flowRow = (await tenantDb
1180
+ .select({ currentStep: flowExecutionsTable.currentStep })
1181
+ .from(flowExecutionsTable)
1182
+ .where(eq(flowExecutionsTable.id, executionId))
1183
+ .limit(1))[0];
1184
+ const nextStepId = flowRow?.currentStep ?? hint.stepId;
1185
+ const terminal = result.status === FlowStatus.Completed ||
1186
+ result.status === FlowStatus.Failed ||
1187
+ result.status === FlowStatus.Cancelled;
1188
+ const cas = await casAdvanceExecution(tenantDb, {
1189
+ executionId,
1190
+ expectedRevision: hint.expectedRevision,
1191
+ nextStatus: terminal
1192
+ ? result.status === FlowStatus.Completed
1193
+ ? 'succeeded'
1194
+ : result.status === FlowStatus.Cancelled
1195
+ ? 'cancelled'
1196
+ : 'failed'
1197
+ : 'running',
1198
+ nextStepId,
1199
+ terminal,
1200
+ nowIso,
1201
+ sideEffectKey: `${executionId}:${hint.stepId}:${hint.expectedRevision}`,
1202
+ sideEffectLabel: `${executionId}:${hint.stepId}`,
1203
+ }, durableSchema);
1204
+ await ackSpineDispatch(spineDispatch.db, hint.dispatchId);
1205
+ claimedHints.delete(executionId);
1206
+ if (cas !== 'ok' || terminal)
1207
+ return;
1208
+ const advanced = await loadExecutionState(tenantDb, executionId, durableSchema);
1209
+ if (!advanced || advanced.terminal || !flowRow?.currentStep)
1210
+ return;
1211
+ const nextStep = flowRow.currentStep;
1212
+ const outbox = await insertDispatchOutbox(tenantDb, advanced, nextStep, nowIso, durableSchema);
1213
+ await publishOutboxToSpine(tenantDb, spineDispatch.db, outbox, `disp:${executionId}:${advanced.revision}`, nowIso, durableSchema);
1214
+ }
1215
+ async function applyDefinitionStrategy(executionId, strategy) {
1216
+ assertSupportedDefinitionStrategy(strategy);
1217
+ const rows = await storeFor(executionId)
1218
+ .select()
1219
+ .from(flowExecutionsTable)
1220
+ .where(eq(flowExecutionsTable.id, executionId))
1221
+ .limit(1);
1222
+ const row = rows[0];
1223
+ if (!row)
1224
+ throw new Error(`Flow execution "${executionId}" not found`);
1225
+ if (strategy === DefinitionInFlightStrategy.CompleteOnOriginal) {
1226
+ restorePinFromRow(row);
1227
+ return { strategy, applied: true };
1228
+ }
1229
+ if (!isTerminal(row.status)) {
1230
+ await cancel(executionId);
1231
+ }
1232
+ await updateExecution(executionId, { lastError: DefinitionInFlightStrategy.StopAndRecover });
1233
+ return { strategy: DefinitionInFlightStrategy.StopAndRecover, applied: true };
1234
+ }
894
1235
  return {
895
1236
  start,
896
1237
  runNext,
@@ -900,8 +1241,10 @@ export function createFlowEngine(config) {
900
1241
  claimNext,
901
1242
  claimExecution,
902
1243
  cancel,
1244
+ terminate,
903
1245
  status,
904
1246
  markFailedFromRunner,
1247
+ applyDefinitionStrategy,
905
1248
  get workerId() {
906
1249
  return workerId;
907
1250
  },