@lostgradient/weft 0.3.0 → 0.4.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 (508) hide show
  1. package/README.md +79 -24
  2. package/dist/cli/conformance.js +7 -1
  3. package/dist/cli/generated/operation-client.generated.d.ts +28 -13
  4. package/dist/cli/generated/operation-client.generated.js +1 -0
  5. package/dist/cli/help-text.d.ts +1 -1
  6. package/dist/cli/help-text.js +1 -0
  7. package/dist/cli/operation-catalog-snapshot.d.ts +8 -0
  8. package/dist/cli/operation-catalog-snapshot.js +11 -0
  9. package/dist/cli/parse-schedule-arguments.js +3 -1
  10. package/dist/cli/schedule.js +2 -1
  11. package/dist/cli/types.d.ts +1 -0
  12. package/dist/cli-main.js +79 -78
  13. package/dist/client/handle-delegation.d.ts +3 -2
  14. package/dist/client/handle-delegation.js +3 -1
  15. package/dist/client/http-client-requests.d.ts +2 -1
  16. package/dist/client/http-client-requests.js +7 -0
  17. package/dist/client/http-client.d.ts +9 -6
  18. package/dist/client/http-client.js +10 -1
  19. package/dist/client/http-request.d.ts +11 -1
  20. package/dist/client/http-request.js +17 -7
  21. package/dist/client/index.d.ts +1 -1
  22. package/dist/client/interface.d.ts +46 -5
  23. package/dist/client/local.d.ts +4 -1
  24. package/dist/client/local.js +12 -4
  25. package/dist/client/start-body.d.ts +4 -3
  26. package/dist/core/byte-arrays.d.ts +5 -0
  27. package/dist/core/byte-arrays.js +5 -0
  28. package/dist/core/checkpoint/lifecycle.d.ts +3 -0
  29. package/dist/core/checkpoint/lifecycle.js +9 -4
  30. package/dist/core/checkpoint/serialization.js +12 -5
  31. package/dist/core/codec/extension-codec.d.ts +16 -0
  32. package/dist/core/codec/extension-codec.js +100 -6
  33. package/dist/core/context/activity-retry-state.d.ts +38 -0
  34. package/dist/core/context/activity-retry-state.js +89 -0
  35. package/dist/core/context/activity-schedule-to-close.d.ts +159 -0
  36. package/dist/core/context/activity-schedule-to-close.js +69 -0
  37. package/dist/core/context/child-workflow-pipe.d.ts +5 -5
  38. package/dist/core/context/child-workflow-pipe.js +15 -2
  39. package/dist/core/context/context-presence.d.ts +19 -0
  40. package/dist/core/context/context-presence.js +13 -0
  41. package/dist/core/context/durable-operations.d.ts +25 -0
  42. package/dist/core/context/durable-operations.js +64 -0
  43. package/dist/core/context/index.d.ts +9 -5
  44. package/dist/core/context/index.js +17 -12
  45. package/dist/core/context/internals.d.ts +20 -0
  46. package/dist/core/context/internals.js +3 -1
  47. package/dist/core/context/operation-request.d.ts +35 -0
  48. package/dist/core/context/parallel-cache-entry.d.ts +14 -5
  49. package/dist/core/context/run-operation.d.ts +6 -1
  50. package/dist/core/context/run-operation.js +41 -87
  51. package/dist/core/context/session-state.js +7 -8
  52. package/dist/core/context/speculative-child.js +2 -0
  53. package/dist/core/context/types.d.ts +15 -0
  54. package/dist/core/context/version-patching.d.ts +8 -0
  55. package/dist/core/context/version-patching.js +40 -0
  56. package/dist/core/context/workflow-logger.d.ts +98 -0
  57. package/dist/core/context/workflow-logger.js +68 -0
  58. package/dist/core/engine/activity-heartbeat-tracking.d.ts +64 -0
  59. package/dist/core/engine/activity-heartbeat-tracking.js +42 -0
  60. package/dist/core/engine/activity-per-attempt-timeout.d.ts +41 -0
  61. package/dist/core/engine/activity-per-attempt-timeout.js +36 -0
  62. package/dist/core/engine/activity-reconciliation.d.ts +1 -0
  63. package/dist/core/engine/activity-reconciliation.js +12 -1
  64. package/dist/core/engine/activity-resolution.d.ts +23 -0
  65. package/dist/core/engine/activity-resolution.js +37 -0
  66. package/dist/core/engine/aggregate.js +66 -30
  67. package/dist/core/engine/async-activity-completion.d.ts +23 -6
  68. package/dist/core/engine/async-activity-completion.js +53 -12
  69. package/dist/core/engine/attributes-tags.js +9 -4
  70. package/dist/core/engine/bulk-operations-purge.d.ts +26 -0
  71. package/dist/core/engine/bulk-operations-purge.js +38 -3
  72. package/dist/core/engine/bulk-operations-shared.d.ts +2 -0
  73. package/dist/core/engine/bulk-operations-shared.js +8 -2
  74. package/dist/core/engine/bulk-operations.d.ts +4 -1
  75. package/dist/core/engine/bulk-operations.js +235 -24
  76. package/dist/core/engine/callback-creators-bundles.d.ts +9 -0
  77. package/dist/core/engine/callback-creators-bundles.js +30 -2
  78. package/dist/core/engine/callback-creators-core.js +6 -4
  79. package/dist/core/engine/callback-creators-router.js +10 -0
  80. package/dist/core/engine/callback-creators-schedule.d.ts +2 -2
  81. package/dist/core/engine/callback-creators-schedule.js +12 -8
  82. package/dist/core/engine/candidate-read-batching.d.ts +6 -0
  83. package/dist/core/engine/candidate-read-batching.js +1 -0
  84. package/dist/core/engine/checkpoint-io.js +61 -25
  85. package/dist/core/engine/checkpoint-reads.js +3 -2
  86. package/dist/core/engine/checkpoint-replay.d.ts +20 -0
  87. package/dist/core/engine/checkpoint-replay.js +242 -0
  88. package/dist/core/engine/checkpoint-side-effects.d.ts +23 -0
  89. package/dist/core/engine/checkpoint-side-effects.js +35 -0
  90. package/dist/core/engine/child-workflow-cancellation.d.ts +10 -0
  91. package/dist/core/engine/child-workflow-cancellation.js +40 -0
  92. package/dist/core/engine/child-workflow.js +27 -4
  93. package/dist/core/engine/condition-waiters.d.ts +14 -0
  94. package/dist/core/engine/condition-waiters.js +5 -0
  95. package/dist/core/engine/construction.d.ts +3 -0
  96. package/dist/core/engine/construction.js +21 -8
  97. package/dist/core/engine/coordination-branch-executors.d.ts +70 -0
  98. package/dist/core/engine/coordination-branch-executors.js +107 -0
  99. package/dist/core/engine/deferred-consume-envelope.d.ts +59 -0
  100. package/dist/core/engine/deferred-consume-envelope.js +14 -0
  101. package/dist/core/engine/disposal.js +13 -0
  102. package/dist/core/engine/engine-create-types.d.ts +14 -4
  103. package/dist/core/engine/engine-internal-types.d.ts +26 -3
  104. package/dist/core/engine/errors.d.ts +47 -0
  105. package/dist/core/engine/errors.js +20 -0
  106. package/dist/core/engine/event-log-compaction.d.ts +8 -0
  107. package/dist/core/engine/event-log-compaction.js +20 -2
  108. package/dist/core/engine/fenced-write.d.ts +65 -0
  109. package/dist/core/engine/fenced-write.js +71 -0
  110. package/dist/core/engine/finalizer-state.d.ts +28 -0
  111. package/dist/core/engine/finalizer-state.js +16 -0
  112. package/dist/core/engine/handles.d.ts +8 -0
  113. package/dist/core/engine/index.d.ts +34 -16
  114. package/dist/core/engine/index.js +133 -8
  115. package/dist/core/engine/inline-parking.js +3 -2
  116. package/dist/core/engine/internals.d.ts +107 -3
  117. package/dist/core/engine/lease-codec.d.ts +35 -0
  118. package/dist/core/engine/lease-codec.js +32 -0
  119. package/dist/core/engine/lease-deposition.d.ts +66 -0
  120. package/dist/core/engine/lease-deposition.js +10 -0
  121. package/dist/core/engine/lease-errors.d.ts +99 -0
  122. package/dist/core/engine/lease-errors.js +30 -0
  123. package/dist/core/engine/lease-manager.d.ts +114 -0
  124. package/dist/core/engine/lease-manager.js +135 -0
  125. package/dist/core/engine/lifecycle/persist.js +10 -32
  126. package/dist/core/engine/lifecycle/recovered-services.d.ts +8 -6
  127. package/dist/core/engine/lifecycle/recovered-services.js +18 -3
  128. package/dist/core/engine/lifecycle/resume.js +16 -9
  129. package/dist/core/engine/lifecycle/shared.d.ts +8 -1
  130. package/dist/core/engine/lifecycle/start-batch.d.ts +1 -1
  131. package/dist/core/engine/lifecycle/start-batch.js +2 -1
  132. package/dist/core/engine/lifecycle/start-commit.d.ts +9 -0
  133. package/dist/core/engine/lifecycle/start-commit.js +50 -8
  134. package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +58 -0
  135. package/dist/core/engine/lifecycle/start-or-signal-create.js +95 -0
  136. package/dist/core/engine/lifecycle/start-or-signal.d.ts +3 -2
  137. package/dist/core/engine/lifecycle/start-or-signal.js +11 -81
  138. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +39 -0
  139. package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +28 -0
  140. package/dist/core/engine/lifecycle/start.d.ts +4 -4
  141. package/dist/core/engine/lifecycle/start.js +17 -9
  142. package/dist/core/engine/lifecycle/transition.js +17 -5
  143. package/dist/core/engine/lifecycle.d.ts +1 -1
  144. package/dist/core/engine/list-candidate-resolution.js +2 -2
  145. package/dist/core/engine/listing.js +9 -5
  146. package/dist/core/engine/operations-activity.d.ts +4 -11
  147. package/dist/core/engine/operations-activity.js +22 -49
  148. package/dist/core/engine/operations-coordination.d.ts +10 -0
  149. package/dist/core/engine/operations-coordination.js +43 -8
  150. package/dist/core/engine/operations-router.d.ts +6 -0
  151. package/dist/core/engine/operations-router.js +2 -0
  152. package/dist/core/engine/operations-speculate.js +6 -2
  153. package/dist/core/engine/operations-stream.js +12 -2
  154. package/dist/core/engine/operations-time.d.ts +4 -2
  155. package/dist/core/engine/operations-time.js +14 -2
  156. package/dist/core/engine/operations-wait-condition.d.ts +23 -0
  157. package/dist/core/engine/operations-wait-condition.js +62 -0
  158. package/dist/core/engine/ownership-options.d.ts +23 -0
  159. package/dist/core/engine/ownership-options.js +26 -0
  160. package/dist/core/engine/payload-size-policy.d.ts +3 -0
  161. package/dist/core/engine/payload-size-policy.js +4 -0
  162. package/dist/core/engine/pending-updates.d.ts +17 -0
  163. package/dist/core/engine/pending-updates.js +33 -11
  164. package/dist/core/engine/queries.js +1 -1
  165. package/dist/core/engine/registration.d.ts +1 -0
  166. package/dist/core/engine/registration.js +18 -3
  167. package/dist/core/engine/schedule-jitter.d.ts +5 -0
  168. package/dist/core/engine/schedule-jitter.js +10 -0
  169. package/dist/core/engine/schedule-run.d.ts +17 -0
  170. package/dist/core/engine/schedule-run.js +38 -0
  171. package/dist/core/engine/schedule-timer.js +81 -17
  172. package/dist/core/engine/schedules.d.ts +7 -4
  173. package/dist/core/engine/schedules.js +37 -16
  174. package/dist/core/engine/search-attribute-records.d.ts +4 -0
  175. package/dist/core/engine/search-attribute-records.js +17 -0
  176. package/dist/core/engine/signals.d.ts +18 -2
  177. package/dist/core/engine/signals.js +42 -21
  178. package/dist/core/engine/state-utilities.d.ts +49 -2
  179. package/dist/core/engine/state-utilities.js +114 -3
  180. package/dist/core/engine/storage-io.d.ts +16 -2
  181. package/dist/core/engine/storage-io.js +26 -5
  182. package/dist/core/engine/stream-chunk-loading.d.ts +2 -0
  183. package/dist/core/engine/stream-chunk-loading.js +24 -1
  184. package/dist/core/engine/sub-operation.js +22 -3
  185. package/dist/core/engine/termination/cleanup.d.ts +3 -1
  186. package/dist/core/engine/termination/cleanup.js +25 -1
  187. package/dist/core/engine/termination/complete.js +44 -6
  188. package/dist/core/engine/termination/finalizer-activity.d.ts +53 -0
  189. package/dist/core/engine/termination/finalizer-activity.js +30 -0
  190. package/dist/core/engine/termination/finalizer-claim.d.ts +110 -0
  191. package/dist/core/engine/termination/finalizer-claim.js +80 -0
  192. package/dist/core/engine/termination/finalizer.d.ts +48 -0
  193. package/dist/core/engine/termination/finalizer.js +159 -0
  194. package/dist/core/engine/termination/suspend.d.ts +3 -1
  195. package/dist/core/engine/termination.d.ts +1 -0
  196. package/dist/core/engine/termination.js +4 -0
  197. package/dist/core/engine/updates.js +5 -25
  198. package/dist/core/engine/validation/schedule.d.ts +2 -1
  199. package/dist/core/engine/validation/schedule.js +51 -2
  200. package/dist/core/engine/validation.js +32 -5
  201. package/dist/core/engine/waiting-update-response.d.ts +8 -0
  202. package/dist/core/engine/waiting-update-response.js +22 -0
  203. package/dist/core/engine/workflow-concurrency.d.ts +10 -0
  204. package/dist/core/engine/workflow-concurrency.js +136 -0
  205. package/dist/core/engine/workflow-feed.d.ts +4 -5
  206. package/dist/core/engine/workflow-feed.js +4 -1
  207. package/dist/core/engine/workflow-indexes.d.ts +9 -0
  208. package/dist/core/engine/workflow-indexes.js +10 -0
  209. package/dist/core/engine/workflow-state-stream.js +33 -13
  210. package/dist/core/engine/workflow-visibility-queries.d.ts +2 -2
  211. package/dist/core/engine-helpers.js +1 -0
  212. package/dist/core/events/activity-events.d.ts +43 -12
  213. package/dist/core/events/activity-events.js +28 -0
  214. package/dist/core/events/attribute-events.d.ts +2 -3
  215. package/dist/core/events/event-map.d.ts +18 -12
  216. package/dist/core/events/index.d.ts +1 -0
  217. package/dist/core/events/index.js +1 -0
  218. package/dist/core/events/schedule-events.d.ts +71 -0
  219. package/dist/core/events/schedule-events.js +29 -0
  220. package/dist/core/events/signal-events.d.ts +4 -6
  221. package/dist/core/events/system-events.d.ts +29 -19
  222. package/dist/core/events/system-events.js +26 -0
  223. package/dist/core/events/update-events.d.ts +6 -8
  224. package/dist/core/events/workflow-events.d.ts +88 -23
  225. package/dist/core/events/workflow-events.js +26 -0
  226. package/dist/core/failure-categories.js +5 -17
  227. package/dist/core/fault-code.d.ts +9 -3
  228. package/dist/core/fault-code.js +1 -0
  229. package/dist/core/inline-execution-strategy.context-options.d.ts +69 -0
  230. package/dist/core/inline-execution-strategy.context-options.js +56 -0
  231. package/dist/core/inline-execution-strategy.d.ts +31 -36
  232. package/dist/core/inline-execution-strategy.js +16 -27
  233. package/dist/core/list-filter-validation.d.ts +1 -1
  234. package/dist/core/payload-size.d.ts +1 -1
  235. package/dist/core/persisted-data-incompatible-error.d.ts +9 -7
  236. package/dist/core/review/events.d.ts +6 -8
  237. package/dist/core/scheduler/scheduler-class.d.ts +10 -1
  238. package/dist/core/scheduler/scheduler-class.js +38 -11
  239. package/dist/core/scheduler/timer-batch.js +9 -1
  240. package/dist/core/scheduler/timer-sources.d.ts +1 -0
  241. package/dist/core/scheduler/timer-sources.js +11 -5
  242. package/dist/core/session-state.d.ts +0 -1
  243. package/dist/core/session-state.js +2 -2
  244. package/dist/core/start-workflow-validation.d.ts +34 -0
  245. package/dist/core/start-workflow-validation.js +19 -0
  246. package/dist/core/types/activity-verification.d.ts +67 -0
  247. package/dist/core/types/activity-verification.js +0 -0
  248. package/dist/core/types/activity.d.ts +80 -61
  249. package/dist/core/types/bulk.d.ts +50 -9
  250. package/dist/core/types/checkpoint.d.ts +44 -8
  251. package/dist/core/types/list-options.d.ts +146 -0
  252. package/dist/core/types/list-options.js +0 -0
  253. package/dist/core/types/options.d.ts +121 -165
  254. package/dist/core/types/schedules.d.ts +39 -3
  255. package/dist/core/types/services-resolution.d.ts +2 -2
  256. package/dist/core/types/workflow-builder-runtime.d.ts +12 -3
  257. package/dist/core/types/workflow-builder-runtime.js +11 -2
  258. package/dist/core/types/workflow-builder.d.ts +42 -11
  259. package/dist/core/types/workflow-concurrency.d.ts +39 -0
  260. package/dist/core/types/workflow-concurrency.js +0 -0
  261. package/dist/core/types/workflow-context.d.ts +157 -14
  262. package/dist/core/types/workflow-definition.d.ts +53 -10
  263. package/dist/core/types/workflow-function.d.ts +84 -9
  264. package/dist/core/types/workflow-log.d.ts +98 -0
  265. package/dist/core/types/workflow-log.js +0 -0
  266. package/dist/core/types/workflow-registries.d.ts +14 -3
  267. package/dist/core/types/workflow-registry.d.ts +3 -0
  268. package/dist/core/types.d.ts +4 -0
  269. package/dist/core/types.js +4 -0
  270. package/dist/core/versioning.d.ts +9 -39
  271. package/dist/core/versioning.js +2 -12
  272. package/dist/core/weft-error.d.ts +44 -1
  273. package/dist/core/weft-error.js +16 -0
  274. package/dist/core/worker-execution-strategy-options.d.ts +26 -0
  275. package/dist/core/worker-execution-strategy.js +40 -36
  276. package/dist/core/worker-inbound-message.d.ts +38 -0
  277. package/dist/core/worker-inbound-message.js +37 -0
  278. package/dist/core/worker-log-abuse-counter.d.ts +175 -0
  279. package/dist/core/worker-log-abuse-counter.js +107 -0
  280. package/dist/core/worker-message-helpers.d.ts +47 -0
  281. package/dist/core/worker-message-helpers.js +26 -0
  282. package/dist/core/worker-protocol-guard.js +1 -1
  283. package/dist/core/worker-protocol-log.d.ts +56 -0
  284. package/dist/core/worker-protocol-log.js +18 -0
  285. package/dist/core/worker-protocol.d.ts +23 -0
  286. package/dist/core/worker-protocol.js +12 -2
  287. package/dist/core/workflow-version-tuple.d.ts +3 -9
  288. package/dist/core/workflow-version-tuple.js +2 -3
  289. package/dist/diagnostics/format.js +1 -6
  290. package/dist/diagnostics/types.d.ts +4 -5
  291. package/dist/diagnostics/validate.d.ts +0 -1
  292. package/dist/diagnostics/version-check.d.ts +1 -1
  293. package/dist/diagnostics/version-check.js +4 -9
  294. package/dist/index.d.ts +12 -10
  295. package/dist/index.js +29 -3
  296. package/dist/json-schema.js +3 -3
  297. package/dist/mcp/cli.js +27 -27
  298. package/dist/mcp/dispatcher.js +1 -1
  299. package/dist/mcp/http.js +16 -6
  300. package/dist/mcp/protocol.d.ts +2 -0
  301. package/dist/mcp/protocol.js +1 -1
  302. package/dist/mcp/session.d.ts +16 -0
  303. package/dist/mcp/session.js +59 -6
  304. package/dist/mcp/tools.js +73 -6
  305. package/dist/observability/index.js +2 -2
  306. package/dist/server/asyncapi-channels.js +5 -2
  307. package/dist/server/attribute-filters.d.ts +8 -3
  308. package/dist/server/attribute-filters.js +6 -1
  309. package/dist/server/authentication/api-key.d.ts +2 -1
  310. package/dist/server/authentication/api-key.js +5 -2
  311. package/dist/server/authentication/constant-time-api-key.d.ts +15 -0
  312. package/dist/server/authentication/constant-time-api-key.js +24 -0
  313. package/dist/server/authentication/index.d.ts +1 -1
  314. package/dist/server/authentication/index.js +7 -4
  315. package/dist/server/authentication/rotating-api-key-store.d.ts +5 -3
  316. package/dist/server/authentication/rotating-api-key-store.js +19 -11
  317. package/dist/server/engine-event-feed-backend.d.ts +4 -5
  318. package/dist/server/fault-to-http.js +8 -1
  319. package/dist/server/fault-to-json-rpc.js +1 -0
  320. package/dist/server/fleet-event-feed.d.ts +31 -0
  321. package/dist/server/fleet-event-feed.js +157 -0
  322. package/dist/server/handler/index.js +1 -1
  323. package/dist/server/handler/response-helpers.js +2 -1
  324. package/dist/server/handler/route-dispatch.d.ts +6 -3
  325. package/dist/server/handler/route-dispatch.js +2 -2
  326. package/dist/server/handler.js +23 -23
  327. package/dist/server/index.d.ts +32 -2
  328. package/dist/server/index.js +26 -26
  329. package/dist/server/json-rpc-transport-helpers.d.ts +1 -0
  330. package/dist/server/json-rpc-transport-helpers.js +2 -1
  331. package/dist/server/json-rpc-websocket-runtime.d.ts +11 -2
  332. package/dist/server/json-rpc-websocket-runtime.js +1 -0
  333. package/dist/server/json-rpc-websocket-subscriptions.d.ts +10 -0
  334. package/dist/server/json-rpc-websocket-subscriptions.js +29 -0
  335. package/dist/server/json-rpc-websocket-types.d.ts +22 -0
  336. package/dist/server/json-rpc-websocket-types.js +0 -0
  337. package/dist/server/json-rpc-websocket-validation.js +20 -4
  338. package/dist/server/json-rpc-websocket.d.ts +2 -43
  339. package/dist/server/json-rpc-websocket.js +57 -74
  340. package/dist/server/openapi.d.ts +2 -1
  341. package/dist/server/openapi.js +1 -3
  342. package/dist/server/openrpc-document-schema.d.ts +78 -0
  343. package/dist/server/openrpc-document-schema.js +27 -1
  344. package/dist/server/openrpc-errors.d.ts +3 -0
  345. package/dist/server/openrpc-errors.js +2 -1
  346. package/dist/server/openrpc.js +32 -0
  347. package/dist/server/operation-catalog/index.d.ts +1 -1
  348. package/dist/server/operation-catalog/pipeline-helpers.js +1 -0
  349. package/dist/server/operation-catalog/pipeline-stages.js +26 -12
  350. package/dist/server/operation-catalog/pipeline.js +8 -1
  351. package/dist/server/operation-catalog/registry.js +13 -0
  352. package/dist/server/operation-catalog/types.d.ts +20 -9
  353. package/dist/server/operation-fault.d.ts +10 -0
  354. package/dist/server/operation-fault.js +2 -0
  355. package/dist/server/operation-registry.d.ts +12 -11
  356. package/dist/server/operation-registry.js +12 -3
  357. package/dist/server/operations/aggregate-workflows.d.ts +15 -15
  358. package/dist/server/operations/async-activity.d.ts +2 -2
  359. package/dist/server/operations/async-activity.js +12 -7
  360. package/dist/server/operations/bulk-cancel-workflows.d.ts +1 -1
  361. package/dist/server/operations/bulk-cancel-workflows.js +2 -2
  362. package/dist/server/operations/bulk-delete-workflows.d.ts +1 -1
  363. package/dist/server/operations/bulk-delete-workflows.js +2 -2
  364. package/dist/server/operations/bulk-filter-helpers.d.ts +8 -6
  365. package/dist/server/operations/bulk-filter-helpers.js +54 -24
  366. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +11 -9
  367. package/dist/server/operations/bulk-mutate-workflow-tags.js +2 -2
  368. package/dist/server/operations/bulk-operation-helpers.d.ts +2 -2
  369. package/dist/server/operations/bulk-retry-failed-workflows.d.ts +7 -0
  370. package/dist/server/operations/bulk-retry-failed-workflows.js +64 -0
  371. package/dist/server/operations/bulk-signal-workflows.d.ts +11 -9
  372. package/dist/server/operations/bulk-signal-workflows.js +2 -2
  373. package/dist/server/operations/cancel-schedule.d.ts +1 -1
  374. package/dist/server/operations/create-schedule.d.ts +3 -1
  375. package/dist/server/operations/create-schedule.js +29 -11
  376. package/dist/server/operations/fleet-events-subscription.d.ts +54 -0
  377. package/dist/server/operations/fleet-events-subscription.js +99 -0
  378. package/dist/server/operations/fork-workflow.d.ts +1 -1
  379. package/dist/server/operations/fork-workflow.js +3 -2
  380. package/dist/server/operations/get-checkpoint-at.d.ts +1 -1
  381. package/dist/server/operations/get-registry.d.ts +1 -1
  382. package/dist/server/operations/get-retention-overview.d.ts +1 -1
  383. package/dist/server/operations/get-review.d.ts +1 -1
  384. package/dist/server/operations/get-schedule.d.ts +1 -1
  385. package/dist/server/operations/get-stream-chunks.d.ts +1 -1
  386. package/dist/server/operations/get-system-metrics.d.ts +2 -2
  387. package/dist/server/operations/get-task-diagnostics.d.ts +45 -6
  388. package/dist/server/operations/get-task-diagnostics.js +101 -7
  389. package/dist/server/operations/get-update-result.d.ts +1 -1
  390. package/dist/server/operations/get-workflow-attributes.d.ts +1 -1
  391. package/dist/server/operations/get-workflow-events.d.ts +1 -1
  392. package/dist/server/operations/get-workflow-result.d.ts +1 -1
  393. package/dist/server/operations/get-workflow-timeline.d.ts +1 -1
  394. package/dist/server/operations/get-workflow.d.ts +1 -1
  395. package/dist/server/operations/list-checkpoints.d.ts +1 -1
  396. package/dist/server/operations/list-reviews.d.ts +1 -1
  397. package/dist/server/operations/list-schedules.d.ts +1 -1
  398. package/dist/server/operations/list-task-queues.d.ts +2 -2
  399. package/dist/server/operations/list-workers.d.ts +2 -2
  400. package/dist/server/operations/list-workflows.d.ts +1 -1
  401. package/dist/server/operations/operation-helpers.d.ts +16 -1
  402. package/dist/server/operations/operation-helpers.js +15 -3
  403. package/dist/server/operations/pause-schedule.d.ts +1 -1
  404. package/dist/server/operations/purge-workflows.d.ts +5 -5
  405. package/dist/server/operations/purge-workflows.js +2 -2
  406. package/dist/server/operations/query-workflow.d.ts +1 -1
  407. package/dist/server/operations/query-workflow.js +3 -2
  408. package/dist/server/operations/recover-all.d.ts +1 -1
  409. package/dist/server/operations/replay-workflow.d.ts +1 -1
  410. package/dist/server/operations/resume-schedule.d.ts +1 -1
  411. package/dist/server/operations/set-workflow-attributes.d.ts +1 -1
  412. package/dist/server/operations/set-workflow-attributes.js +6 -3
  413. package/dist/server/operations/signal-workflow.js +8 -3
  414. package/dist/server/operations/single-workflow-tag-mutation.js +13 -3
  415. package/dist/server/operations/start-or-signal-workflow.d.ts +6 -1
  416. package/dist/server/operations/start-or-signal-workflow.js +14 -24
  417. package/dist/server/operations/start-workflow-options.d.ts +6 -0
  418. package/dist/server/operations/start-workflow-rest-input.d.ts +14 -0
  419. package/dist/server/operations/start-workflow-rest-input.js +31 -0
  420. package/dist/server/operations/start-workflow.d.ts +1 -1
  421. package/dist/server/operations/start-workflow.js +8 -23
  422. package/dist/server/operations/storage.d.ts +6 -6
  423. package/dist/server/operations/storage.js +24 -14
  424. package/dist/server/operations/stream-workflow-sse.d.ts +1 -1
  425. package/dist/server/operations/submit-review-decision.d.ts +1 -1
  426. package/dist/server/operations/submit-review-decision.js +6 -3
  427. package/dist/server/operations/update-schedule.d.ts +1 -1
  428. package/dist/server/operations/update-schedule.js +7 -4
  429. package/dist/server/operations/update-workflow.d.ts +1 -1
  430. package/dist/server/operations/update-workflow.js +7 -3
  431. package/dist/server/operations/worker-drain.d.ts +8 -8
  432. package/dist/server/operations/worker-drain.js +11 -8
  433. package/dist/server/operations/workflow-events-subscription.d.ts +7 -8
  434. package/dist/server/operations/workflow-events-subscription.js +43 -8
  435. package/dist/server/rest-binding.d.ts +3 -1
  436. package/dist/server/rest-bindings.js +12 -0
  437. package/dist/server/rest-body.d.ts +10 -0
  438. package/dist/server/rest-body.js +80 -0
  439. package/dist/server/runtime/authentication-bridge.d.ts +1 -0
  440. package/dist/server/runtime/authentication-bridge.js +33 -6
  441. package/dist/server/runtime/client-visible-events.d.ts +3 -0
  442. package/dist/server/runtime/client-visible-events.js +65 -0
  443. package/dist/server/runtime/context.d.ts +7 -0
  444. package/dist/server/runtime/event-broadcasting.d.ts +6 -0
  445. package/dist/server/runtime/event-broadcasting.js +72 -38
  446. package/dist/server/runtime/retry.d.ts +1 -0
  447. package/dist/server/runtime/retry.js +14 -0
  448. package/dist/server/runtime/shutdown.d.ts +7 -6
  449. package/dist/server/runtime/shutdown.js +6 -1
  450. package/dist/server/runtime/task-polling.js +61 -20
  451. package/dist/server/runtime/task-reconciliation.js +9 -1
  452. package/dist/server/runtime/task-result-resolution.d.ts +15 -0
  453. package/dist/server/runtime/task-result-resolution.js +81 -0
  454. package/dist/server/runtime/websocket-stream.d.ts +20 -1
  455. package/dist/server/runtime/websocket-stream.js +173 -7
  456. package/dist/server/runtime/websocket-upgrade.js +22 -3
  457. package/dist/server/runtime/websocket-worker.d.ts +1 -1
  458. package/dist/server/runtime/websocket-worker.js +38 -30
  459. package/dist/server/serve-internals.d.ts +3 -1
  460. package/dist/server/serve-internals.js +18 -2
  461. package/dist/server/task-state.d.ts +30 -0
  462. package/dist/server/task-state.js +28 -0
  463. package/dist/server/workflow-event-feed.d.ts +36 -115
  464. package/dist/server/workflow-event-feed.js +92 -27
  465. package/dist/service-worker/index.js +30 -30
  466. package/dist/storage/auto.js +1 -1
  467. package/dist/storage/bun-sql.js +24 -0
  468. package/dist/storage/compressed-storage.js +1 -1
  469. package/dist/storage/default-scope.d.ts +7 -4
  470. package/dist/storage/http.js +2 -2
  471. package/dist/storage/index.d.ts +76 -4
  472. package/dist/storage/indexeddb.js +1 -1
  473. package/dist/storage/interface.d.ts +163 -0
  474. package/dist/storage/interface.js +1 -1
  475. package/dist/storage/key-prefixes.d.ts +1 -1
  476. package/dist/storage/key-prefixes.js +10 -0
  477. package/dist/storage/lmdb.d.ts +4 -3
  478. package/dist/storage/lmdb.js +1 -1
  479. package/dist/storage/memory.js +1 -1
  480. package/dist/storage/neon-batch.d.ts +30 -0
  481. package/dist/storage/neon-batch.js +30 -0
  482. package/dist/storage/neon-value-mapping.d.ts +30 -3
  483. package/dist/storage/neon-value-mapping.js +12 -0
  484. package/dist/storage/neon.d.ts +16 -0
  485. package/dist/storage/neon.js +9 -4
  486. package/dist/storage/node-sqlite.js +23 -0
  487. package/dist/storage/postgres-key-value-queries.d.ts +69 -44
  488. package/dist/storage/postgres-key-value-queries.js +62 -24
  489. package/dist/storage/resolve.js +1 -1
  490. package/dist/storage/scoped-storage.js +1 -1
  491. package/dist/storage/testing.d.ts +168 -0
  492. package/dist/storage/testing.js +2 -0
  493. package/dist/storage/turso.d.ts +8 -0
  494. package/dist/storage/turso.js +2 -2
  495. package/dist/storage/typed-storage.js +1 -1
  496. package/dist/storage/web-extension.js +1 -1
  497. package/dist/testing/index.js +28 -28
  498. package/dist/version.d.ts +1 -1
  499. package/dist/version.js +1 -1
  500. package/dist/worker/protocol.js +1 -1
  501. package/dist/workers/worker-replay-state.d.ts +32 -0
  502. package/dist/workers/worker-replay-state.js +35 -0
  503. package/dist/workers/worker-state-namespace.d.ts +21 -0
  504. package/dist/workers/worker-state-namespace.js +13 -0
  505. package/dist/workers/workflow-runner.d.ts +19 -15
  506. package/dist/workers/workflow-runner.js +72 -60
  507. package/dist/workers/workflow-worker-entry.js +5 -2
  508. package/package.json +5 -1
@@ -0,0 +1,99 @@
1
+ import { WeftError } from '../weft-error.ts';
2
+ /**
3
+ * Thrown when `ownership: 'lease'` is configured and the engine cannot acquire the
4
+ * storage lease within the configured wait window — another live instance still
5
+ * holds it. Raised from the lease-acquire step, which runs both inside
6
+ * {@link Engine.create} and at the top of {@link Engine.recoverAll} (so a
7
+ * `new Engine({ ownership: 'lease' })` + `recoverAll()` boot must handle it too).
8
+ * In a rolling deploy this means the
9
+ * outgoing instance has not released the lease (or its lease has not yet expired)
10
+ * within `leaseWaitTimeout`; size that window above the outgoing instance's drain
11
+ * time and above the lease TTL so a crash (no clean release) still resolves once
12
+ * the held lease expires. Inspect `waitedMs` for how long this instance waited
13
+ * and `heldBy` for the holder id observed when it gave up (`null` if unknown).
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { EngineLeaseAcquisitionTimeoutError } from '@lostgradient/weft';
18
+ *
19
+ * function isLeaseHandoffStuck(error: unknown): boolean {
20
+ * return error instanceof EngineLeaseAcquisitionTimeoutError;
21
+ * }
22
+ * ```
23
+ */
24
+ export declare class EngineLeaseAcquisitionTimeoutError extends WeftError<'EngineLeaseAcquisitionTimeoutError'> {
25
+ readonly waitedMs: number;
26
+ readonly heldBy: string | null;
27
+ constructor(waitedMs: number, heldBy: string | null);
28
+ }
29
+ /**
30
+ * Thrown when `ownership: 'lease'` is configured but the lease keys in storage are
31
+ * corrupt. Raised from the lease-acquire step, which runs both inside
32
+ * {@link Engine.create} and at the top of {@link Engine.recoverAll} (so a
33
+ * `new Engine({ ownership: 'lease' })` + `recoverAll()` boot must handle it too) —
34
+ * the `lease:epoch` high-water mark is present
35
+ * but does not decode to a valid epoch, or a holder record exists with no epoch
36
+ * key (which the lease protocol never produces, since `release()` deletes only the
37
+ * holder and never the epoch). The lease epoch is the sole source of truth for the
38
+ * monotonic fencing generation, so a booting instance fails closed rather than
39
+ * re-minting a generation at or below the true high-water mark and risking a
40
+ * split-brain. Resolve by operator repair: delete both `lease:` keys only if you
41
+ * are certain no other instance is running, or reset `lease:epoch` above its last
42
+ * known value.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { EngineLeaseCorruptedError } from '@lostgradient/weft';
47
+ *
48
+ * function isLeaseCorrupted(error: unknown): boolean {
49
+ * return error instanceof EngineLeaseCorruptedError;
50
+ * }
51
+ * ```
52
+ */
53
+ export declare class EngineLeaseCorruptedError extends WeftError<'EngineLeaseCorruptedError'> {
54
+ constructor(detail: string);
55
+ }
56
+ /**
57
+ * Thrown by {@link Engine.start} and {@link Engine.startOrSignal} when
58
+ * `ownership: 'lease'` is configured but the engine does not yet hold the lease —
59
+ * it was constructed with `new Engine({ ownership: 'lease' })` and used to start
60
+ * work before `recoverAll()` ran, or created with `recover: false`. In lease mode
61
+ * an engine must hold the single-writer lease (and have recovered existing runs)
62
+ * before it durably writes new workflow state, or a fresh start could race an
63
+ * instance that legitimately owns the store. {@link Engine.create} acquires the
64
+ * lease and recovers for you; a direct constructor must `await engine.recoverAll()`
65
+ * first.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { Engine, EngineLeaseNotHeldError } from '@lostgradient/weft';
70
+ *
71
+ * const engine = new Engine({ ownership: 'lease' });
72
+ * try {
73
+ * await engine.start('ping', null);
74
+ * } catch (error) {
75
+ * if (error instanceof EngineLeaseNotHeldError) {
76
+ * await engine.recoverAll(); // acquires the lease, then retry the start
77
+ * }
78
+ * }
79
+ * ```
80
+ */
81
+ export declare class EngineLeaseNotHeldError extends WeftError<'EngineLeaseNotHeldError'> {
82
+ constructor();
83
+ }
84
+ /**
85
+ * Thrown internally on the durable-write path when `ownership: 'lease'` is
86
+ * configured and a fenced write's CAS fails because the held lease epoch is
87
+ * stale — a successor instance has taken the lease and bumped the epoch, so this
88
+ * instance has been deposed. It is the local unwind that stops the commit from
89
+ * advancing in-memory state past a durable write that did not land; the actual
90
+ * "this engine is deposed" reaction (set the `deposed` flag, warn the operator,
91
+ * tear the engine down) is driven by {@link handleDeposition} at the detection
92
+ * site, not by this error propagating — the inline strategy swallows turn
93
+ * rejections, so the throw never reaches a handler. Internal-only: it is never
94
+ * surfaced to user code (deposition reaches operators through the
95
+ * `WeftEngineLeaseLostWarning` process warning).
96
+ */
97
+ export declare class EngineDeposedError extends WeftError<'EngineDeposedError'> {
98
+ constructor();
99
+ }
@@ -0,0 +1,30 @@
1
+ import { WeftError } from "../weft-error.js";
2
+
3
+ export class EngineLeaseAcquisitionTimeoutError extends WeftError {
4
+ waitedMs;
5
+ heldBy;
6
+ constructor(waitedMs, heldBy) {
7
+ const heldClause = heldBy === null ? "" : ` (currently held by "${heldBy}")`;
8
+ super("EngineLeaseAcquisitionTimeoutError", `Could not acquire the storage ownership lease within ${waitedMs}ms${heldClause}. Another engine instance still holds it. Increase leaseWaitTimeout above the outgoing instance's drain time and the lease TTL, or ensure the previous instance releases the lease on shutdown.`);
9
+ this.waitedMs = waitedMs;
10
+ this.heldBy = heldBy;
11
+ }
12
+ }
13
+
14
+ export class EngineLeaseCorruptedError extends WeftError {
15
+ constructor(detail) {
16
+ super("EngineLeaseCorruptedError", `The ownership lease state in storage is corrupt and cannot be used safely: ${detail}. Resolve by operator repair (delete both "lease:" keys only if no other instance is running, or reset "lease:epoch" above its last known value).`);
17
+ }
18
+ }
19
+
20
+ export class EngineLeaseNotHeldError extends WeftError {
21
+ constructor() {
22
+ super("EngineLeaseNotHeldError", "ownership: 'lease' requires the engine to hold the lease before starting work. Use `Engine.create({ ownership: \"lease\" })` (which acquires the lease and recovers), or call `await engine.recoverAll()` on a directly-constructed engine before `start()`.");
23
+ }
24
+ }
25
+
26
+ export class EngineDeposedError extends WeftError {
27
+ constructor() {
28
+ super("EngineDeposedError", "Engine was deposed: a fenced durable write lost its CAS race because another instance now holds the ownership lease at a newer epoch. This engine is halting.");
29
+ }
30
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Lease-fenced single-writer ownership over a shared durable store. This is the
3
+ * opt-in `Engine.create({ ownership: 'lease' })` mechanism — a CORRECTNESS-PATH
4
+ * coordinator, NOT a best-effort smoke alarm like the second-instance detector.
5
+ * Lease outcomes are acted on, never silently dropped, but the channel differs by
6
+ * phase: a failed acquire THROWS and blocks recovery (corruption fails closed, a
7
+ * timed-out handoff raises a typed error); a renewal reports loss through
8
+ * `onLeaseLost` rather than throwing — a CAS-failed renewal means a successor took
9
+ * the lease ('deposed'), and a transient renewal storage error is tolerated until
10
+ * the lease is too close to lapsing to prove ownership ('renewal-unconfirmable').
11
+ * In Step 1 that loss is observability only (the engine warns); Step 2 will make it
12
+ * enforceable by fencing every durable write on the lease epoch.
13
+ *
14
+ * **Why a lease.** Weft's supported model is one engine process per durable
15
+ * store. Without a lease, a rolling deploy briefly runs two engines (old draining
16
+ * + new booting), both recovering and both issuing at-least-once activities. The
17
+ * lease turns that into a clean handoff: the booting instance ACQUIRES the lease
18
+ * before recovering, the draining instance RELEASES it on dispose, and the new
19
+ * instance only recovers once it owns the lease.
20
+ *
21
+ * **Two-key schema (this is load-bearing, not an optimization).** Ownership is
22
+ * tracked by two keys, never one:
23
+ *
24
+ * - `lease:epoch` — an 8-byte big-endian uint64 fencing token. It changes ONLY on
25
+ * ownership transfer (cold acquire, or a steal/re-acquire after the prior
26
+ * holder's lease lapsed), NEVER on a renewal. It is a monotonic high-water mark
27
+ * that SURVIVES release.
28
+ * - `lease:holder` — a JSON `{ holderId, expiresAt, epoch }` record renewed on
29
+ * every heartbeat (its `expiresAt` advances), so its bytes churn constantly.
30
+ *
31
+ * They are split because `conditionalBatch` compares the WHOLE stored value as
32
+ * bytes. If the epoch lived inside the churning holder record, the fencing token
33
+ * (Step 2) would change on every renewal, spuriously failing the fence and
34
+ * self-terminating healthy holders. Keeping the epoch in its own stable key lets
35
+ * a holder cache its epoch once ({@link LeaseManager.currentEpochBytes}) and use
36
+ * it unchanged as a fencing condition across many renewals.
37
+ *
38
+ * **Epoch monotonicity is the anti-split-brain invariant.** Every transfer
39
+ * conditions on BOTH the holder AND the current epoch and bumps the epoch by one;
40
+ * `release()` deletes ONLY the holder and never the epoch. This guarantees a
41
+ * deposed zombie from generation N can never see its epoch re-minted under it —
42
+ * so once Step 2 fences durable writes on the epoch, the zombie always loses.
43
+ *
44
+ * @module core/engine/lease-manager
45
+ */
46
+ import { type Storage } from '../../storage/interface.ts';
47
+ /** Why a holder lost its lease — surfaced to {@link LeaseManagerOptions.onLeaseLost}. */
48
+ export type LeaseLostReason = 'deposed' | 'renewal-unconfirmable';
49
+ /** Options for {@link createLeaseManager}. */
50
+ export type LeaseManagerOptions = {
51
+ storage: Storage;
52
+ /** This engine's unique instance id (the lease holder id). */
53
+ holderId: string;
54
+ /** Wall-clock source (ms), injected so tests can advance time deterministically. */
55
+ getNow: () => number;
56
+ /** Lease time-to-live (ms). A stolen lease becomes available `ttlMs` after its last renewal. */
57
+ ttlMs: number;
58
+ /** Renewal interval (ms). The holder re-asserts the lease this often; must be `< ttlMs`. */
59
+ renewIntervalMs: number;
60
+ /** Boot-time wait window (ms) before {@link LeaseManager.acquire} throws. */
61
+ waitTimeoutMs: number;
62
+ /** Poll interval (ms) for the acquire wait loop. */
63
+ acquirePollIntervalMs?: number;
64
+ /**
65
+ * Delay primitive for the acquire poll loop. Defaults to a real `setTimeout`.
66
+ * Injected so tests drive the loop deterministically: a test `delay` advances
67
+ * the same injected clock by the poll interval and resolves immediately, so the
68
+ * wait deadline (read from `getNow`) trips without real waiting. Mirrors the
69
+ * `getNow` injection — not test-only scaffolding.
70
+ */
71
+ delay?: (ms: number) => Promise<void>;
72
+ /**
73
+ * Called once when this holder loses the lease while running — either CAS-false
74
+ * on renewal (`'deposed'`: a successor stole it) or storage failures that make
75
+ * the holder unable to prove it still holds before the lease lapses
76
+ * (`'renewal-unconfirmable'`). In Step 1 the engine reacts by warning; Step 2
77
+ * uses this to halt fenced writes. The lease manager never throws into the
78
+ * renewal timer — it reports through this seam instead.
79
+ */
80
+ onLeaseLost?: (reason: LeaseLostReason) => void;
81
+ };
82
+ /**
83
+ * A running lease manager.
84
+ *
85
+ * - `acquire()` blocks until this instance owns the lease, then resolves; throws
86
+ * {@link EngineLeaseAcquisitionTimeoutError} on timeout. Call before recovery.
87
+ * One exception to "resolved ⇒ owned": if the manager was `stop()`ped (disposal)
88
+ * while acquire was waiting, it resolves WITHOUT owning — the engine gates
89
+ * recovery on `disposed` so a stopped-then-resolved acquire never proceeds.
90
+ * - `startRenewal()` begins the heartbeat that keeps the lease held (drives
91
+ * {@link LeaseManager.renewOnce} on an interval).
92
+ * - `renewOnce()` runs a single renewal round; exposed for tests so renewal can be
93
+ * driven deterministically with an injected clock, mirroring the detector's
94
+ * `tick()`.
95
+ * - `currentEpochBytes()` returns the held epoch as bytes for Step-2 fencing
96
+ * conditions; `null` before a successful acquire. Synchronous and stable across
97
+ * renewals.
98
+ * - `release()` best-effort relinquishes the lease (holder key only) on dispose.
99
+ */
100
+ export type LeaseManager = {
101
+ acquire(): Promise<void>;
102
+ startRenewal(): void;
103
+ renewOnce(): Promise<void>;
104
+ currentEpochBytes(): Uint8Array | null;
105
+ release(): Promise<void>;
106
+ stop(): void;
107
+ };
108
+ /**
109
+ * Create a lease manager. Does not start any timer or acquire anything itself —
110
+ * the engine drives `acquire()` at the boot gate (before recovery) and
111
+ * `startRenewal()` afterward, and clears the renewal timer through its own
112
+ * disposal path (mirroring how it owns the second-instance detector's interval).
113
+ */
114
+ export declare function createLeaseManager(options: LeaseManagerOptions): LeaseManager;
@@ -0,0 +1,135 @@
1
+ import { KEYS, storageConditionalBatch } from "../../storage/interface.js";
2
+ import {
3
+ decodeEpoch,
4
+ decodeHolder,
5
+ encodeEpoch,
6
+ encodeHolder
7
+ } from "./lease-codec.js";
8
+ import { EngineLeaseAcquisitionTimeoutError, EngineLeaseCorruptedError } from "./lease-errors.js";
9
+ const DEFAULT_ACQUIRE_POLL_INTERVAL_MS = 1000;
10
+ export function createLeaseManager(options) {
11
+ const { storage, holderId, getNow, ttlMs, renewIntervalMs, waitTimeoutMs } = options, acquirePollIntervalMs = options.acquirePollIntervalMs ?? DEFAULT_ACQUIRE_POLL_INTERVAL_MS, unconfirmableMarginMs = renewIntervalMs;
12
+ let stopped = !1, heldEpoch = null, heldEpochBytes = null, lastHolderBytes = null, renewalInterval = null, leaseLost = !1, inFlightRenewal = null;
13
+ const delay = options.delay ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
14
+ function reportLeaseLost(reason) {
15
+ if (leaseLost)
16
+ return;
17
+ leaseLost = !0;
18
+ options.onLeaseLost?.(reason);
19
+ }
20
+ async function readState() {
21
+ const holderRaw = await storage.get(KEYS.leaseHolder()), epochRaw = await storage.get(KEYS.leaseEpoch());
22
+ return {
23
+ epochRaw,
24
+ holderRaw,
25
+ epoch: epochRaw === null ? null : decodeEpoch(epochRaw),
26
+ holder: holderRaw === null ? null : decodeHolder(holderRaw)
27
+ };
28
+ }
29
+ async function takeOwnership(expectedEpochBytes, expectedHolderBytes, nextEpoch) {
30
+ const expiresAt = getNow() + ttlMs, holderBytes = encodeHolder({ holderId, expiresAt, epoch: nextEpoch }), committed = await storageConditionalBatch(storage, [
31
+ { key: KEYS.leaseEpoch(), expectedValue: expectedEpochBytes },
32
+ { key: KEYS.leaseHolder(), expectedValue: expectedHolderBytes }
33
+ ], [
34
+ { type: "put", key: KEYS.leaseEpoch(), value: encodeEpoch(nextEpoch) },
35
+ { type: "put", key: KEYS.leaseHolder(), value: holderBytes }
36
+ ]);
37
+ if (committed) {
38
+ heldEpoch = nextEpoch;
39
+ heldEpochBytes = encodeEpoch(nextEpoch);
40
+ lastHolderBytes = holderBytes;
41
+ }
42
+ return committed;
43
+ }
44
+ async function tryAcquireOnce() {
45
+ const { epochRaw, holderRaw, epoch, holder } = await readState();
46
+ if (epochRaw !== null && epoch === null)
47
+ throw new EngineLeaseCorruptedError('the "lease:epoch" high-water mark is present but does not decode to a valid epoch');
48
+ if (epochRaw === null && holderRaw !== null)
49
+ throw new EngineLeaseCorruptedError('a "lease:holder" record exists with no "lease:epoch" key (the lease protocol never deletes the epoch)');
50
+ if (holder !== null && getNow() < holder.expiresAt)
51
+ return !1;
52
+ const nextEpoch = (epoch ?? 0) + 1;
53
+ if (nextEpoch >= Number.MAX_SAFE_INTEGER)
54
+ throw new EngineLeaseCorruptedError(`the next ownership epoch (${nextEpoch}) is at or above the safe-integer ceiling and cannot be minted without bricking future boots`);
55
+ return takeOwnership(epochRaw, holderRaw, nextEpoch);
56
+ }
57
+ async function acquire() {
58
+ const startedAt = getNow(), deadline = startedAt + waitTimeoutMs;
59
+ let lastHolderId = null, firstAttempt = !0;
60
+ for (;; ) {
61
+ if (stopped)
62
+ return;
63
+ if (!firstAttempt && getNow() >= deadline)
64
+ throw new EngineLeaseAcquisitionTimeoutError(getNow() - startedAt, lastHolderId);
65
+ firstAttempt = !1;
66
+ if (await tryAcquireOnce())
67
+ return;
68
+ const holderRaw = await storage.get(KEYS.leaseHolder());
69
+ lastHolderId = holderRaw === null ? null : decodeHolder(holderRaw)?.holderId ?? null;
70
+ if (getNow() >= deadline)
71
+ throw new EngineLeaseAcquisitionTimeoutError(getNow() - startedAt, lastHolderId);
72
+ await delay(acquirePollIntervalMs);
73
+ }
74
+ }
75
+ async function renewOnce() {
76
+ if (stopped || leaseLost || heldEpoch === null || lastHolderBytes === null)
77
+ return;
78
+ const expiresAt = getNow() + ttlMs, holderBytes = encodeHolder({ holderId, expiresAt, epoch: heldEpoch });
79
+ let committed;
80
+ try {
81
+ committed = await storageConditionalBatch(storage, [{ key: KEYS.leaseHolder(), expectedValue: lastHolderBytes }], [{ type: "put", key: KEYS.leaseHolder(), value: holderBytes }]);
82
+ } catch {
83
+ const priorExpiry = decodeHolder(lastHolderBytes)?.expiresAt ?? 0;
84
+ if (getNow() >= priorExpiry - unconfirmableMarginMs)
85
+ reportLeaseLost("renewal-unconfirmable");
86
+ return;
87
+ }
88
+ if (committed) {
89
+ lastHolderBytes = holderBytes;
90
+ return;
91
+ }
92
+ reportLeaseLost("deposed");
93
+ }
94
+ function renewUnderGuard() {
95
+ if (inFlightRenewal !== null)
96
+ return;
97
+ inFlightRenewal = renewOnce().catch(() => {}).finally(() => {
98
+ inFlightRenewal = null;
99
+ });
100
+ }
101
+ function startRenewal() {
102
+ if (stopped || renewalInterval !== null)
103
+ return;
104
+ renewalInterval = setInterval(() => {
105
+ renewUnderGuard();
106
+ }, renewIntervalMs);
107
+ renewalInterval.unref?.();
108
+ }
109
+ function currentEpochBytes() {
110
+ return heldEpochBytes === null ? null : heldEpochBytes.slice();
111
+ }
112
+ function clearRenewal() {
113
+ if (renewalInterval !== null) {
114
+ clearInterval(renewalInterval);
115
+ renewalInterval = null;
116
+ }
117
+ }
118
+ async function release() {
119
+ stopped = !0;
120
+ clearRenewal();
121
+ if (inFlightRenewal !== null)
122
+ await inFlightRenewal;
123
+ if (lastHolderBytes === null)
124
+ return;
125
+ try {
126
+ if (await storageConditionalBatch(storage, [{ key: KEYS.leaseHolder(), expectedValue: lastHolderBytes }], [{ type: "delete", key: KEYS.leaseHolder() }]))
127
+ lastHolderBytes = null;
128
+ } catch {}
129
+ }
130
+ function stop() {
131
+ stopped = !0;
132
+ clearRenewal();
133
+ }
134
+ return { acquire, startRenewal, renewOnce, currentEpochBytes, release, stop };
135
+ }
@@ -1,15 +1,8 @@
1
- import { serializeCheckpoint } from "../../checkpoint.js";
2
- import { encode } from "../../codec.js";
3
- import {
4
- VersionMismatchError,
5
- buildVersionUpdateOperations,
6
- checkVersionCompatibility,
7
- migrateCheckpoint
8
- } from "../../versioning.js";
1
+ import { VersionMismatchError, checkVersionCompatibility } from "../../versioning.js";
9
2
  import {
10
3
  diffWorkflowVersionTuples
11
4
  } from "../../workflow-version-tuple.js";
12
- import { buildWorkflowVisibilityIndexTransition } from "../workflow-indexes.js";
5
+ import { hydrateCheckpointReplayState } from "../checkpoint-replay.js";
13
6
  export function createWorkflowVersionTuple(_internals, registration, _callbacks) {
14
7
  return {
15
8
  workflowVersion: registration.version
@@ -26,37 +19,22 @@ export function workflowStateWithVersionTuple(internals, state, versionTuple, _c
26
19
  };
27
20
  }
28
21
  export function derivePreparedExecutionState(internals, workflowId, state, checkpoint, registration, callbacks) {
29
- const compatibility = checkVersionCompatibility(checkpoint.version, registration.version, !!registration.migrate), registeredVersionTuple = createWorkflowVersionTuple(internals, registration, callbacks), versionDiff = diffWorkflowVersionTuples(workflowVersionTupleFromState(internals, state, callbacks), registeredVersionTuple), hasVersionTupleDrift = versionDiff.workflowVersion !== void 0 || versionDiff.agentVersion !== void 0 || versionDiff.toolVersions !== void 0;
30
- if (compatibility === "incompatible" || hasVersionTupleDrift && !registration.migrate)
22
+ const compatibility = checkVersionCompatibility(checkpoint.version, registration.version), registeredVersionTuple = createWorkflowVersionTuple(internals, registration, callbacks), versionDiff = diffWorkflowVersionTuples(workflowVersionTupleFromState(internals, state, callbacks), registeredVersionTuple), hasVersionTupleDrift = versionDiff.workflowVersion !== void 0 || versionDiff.agentVersion !== void 0 || versionDiff.toolVersions !== void 0;
23
+ if (compatibility === "incompatible" || hasVersionTupleDrift)
31
24
  throwVersionMismatch(internals, workflowId, state, registration, versionDiff, callbacks);
32
- let preparedState = state, preparedCheckpoint = checkpoint, shouldPersistPreparedState = !1;
33
- if ((compatibility === "needs-migration" || hasVersionTupleDrift) && registration.migrate) {
34
- const migrated = migrateCheckpoint(checkpoint, checkpoint.version, registration.version, registration.migrate);
35
- migrated.version = registeredVersionTuple.workflowVersion;
36
- preparedCheckpoint = migrated;
37
- preparedState = workflowStateWithVersionTuple(internals, state, registeredVersionTuple, callbacks);
38
- shouldPersistPreparedState = !0;
39
- }
40
25
  return {
41
- state: preparedState,
42
- checkpoint: preparedCheckpoint,
26
+ state,
27
+ checkpoint,
43
28
  versionTuple: registeredVersionTuple,
44
- shouldPersistPreparedState
29
+ shouldPersistPreparedState: !1
45
30
  };
46
31
  }
47
32
  export async function prepareResumeState(internals, workflowId, state, checkpoint, checkpointBytes, registration, callbacks) {
48
- const preparedExecutionState = derivePreparedExecutionState(internals, workflowId, state, checkpoint, registration, callbacks);
49
- if (preparedExecutionState.shouldPersistPreparedState) {
50
- const versionVisibilityOperations = buildWorkflowVisibilityIndexTransition(workflowId, state, preparedExecutionState.state).batchOps;
51
- await internals.storage.batch([
52
- ...buildVersionUpdateOperations(workflowId, serializeCheckpoint(preparedExecutionState.checkpoint), preparedExecutionState.versionTuple.workflowVersion, encode(preparedExecutionState.state)),
53
- ...versionVisibilityOperations
54
- ]);
55
- }
33
+ const preparedExecutionState = derivePreparedExecutionState(internals, workflowId, state, checkpoint, registration, callbacks), hydratedCheckpoint = await hydrateCheckpointReplayState(internals.storage, workflowId, preparedExecutionState.checkpoint);
56
34
  return {
57
35
  state: preparedExecutionState.state,
58
- checkpoint: preparedExecutionState.checkpoint,
59
- serializedCheckpoint: preparedExecutionState.shouldPersistPreparedState ? serializeCheckpoint(preparedExecutionState.checkpoint) : checkpointBytes,
36
+ checkpoint: hydratedCheckpoint,
37
+ serializedCheckpoint: checkpointBytes,
60
38
  versionTuple: preparedExecutionState.versionTuple
61
39
  };
62
40
  }
@@ -16,8 +16,8 @@ import type { EngineInternals } from '../internals.ts';
16
16
  * has no marker and proceeds without touching the resolver — so a fail-closed
17
17
  * resolver does not fail healthy no-services runs.
18
18
  *
19
- * Returns `false` to proceed (services available, none expected, or no resolver
20
- * is configured). Returns `true` to STOP — the run was terminally
19
+ * Returns `false` to proceed (services available or none expected). Returns
20
+ * `true` to STOP — the run was terminally
21
21
  * failed (services unavailable), or the terminal commit faulted and the run was
22
22
  * left for a later boot to retry. Either way the generator must not advance:
23
23
  * driving it without services would crash the body and that throw would escape
@@ -36,10 +36,12 @@ import type { EngineInternals } from '../internals.ts';
36
36
  * @param onCommitError - Records a fail-warn when `failRun` itself throws, so the
37
37
  * swallowed terminal-commit fault is still observable.
38
38
  */
39
- export declare function reprovideRecoveredServices(internals: EngineInternals, state: WorkflowState, failRun: (workflowId: string, error: Error) => Promise<void>, onCommitError: (source: string, error: unknown, workflowId: string) => void): Promise<boolean>;
39
+ export declare function reprovideRecoveredServices(internals: EngineInternals, state: WorkflowState, failRun: (workflowId: string, error: Error) => Promise<void>, onCommitError: (source: string, error: unknown, workflowId: string) => void, dispatchDiagnostic?: (event: Event) => void): Promise<boolean>;
40
40
  /**
41
- * The canonical terminal error for a recovered run whose services could not be
42
- * re-provided. Shared by both recovery paths so they fail with an identical
43
- * message (the failure category is `system`, the default for `failWorkflow`).
41
+ * The canonical terminal error for any run whose services could not be provided —
42
+ * both recovery re-provision paths and a scheduled-occurrence launch. Shared so
43
+ * every caller fails with an identical message (the failure category is `system`,
44
+ * the default for `failWorkflow`). The message is recovery-agnostic because a
45
+ * freshly launched scheduled occurrence has never been recovered.
44
46
  */
45
47
  export declare function unavailableServicesError(workflowId: string, reason: string): Error;
@@ -1,12 +1,24 @@
1
1
  import { KEYS } from "../../../storage/interface.js";
2
- export async function reprovideRecoveredServices(internals, state, failRun, onCommitError) {
2
+ import { DevelopmentWarningEvent } from "../../events.js";
3
+ const RESOLVE_WORKFLOW_SERVICES_OPTION_PATH = "EngineOptions.resolveWorkflowServices";
4
+ export async function reprovideRecoveredServices(internals, state, failRun, onCommitError, dispatchDiagnostic = () => {}) {
3
5
  const resolver = internals.options.resolveWorkflowServices;
4
- if (internals.inlineStrategy === null || !resolver)
6
+ if (internals.inlineStrategy === null)
5
7
  return !1;
6
8
  if (internals.workflowServices.has(state.id))
7
9
  return !1;
8
10
  if (await internals.storage.get(KEYS.workflowHasServices(state.id)) === null)
9
11
  return !1;
12
+ if (!resolver) {
13
+ const reason = missingServicesResolverReason(state.id);
14
+ dispatchDiagnostic(new DevelopmentWarningEvent(state.id, reason, [RESOLVE_WORKFLOW_SERVICES_OPTION_PATH]));
15
+ try {
16
+ await failRun(state.id, unavailableServicesError(state.id, reason));
17
+ } catch (error) {
18
+ onCommitError("reprovideRecoveredServices", error, state.id);
19
+ }
20
+ return !0;
21
+ }
10
22
  let reason;
11
23
  try {
12
24
  const resolution = await resolver({
@@ -30,5 +42,8 @@ export async function reprovideRecoveredServices(internals, state, failRun, onCo
30
42
  return !0;
31
43
  }
32
44
  export function unavailableServicesError(workflowId, reason) {
33
- return Error(`Recovered workflow "${workflowId}" services unavailable: ${reason}`);
45
+ return Error(`Workflow "${workflowId}" services unavailable: ${reason}`);
46
+ }
47
+ function missingServicesResolverReason(workflowId) {
48
+ return `Workflow "${workflowId}" was started with services, but this engine has no resolveWorkflowServices option. Configure EngineOptions.resolveWorkflowServices before recovery so ctx.services can be re-provided.`;
34
49
  }
@@ -7,6 +7,8 @@ import { WorkflowResumedEvent } from "../../events.js";
7
7
  import { buildTimerBatchOperations } from "../../scheduler.js";
8
8
  import { createCancelHandlerRegistration, resetCancelHandlers } from "../cancel-handlers.js";
9
9
  import { rememberCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
10
+ import { rehydrateChildCancellationHandlers } from "../child-workflow-cancellation.js";
11
+ import { commitFencedEngineWrite } from "../fenced-write.js";
10
12
  import { getWorkflowExecutionStartedAt } from "../handles.js";
11
13
  import { loadWorkflowState } from "../storage-io.js";
12
14
  import { getComposedWorkflowInterceptor } from "../strategy-helpers.js";
@@ -21,7 +23,7 @@ import {
21
23
  setWorkflowStartHeaders
22
24
  } from "./shared.js";
23
25
  async function prepareRecoveredServicesOrFail(internals, state, callbacks) {
24
- return reprovideRecoveredServices(internals, state, callbacks.failWorkflowForUnavailableServices, callbacks.handleCleanupError);
26
+ return reprovideRecoveredServices(internals, state, callbacks.failWorkflowForUnavailableServices, callbacks.handleCleanupError, callbacks.dispatchEvent);
25
27
  }
26
28
  function assertResumeNotTerminating(internals, workflowId) {
27
29
  if (internals.terminalizingWorkflows.has(workflowId))
@@ -43,9 +45,10 @@ function commitSerializedResumeState(internals, args) {
43
45
  setWorkflowStartHeaders(internals, workflowId, args.workflowStartHeaders, callbacks);
44
46
  internals.parkedInlineWorkflows.delete(workflowId);
45
47
  }
46
- function relaunchInlineWorkflowAfterResume(internals, latestState, args) {
48
+ async function relaunchInlineWorkflowAfterResume(internals, latestState, args) {
47
49
  const { workflowId, resumeCheckpoint, registration, callbacks } = args, accumulatedResults = new Map(resumeCheckpoint.accumulatedResults), workflowAbort = new AbortController;
48
50
  resetCancelHandlers(internals, workflowId);
51
+ await rehydrateChildCancellationHandlers(internals, workflowId, callbacks);
49
52
  const context = new Context({
50
53
  workflowId,
51
54
  workflowType: latestState.type,
@@ -65,7 +68,8 @@ function relaunchInlineWorkflowAfterResume(internals, latestState, args) {
65
68
  ...latestState.executionDeadline !== void 0 && {
66
69
  deadline: latestState.executionDeadline
67
70
  },
68
- services: internals.workflowServices.get(workflowId)
71
+ services: internals.workflowServices.get(workflowId),
72
+ ...internals.options.onLog != null && { logSink: internals.options.onLog }
69
73
  });
70
74
  setContextWorkflowInterceptor(context, getComposedWorkflowInterceptor(internals));
71
75
  if (internals.options.development)
@@ -74,8 +78,11 @@ function relaunchInlineWorkflowAfterResume(internals, latestState, args) {
74
78
  inlineStrategy.adoptWorkflow(workflowId, generator, context, workflowAbort);
75
79
  inlineStrategy.continueWorkflow(workflowId, void 0);
76
80
  }
77
- function relaunchWorkerWorkflowAfterResume(internals, latestState, args) {
78
- const { workflowId, resumeCheckpoint, workflowStartHeaders } = args, serialized = serializeCheckpoint(resumeCheckpoint);
81
+ async function relaunchWorkerWorkflowAfterResume(internals, latestState, args) {
82
+ const { workflowId, resumeCheckpoint, workflowStartHeaders, callbacks } = args;
83
+ resetCancelHandlers(internals, workflowId);
84
+ await rehydrateChildCancellationHandlers(internals, workflowId, callbacks);
85
+ const serialized = serializeCheckpoint(resumeCheckpoint);
79
86
  internals.strategy.startWorkflow({
80
87
  workflowId,
81
88
  workflowType: latestState.type,
@@ -103,10 +110,10 @@ async function performSerializedResume(internals, args) {
103
110
  await reactivateSuspendedWorkflowState(internals, latestState);
104
111
  commitSerializedResumeState(internals, args);
105
112
  if (internals.inlineStrategy) {
106
- relaunchInlineWorkflowAfterResume(internals, latestState, args);
113
+ await relaunchInlineWorkflowAfterResume(internals, latestState, args);
107
114
  return;
108
115
  }
109
- relaunchWorkerWorkflowAfterResume(internals, latestState, args);
116
+ await relaunchWorkerWorkflowAfterResume(internals, latestState, args);
110
117
  }
111
118
  async function reactivateSuspendedWorkflowState(internals, state) {
112
119
  if (state.status !== "suspended")
@@ -114,7 +121,7 @@ async function reactivateSuspendedWorkflowState(internals, state) {
114
121
  const previousState = { ...state };
115
122
  state.status = "running";
116
123
  state.updatedAt = internals.options.getNow();
117
- await internals.storage.batch([
124
+ await commitFencedEngineWrite(internals, [
118
125
  { type: "put", key: KEYS.workflow(state.id), value: encode(state) },
119
126
  ...buildWorkflowVisibilityIndexTransition(state.id, previousState, state).batchOps,
120
127
  ...state.executionDeadline !== void 0 ? buildTimerBatchOperations({
@@ -123,7 +130,7 @@ async function reactivateSuspendedWorkflowState(internals, state) {
123
130
  fireAt: state.executionDeadline,
124
131
  kind: "execution-deadline"
125
132
  }) : []
126
- ]);
133
+ ], [], () => Error(`Resume of workflow "${state.id}" lost its CAS race.`));
127
134
  }
128
135
  export async function resumeWorkflowFromStorage(internals, workflowId, dispatchResumedEvent, callbacks) {
129
136
  const stateBytes = await internals.storage.get(KEYS.workflow(workflowId));
@@ -22,7 +22,7 @@ export declare const EMPTY_STORAGE_VALUE: Uint8Array<ArrayBuffer>;
22
22
  export type RecoverAllOptions = {
23
23
  /**
24
24
  * Skip stored running workflows whose type is not registered. Use only for
25
- * rolling deploys or explicit storage migrations.
25
+ * rolling deploys or explicit operator storage repair.
26
26
  */
27
27
  acknowledgeUnknownWorkflowTypes?: boolean;
28
28
  };
@@ -56,6 +56,13 @@ export type LifecycleCallbacks = {
56
56
  * `error` is the canonical {@link unavailableServicesError}.
57
57
  */
58
58
  failWorkflowForUnavailableServices: (workflowId: string, error: Error) => Promise<void>;
59
+ /**
60
+ * Force a recovered workflow to a terminal `failed` state because its
61
+ * persisted checkpoint cannot be decoded on this runtime. The failure is
62
+ * isolated to the affected run so `recoverAll()` can continue recovering
63
+ * other workflows from the same storage backend.
64
+ */
65
+ failWorkflowForCheckpointDecodeError: (workflowId: string, error: Error) => Promise<void>;
59
66
  };
60
67
  /**
61
68
  * Pre-replay history circuit breaker. Called at every restore-from-checkpoint
@@ -2,6 +2,6 @@ import type { BatchOperation } from '../../../storage/interface.ts';
2
2
  import type { Checkpoint, SearchAttributeValue, StartOptions, TimerEntry, WorkflowState } from '../../types.ts';
3
3
  import type { EngineInternals } from '../internals.ts';
4
4
  import { type LifecycleCallbacks, type RegistrationEntry } from './shared.ts';
5
- export declare function buildStartBatchOperations(_internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, registration: RegistrationEntry, options: StartOptions | undefined, executionDeadline: number | undefined, delayedStartTimer: TimerEntry | undefined, workflowStartHeaders: Map<string, string> | undefined, additionalOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks): BatchOperation[];
5
+ export declare function buildStartBatchOperations(_internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, registration: RegistrationEntry, options: StartOptions | undefined, executionDeadline: number | undefined, delayedStartTimer: TimerEntry | undefined, workflowStartHeaders: Map<string, string> | undefined, additionalOperations: BatchOperation[] | undefined, callbacks: LifecycleCallbacks, purgeDeleteOperations: BatchOperation[] | undefined): BatchOperation[];
6
6
  export declare function buildInitialSearchAttributeOperations(_internals: EngineInternals, workflowId: string, registration: RegistrationEntry, searchAttributes: StartOptions['searchAttributes'], callbacks: LifecycleCallbacks): BatchOperation[];
7
7
  export declare function validateSearchAttributes(_internals: EngineInternals, registration: RegistrationEntry, searchAttributes: Record<string, SearchAttributeValue>, _callbacks: LifecycleCallbacks): void;
@@ -8,8 +8,9 @@ import { validateAttributeValueSizes } from "../attributes-tags.js";
8
8
  import { encodeWorkflowStartHeaders } from "../state-utilities.js";
9
9
  import { buildWorkflowVisibilityIndexOperations } from "../workflow-indexes.js";
10
10
  import { EMPTY_STORAGE_VALUE } from "./shared.js";
11
- export function buildStartBatchOperations(_internals, workflowId, state, checkpoint, registration, options, executionDeadline, delayedStartTimer, workflowStartHeaders, additionalOperations, callbacks) {
11
+ export function buildStartBatchOperations(_internals, workflowId, state, checkpoint, registration, options, executionDeadline, delayedStartTimer, workflowStartHeaders, additionalOperations, callbacks, purgeDeleteOperations) {
12
12
  const visibilityIndexOperations = buildWorkflowVisibilityIndexOperations(workflowId, null, state).batchOps, operations = [
13
+ ...purgeDeleteOperations ?? [],
13
14
  { type: "put", key: KEYS.workflow(workflowId), value: encode(state) },
14
15
  {
15
16
  type: "put",