@adonis-agora/durable 0.58.3 → 0.60.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 (219) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/assets/spa/assets/index-70hGzOnT.css +1 -0
  3. package/dist/assets/spa/assets/index-BI1EANJO.js +16 -0
  4. package/dist/assets/spa/assets/index-BI1EANJO.js.map +1 -0
  5. package/dist/assets/spa/index.html +2 -2
  6. package/dist/commands/lifecycle_republish.d.ts +23 -0
  7. package/dist/commands/lifecycle_republish.d.ts.map +1 -0
  8. package/dist/commands/lifecycle_republish.js +64 -0
  9. package/dist/commands/lifecycle_republish.js.map +1 -0
  10. package/dist/commands/main.d.ts +4 -3
  11. package/dist/commands/main.d.ts.map +1 -1
  12. package/dist/commands/main.js +3 -1
  13. package/dist/commands/main.js.map +1 -1
  14. package/dist/providers/dashboard_provider.d.ts +2 -0
  15. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  16. package/dist/providers/dashboard_provider.js +9 -1
  17. package/dist/providers/dashboard_provider.js.map +1 -1
  18. package/dist/providers/durable_provider.d.ts.map +1 -1
  19. package/dist/providers/durable_provider.js +3 -0
  20. package/dist/providers/durable_provider.js.map +1 -1
  21. package/dist/src/codec-state-store.d.ts +10 -1
  22. package/dist/src/codec-state-store.d.ts.map +1 -1
  23. package/dist/src/codec-state-store.js +25 -0
  24. package/dist/src/codec-state-store.js.map +1 -1
  25. package/dist/src/commands/index.d.ts +1 -1
  26. package/dist/src/commands/index.d.ts.map +1 -1
  27. package/dist/src/commands/index.js +1 -1
  28. package/dist/src/commands/index.js.map +1 -1
  29. package/dist/src/commands/runs.d.ts +24 -1
  30. package/dist/src/commands/runs.d.ts.map +1 -1
  31. package/dist/src/commands/runs.js +50 -0
  32. package/dist/src/commands/runs.js.map +1 -1
  33. package/dist/src/commands/worker.d.ts.map +1 -1
  34. package/dist/src/commands/worker.js +3 -0
  35. package/dist/src/commands/worker.js.map +1 -1
  36. package/dist/src/dashboard/gateway-adapter.d.ts +5 -0
  37. package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -1
  38. package/dist/src/dashboard/gateway-adapter.js +20 -0
  39. package/dist/src/dashboard/gateway-adapter.js.map +1 -1
  40. package/dist/src/dashboard/handlers.d.ts +29 -1
  41. package/dist/src/dashboard/handlers.d.ts.map +1 -1
  42. package/dist/src/dashboard/handlers.js +103 -0
  43. package/dist/src/dashboard/handlers.js.map +1 -1
  44. package/dist/src/dashboard/index.d.ts +2 -2
  45. package/dist/src/dashboard/index.d.ts.map +1 -1
  46. package/dist/src/dashboard/index.js +2 -2
  47. package/dist/src/dashboard/index.js.map +1 -1
  48. package/dist/src/dashboard/openapi.d.ts.map +1 -1
  49. package/dist/src/dashboard/openapi.js +39 -0
  50. package/dist/src/dashboard/openapi.js.map +1 -1
  51. package/dist/src/define_config.d.ts +13 -0
  52. package/dist/src/define_config.d.ts.map +1 -1
  53. package/dist/src/define_config.js.map +1 -1
  54. package/dist/src/diagnostics-bridge.d.ts +5 -1
  55. package/dist/src/diagnostics-bridge.d.ts.map +1 -1
  56. package/dist/src/diagnostics-bridge.js +16 -2
  57. package/dist/src/diagnostics-bridge.js.map +1 -1
  58. package/dist/src/engine.d.ts +426 -6
  59. package/dist/src/engine.d.ts.map +1 -1
  60. package/dist/src/engine.js +1951 -101
  61. package/dist/src/engine.js.map +1 -1
  62. package/dist/src/errors.d.ts.map +1 -1
  63. package/dist/src/errors.js +4 -0
  64. package/dist/src/errors.js.map +1 -1
  65. package/dist/src/event-accumulators.d.ts +28 -5
  66. package/dist/src/event-accumulators.d.ts.map +1 -1
  67. package/dist/src/event-accumulators.js +102 -21
  68. package/dist/src/event-accumulators.js.map +1 -1
  69. package/dist/src/events.d.ts +25 -0
  70. package/dist/src/events.d.ts.map +1 -1
  71. package/dist/src/events.js +24 -0
  72. package/dist/src/events.js.map +1 -1
  73. package/dist/src/handshake/announced.d.ts +6 -0
  74. package/dist/src/handshake/announced.d.ts.map +1 -1
  75. package/dist/src/handshake/announced.js +20 -0
  76. package/dist/src/handshake/announced.js.map +1 -1
  77. package/dist/src/handshake/descriptor.d.ts +6 -0
  78. package/dist/src/handshake/descriptor.d.ts.map +1 -1
  79. package/dist/src/handshake/descriptor.js +1 -0
  80. package/dist/src/handshake/descriptor.js.map +1 -1
  81. package/dist/src/index.d.ts +3 -2
  82. package/dist/src/index.d.ts.map +1 -1
  83. package/dist/src/index.js +3 -2
  84. package/dist/src/index.js.map +1 -1
  85. package/dist/src/interfaces.d.ts +139 -3
  86. package/dist/src/interfaces.d.ts.map +1 -1
  87. package/dist/src/interfaces.js +11 -0
  88. package/dist/src/interfaces.js.map +1 -1
  89. package/dist/src/lifecycle-events.d.ts +372 -0
  90. package/dist/src/lifecycle-events.d.ts.map +1 -0
  91. package/dist/src/lifecycle-events.js +383 -0
  92. package/dist/src/lifecycle-events.js.map +1 -0
  93. package/dist/src/metrics.d.ts +15 -0
  94. package/dist/src/metrics.d.ts.map +1 -1
  95. package/dist/src/metrics.js +36 -1
  96. package/dist/src/metrics.js.map +1 -1
  97. package/dist/src/otel/durable-metrics.d.ts +9 -0
  98. package/dist/src/otel/durable-metrics.d.ts.map +1 -1
  99. package/dist/src/otel/durable-metrics.js +17 -0
  100. package/dist/src/otel/durable-metrics.js.map +1 -1
  101. package/dist/src/otel/durable-otel.d.ts +7 -0
  102. package/dist/src/otel/durable-otel.d.ts.map +1 -1
  103. package/dist/src/otel/durable-otel.js +69 -3
  104. package/dist/src/otel/durable-otel.js.map +1 -1
  105. package/dist/src/run-detail-summary.d.ts +48 -1
  106. package/dist/src/run-detail-summary.d.ts.map +1 -1
  107. package/dist/src/run-detail-summary.js +30 -2
  108. package/dist/src/run-detail-summary.js.map +1 -1
  109. package/dist/src/run-gateway/interface.d.ts +16 -0
  110. package/dist/src/run-gateway/interface.d.ts.map +1 -1
  111. package/dist/src/run-gateway/proxy-run-gateway.d.ts +31 -1
  112. package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -1
  113. package/dist/src/run-gateway/proxy-run-gateway.js +59 -7
  114. package/dist/src/run-gateway/proxy-run-gateway.js.map +1 -1
  115. package/dist/src/run-gateway/run-request-responder.d.ts +4 -29
  116. package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -1
  117. package/dist/src/run-gateway/run-request-responder.js +183 -6
  118. package/dist/src/run-gateway/run-request-responder.js.map +1 -1
  119. package/dist/src/run-gateway/store-run-gateway.d.ts +9 -0
  120. package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -1
  121. package/dist/src/run-gateway/store-run-gateway.js +8 -0
  122. package/dist/src/run-gateway/store-run-gateway.js.map +1 -1
  123. package/dist/src/run-waiting.d.ts +1 -0
  124. package/dist/src/run-waiting.d.ts.map +1 -1
  125. package/dist/src/run-waiting.js +6 -1
  126. package/dist/src/run-waiting.js.map +1 -1
  127. package/dist/src/sticky-continuation.d.ts +141 -0
  128. package/dist/src/sticky-continuation.d.ts.map +1 -0
  129. package/dist/src/sticky-continuation.js +231 -0
  130. package/dist/src/sticky-continuation.js.map +1 -0
  131. package/dist/src/stores/factory.d.ts +6 -0
  132. package/dist/src/stores/factory.d.ts.map +1 -1
  133. package/dist/src/stores/factory.js +3 -0
  134. package/dist/src/stores/factory.js.map +1 -1
  135. package/dist/src/stores/lucid-pg-hot-path.d.ts +30 -0
  136. package/dist/src/stores/lucid-pg-hot-path.d.ts.map +1 -0
  137. package/dist/src/stores/lucid-pg-hot-path.js +67 -0
  138. package/dist/src/stores/lucid-pg-hot-path.js.map +1 -0
  139. package/dist/src/stores/lucid.d.ts +30 -1
  140. package/dist/src/stores/lucid.d.ts.map +1 -1
  141. package/dist/src/stores/lucid.js +460 -17
  142. package/dist/src/stores/lucid.js.map +1 -1
  143. package/dist/src/telescope/dashboard.d.ts.map +1 -1
  144. package/dist/src/telescope/dashboard.js +50 -0
  145. package/dist/src/telescope/dashboard.js.map +1 -1
  146. package/dist/src/telescope/data-providers.d.ts +19 -0
  147. package/dist/src/telescope/data-providers.d.ts.map +1 -1
  148. package/dist/src/telescope/data-providers.js +128 -2
  149. package/dist/src/telescope/data-providers.js.map +1 -1
  150. package/dist/src/telescope/extension.d.ts.map +1 -1
  151. package/dist/src/telescope/extension.js +4 -1
  152. package/dist/src/telescope/extension.js.map +1 -1
  153. package/dist/src/telescope/index.d.ts +3 -2
  154. package/dist/src/telescope/index.d.ts.map +1 -1
  155. package/dist/src/telescope/index.js +2 -1
  156. package/dist/src/telescope/index.js.map +1 -1
  157. package/dist/src/telescope/telescope-sdk.d.ts +16 -0
  158. package/dist/src/telescope/telescope-sdk.d.ts.map +1 -1
  159. package/dist/src/telescope/watcher.d.ts +66 -0
  160. package/dist/src/telescope/watcher.d.ts.map +1 -0
  161. package/dist/src/telescope/watcher.js +185 -0
  162. package/dist/src/telescope/watcher.js.map +1 -0
  163. package/dist/src/testing/in-memory-state-store.d.ts +12 -1
  164. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
  165. package/dist/src/testing/in-memory-state-store.js +109 -1
  166. package/dist/src/testing/in-memory-state-store.js.map +1 -1
  167. package/dist/src/testing-kit/harness.d.ts +1 -1
  168. package/dist/src/testing-kit/harness.d.ts.map +1 -1
  169. package/dist/src/testing-kit/harness.js +3 -1
  170. package/dist/src/testing-kit/harness.js.map +1 -1
  171. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
  172. package/dist/src/testing-kit/state-store-conformance.js +258 -0
  173. package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
  174. package/dist/src/transport-pool.d.ts +10 -0
  175. package/dist/src/transport-pool.d.ts.map +1 -1
  176. package/dist/src/transport-pool.js +23 -2
  177. package/dist/src/transport-pool.js.map +1 -1
  178. package/dist/src/transports/bullmq/bullmq-transport.d.ts +41 -1
  179. package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
  180. package/dist/src/transports/bullmq/bullmq-transport.js +283 -5
  181. package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
  182. package/dist/src/transports/bullmq/deps.d.ts +12 -1
  183. package/dist/src/transports/bullmq/deps.d.ts.map +1 -1
  184. package/dist/src/transports/bullmq/deps.js.map +1 -1
  185. package/dist/src/transports/bullmq/naming.d.ts +10 -0
  186. package/dist/src/transports/bullmq/naming.d.ts.map +1 -1
  187. package/dist/src/transports/bullmq/naming.js +16 -0
  188. package/dist/src/transports/bullmq/naming.js.map +1 -1
  189. package/dist/src/transports/db.d.ts +3 -1
  190. package/dist/src/transports/db.d.ts.map +1 -1
  191. package/dist/src/transports/db.js +4 -4
  192. package/dist/src/transports/db.js.map +1 -1
  193. package/dist/src/transports/queue.d.ts +3 -1
  194. package/dist/src/transports/queue.d.ts.map +1 -1
  195. package/dist/src/transports/queue.js +2 -2
  196. package/dist/src/transports/queue.js.map +1 -1
  197. package/dist/src/transports/worker-keys.d.ts +2 -1
  198. package/dist/src/transports/worker-keys.d.ts.map +1 -1
  199. package/dist/src/transports/worker-keys.js +3 -2
  200. package/dist/src/transports/worker-keys.js.map +1 -1
  201. package/dist/src/workflow-ctx.d.ts +7 -0
  202. package/dist/src/workflow-ctx.d.ts.map +1 -1
  203. package/dist/src/workflow-ctx.js +90 -1
  204. package/dist/src/workflow-ctx.js.map +1 -1
  205. package/dist/src/workflow-discovery.d.ts.map +1 -1
  206. package/dist/src/workflow-discovery.js +1 -0
  207. package/dist/src/workflow-discovery.js.map +1 -1
  208. package/dist/src/workflow-events.d.ts +101 -0
  209. package/dist/src/workflow-events.d.ts.map +1 -1
  210. package/dist/src/workflow-events.js +79 -1
  211. package/dist/src/workflow-events.js.map +1 -1
  212. package/dist/src/workflow-ref.d.ts +17 -2
  213. package/dist/src/workflow-ref.d.ts.map +1 -1
  214. package/dist/src/workflow-ref.js.map +1 -1
  215. package/package.json +5 -5
  216. package/skills/durable-cluster/SKILL.md +4 -2
  217. package/dist/assets/spa/assets/index-6nmSTfT7.css +0 -1
  218. package/dist/assets/spa/assets/index-Dj0X-IAu.js +0 -16
  219. package/dist/assets/spa/assets/index-Dj0X-IAu.js.map +0 -1
@@ -2,9 +2,11 @@ import { type AdmissionBackend } from './admission.js';
2
2
  import { type ConcurrencyConfig, type ConcurrencyQuota } from './concurrency.js';
3
3
  import { type EntityConfig } from './entities.js';
4
4
  import { type EventBatchConfig } from './event-accumulators.js';
5
+ import { type OnEventEntry } from './events.js';
5
6
  import { type AnnouncedWorkflow } from './handshake/announced.js';
6
7
  import { type WorkflowDirectory } from './handshake/directory.js';
7
8
  import type { ControlPlane, EngineListener, GroupHealth, NamedTransport, RunDispatch, RunDispatcher, RunFacetQuery, RunFacetRow, RunQuery, RunResult, RunScope, RunStatus, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScopedRetentionPolicy, SearchAttributes, SearchAttributesSchema, SignalWaiter, StateStore, StepCheckpoint, StepError, StepInterceptor, Transport, UpdateResult, UpdateValidator, WorkflowCtx, WorkflowExecutor, WorkflowRun } from './interfaces.js';
9
+ import { type LifecycleKind, type RepublishLifecycleResult, type RepublishLifecycleTarget, type WorkflowLifecycleLinks } from './lifecycle-events.js';
8
10
  import { type OnErrorRoute } from './on-error.js';
9
11
  import { ORIGIN_RESOLVED } from './origin.js';
10
12
  import { type StepHandler } from './protocol.js';
@@ -358,6 +360,25 @@ export interface WorkflowEngineDeps {
358
360
  * run `runPending` on a worker pod to pick those up; or a broker-backed one for a worker pool.
359
361
  */
360
362
  runDispatcher?: RunDispatcher | undefined;
363
+ /**
364
+ * How many parked workflow bodies this instance keeps in memory between turns (the sticky
365
+ * continuation cache, see `sticky-continuation.ts`). A run whose body parked here on dispatched
366
+ * `ctx.step` calls continues from where it stopped when its next turn runs on this instance,
367
+ * instead of replaying from the top over its whole history: the turn re-reads only the steps it
368
+ * parked on. Least recently used out first; an evicted run just replays. Default 2000. `0` turns
369
+ * the cache off (every turn replays).
370
+ */
371
+ stickyRuns?: number | undefined;
372
+ /** How long a parked body may sit idle in the sticky cache before it is dropped. Default 5 min. */
373
+ stickyIdleMs?: number | undefined;
374
+ /**
375
+ * Ask the transports that can ({@link Transport.consumeRepliesFor}, e.g. BullMQ) to send the result
376
+ * of a dispatched `ctx.step` back to this instance, which holds the body parked on it, rather than
377
+ * to whichever engine instance takes it off the shared results. With several engine instances,
378
+ * that is what keeps the sticky continuation hitting. A result whose instance is gone goes to the
379
+ * shared results and replays elsewhere. Default `true` while the sticky cache is on.
380
+ */
381
+ routeReplies?: boolean | undefined;
361
382
  /**
362
383
  * Capabilities the control-plane itself advertises in its handshake descriptor (design §7.3). The
363
384
  * CP is a router, not an executor, so this is rarely needed — leave empty. Never gates which workers
@@ -438,6 +459,16 @@ export declare class WorkflowEngine {
438
459
  private readonly maxRecoveryAttempts?;
439
460
  private readonly unregisteredWorkflowGraceMs;
440
461
  private readonly reconcileMs;
462
+ /** Parked workflow bodies kept between turns (see {@link WorkflowEngineDeps.stickyRuns}). */
463
+ private readonly sticky;
464
+ /** Pending runs claimed by {@link runPending} for the turn about to run them, as read by the claim. */
465
+ private readonly claimedRuns;
466
+ /** The continuation each turn executing here runs in, by run id, while the turn runs. */
467
+ private readonly turnContinuations;
468
+ /** Whether a transport routes this instance's step results back to it (see `routeReplies`). */
469
+ private repliesRouted;
470
+ /** The run row stamp (`updatedAt`, epoch ms) a turn here wrote when it settled `suspended`. */
471
+ private readonly suspendStamps;
441
472
  private readonly heartbeatPersistThrottleMs;
442
473
  private readonly timerBatchSize;
443
474
  private readonly eventBufferTtlMs;
@@ -481,7 +512,7 @@ export declare class WorkflowEngine {
481
512
  private readonly workflows;
482
513
  /** The newest registered version per workflow name — used to `start` new runs. */
483
514
  private readonly latest;
484
- /** Event name → workflow names started when that event is published (see `onEvent`). */
515
+ /** Event name → subscribing workflow name → that workflow's subscriptions to it. See `onEvent`. */
485
516
  private readonly eventTriggers;
486
517
  /** Durable-entity subsystem (registers the `__entity` runner; see `registerEntity`). */
487
518
  private readonly entities;
@@ -757,7 +788,7 @@ export declare class WorkflowEngine {
757
788
  inputSchema?: StandardSchemaV1 | undefined;
758
789
  /** Typed/validated `searchAttributes` shape — see `RegisteredWorkflow.searchAttributesSchema`. */
759
790
  searchAttributesSchema?: SearchAttributesSchema | undefined;
760
- onEvent?: string[] | undefined;
791
+ onEvent?: OnEventEntry[] | undefined;
761
792
  eventBatch?: EventBatchConfig | undefined;
762
793
  requires?: string[] | undefined;
763
794
  /**
@@ -921,9 +952,336 @@ export declare class WorkflowEngine {
921
952
  * that itself goes dead is not routed again — no `dlq:dlq:…` chain.
922
953
  */
923
954
  private routeDeadLetter;
924
- /** Emit a locally-produced lifecycle event: deliver to subscribers AND broadcast it on the
925
- * control plane so other instances (e.g. a dashboard pod) can live-tail this run. */
955
+ /**
956
+ * Emit a locally-produced lifecycle event: deliver to subscribers AND broadcast it on the control
957
+ * plane so other instances (e.g. a dashboard pod) can live-tail this run. A `run.completed` or
958
+ * `run.failed` also wakes the `ctx.waitForRun` waiters on the run and publishes its lifecycle
959
+ * event (see {@link publishLifecycle}); `lifecycle: false` marks a `run.completed` that is not a
960
+ * real completion (a continue-as-new handoff), which publishes no lifecycle event.
961
+ */
926
962
  private emit;
963
+ /**
964
+ * Start every workflow subscribed to `name` that takes `payload` (see {@link acceptingSubscription}),
965
+ * each under `runIdOf(workflow)` and with its subscription's `input` applied; only the ones in
966
+ * `only` when given. Returns the subscribers reached, with the run each started (none when the
967
+ * payload went into its debounce/batch accumulator) and whether that run is new, and the ones that
968
+ * could not be started, with why.
969
+ *
970
+ * `remote` names subscribers known only from the fleet's announcements (see
971
+ * {@link announcedRemoteSubscribers}): each is started by name with the payload as its input, with
972
+ * no `where` or `input` mapping, unless it is also registered here.
973
+ */
974
+ private startSubscribers;
975
+ /** The workflows registered on this instance that subscribe to `event`. */
976
+ private localSubscribers;
977
+ /**
978
+ * Whether this engine's own subscribers are owed the lifecycle events of `namespace`'s runs: always
979
+ * for an engine with no namespace (its sweep reads every namespace's runs), else only its own
980
+ * namespace's. `undefined` asks without a namespace and is always true.
981
+ */
982
+ private servesNamespace;
983
+ /** The workflows registered here that subscribe to `event` and serve `namespace`'s runs. */
984
+ private localSubscribersFor;
985
+ /**
986
+ * The subscribers of `event` this engine starts by name although it has no `onEvent` for them:
987
+ * announced only by non-Node workers (a Python `@worker.workflow(..., on_workflow_completed=...)`),
988
+ * and reachable from here, either registered (`registerRemote`) or served by convention on a live
989
+ * worker group of the same name. A subscriber some Node engine announces is left to that engine,
990
+ * which applies its own `where` and `input`; one this engine cannot reach stays owed, for an
991
+ * instance that can. Read from the snapshot {@link announcedSubscribers} keeps. With `namespace`,
992
+ * only the subscriptions announced for that namespace's runs (see {@link announcedFor}).
993
+ */
994
+ private announcedRemoteSubscribers;
995
+ /**
996
+ * The subscribers of `event` this engine can deliver to for a run of `namespace`: the ones
997
+ * registered here that serve it, and the announced ones it starts by name (see
998
+ * {@link announcedRemoteSubscribers}).
999
+ */
1000
+ private deliverableSubscribers;
1001
+ /**
1002
+ * Every workflow a run of `workflow` settling as `kind` owes its lifecycle event to: the ones
1003
+ * registered here plus the ones the fleet announces (from a snapshot of the lifecycle subscription
1004
+ * registry and the worker descriptors at most 5s old), sorted.
1005
+ *
1006
+ * With `namespace` (the settled run's), only the subscribers that serve that namespace's runs: the
1007
+ * ones registered here when this engine has no namespace or has that one, the registry rows of
1008
+ * engines in that namespace or with none, and the worker descriptors serving it. What the
1009
+ * terminal status write marks the run owed to.
1010
+ */
1011
+ lifecycleSubscribers(workflow: string, kind: LifecycleKind, namespace?: string | undefined): string[];
1012
+ /**
1013
+ * Who starts whom through lifecycle events, for `workflow`: the workflows that start when it
1014
+ * settles, and the workflows whose settling starts it. Built from this engine's registry and the
1015
+ * subscriptions live workers announce (read again when the snapshot is older than 5s); a failed
1016
+ * fleet read leaves the local registry alone. What the dashboard's workflow page shows.
1017
+ *
1018
+ * With `namespace`, only the subscriptions of that namespace: the ones announced by registry runs
1019
+ * and worker descriptors serving it, and this engine's own when it is that namespace's engine (an
1020
+ * engine with no namespace is `default`'s). What a tenant is shown.
1021
+ */
1022
+ workflowLifecycle(workflow: string, opts?: {
1023
+ namespace?: string | undefined;
1024
+ }): Promise<WorkflowLifecycleLinks>;
1025
+ /**
1026
+ * `patch` (a terminal status write of `run`) plus the {@link LIFECYCLE_PENDING_TAG} marker and one
1027
+ * {@link LIFECYCLE_OWED_TAG_PREFIX} tag per subscriber of that ending, and the run's span as a
1028
+ * {@link LIFECYCLE_TRACE_TAG_PREFIX} tag when a run trace provider knows it. Written in the same
1029
+ * update as the status, so a run can never be settled without a record of whom its lifecycle event
1030
+ * is owed to.
1031
+ *
1032
+ * While this instance has no successful read of the fleet's announced subscriptions (none yet, or
1033
+ * the last one failed), it cannot know who else subscribes, so the marker is the pending tag alone:
1034
+ * the run's owed set is unresolved, and the publish that next reads the fleet resolves it (see
1035
+ * {@link publishSettled}). Never waits on that read.
1036
+ */
1037
+ private withLifecycleMarker;
1038
+ /** Answers the traceparent of a run's span in this process; see {@link setRunTraceparent}. */
1039
+ private runTraceparent;
1040
+ /**
1041
+ * Install the function that answers the W3C `traceparent` of a run's span in this process, or
1042
+ * `undefined` to remove it. `attachDurableOtel` installs one. With it, a run some workflow
1043
+ * subscribes to records its traceparent (a {@link LIFECYCLE_TRACE_TAG_PREFIX} tag) with its first
1044
+ * suspend (see {@link withRunTrace}), or else with its terminal write, and the `run.started` of a
1045
+ * run a lifecycle event started carries its source run's as `sourceTraceparent`, so the
1046
+ * subscriber's trace links to the source's whichever process ran either.
1047
+ */
1048
+ setRunTraceparent(provider: ((runId: string) => string | undefined) | undefined): void;
1049
+ /**
1050
+ * `patch` (a suspend write of `run`) plus the run's {@link LIFECYCLE_TRACE_TAG_PREFIX} tag, when a
1051
+ * run trace provider is installed (see {@link setRunTraceparent}), some workflow subscribes to the
1052
+ * run's ending, the provider answers for the run and the run does not carry the tag yet. The first
1053
+ * turn's suspend records the run's root span this way, so a run that settles in a process that
1054
+ * never saw its first turn still links its subscribers' traces; a workflow nobody subscribes to
1055
+ * gets no tag and no extra read or write. The tags are read again before they are written, so a
1056
+ * tag the turn added is kept.
1057
+ */
1058
+ private withRunTrace;
1059
+ /**
1060
+ * `{ sourceTraceparent }` for the `run.started` of `run`, when a lifecycle event started it and a
1061
+ * run trace provider is installed: the source run's recorded traceparent, else this process's.
1062
+ */
1063
+ private sourceTraceOf;
1064
+ /** How many registry rows one page of a read of the lifecycle subscription registry lists. */
1065
+ private static readonly LIFECYCLE_REGISTRY_PAGE_SIZE;
1066
+ /** How long a read of the subscriptions the fleet announces is reused. */
1067
+ private static readonly ANNOUNCED_SUBSCRIPTIONS_TTL_MS;
1068
+ /** The last read of the fleet's announced subscriptions; see {@link announcedSubscribers}. */
1069
+ private announcedSubscriptionsSnapshot;
1070
+ private announcedSubscriptionsRead;
1071
+ /**
1072
+ * The workflows the fleet announces as subscribers of `event`: the live rows of the lifecycle
1073
+ * subscription registry (see {@link LIFECYCLE_REGISTRY_WORKFLOW}) and the live worker descriptors'
1074
+ * `registrations[].subscribes`, from a snapshot refreshed in the background once it is older than
1075
+ * {@link ANNOUNCED_SUBSCRIPTIONS_TTL_MS}. Synchronous, so a terminal status write never waits on
1076
+ * the fleet; before the first read completes it answers with nothing.
1077
+ */
1078
+ private announcedSubscribers;
1079
+ /**
1080
+ * The subscribers of `event` in the current snapshot that serve `namespace`'s runs: announced for
1081
+ * that namespace, or by an engine with no namespace (see {@link LifecycleRegistryEntry}). Every
1082
+ * announced subscriber when `namespace` is undefined. `node` keeps only the ones Node engines
1083
+ * announce (registry rows and Node worker descriptors), which start their own subscribers.
1084
+ */
1085
+ private announcedFor;
1086
+ /**
1087
+ * Read the fleet's announced subscriptions into the snapshot: the registry rows whose heartbeat
1088
+ * is at most {@link LIFECYCLE_REGISTRY_TTL_MS} old (a range of the store's `created_at` index, read
1089
+ * page by page until a short page, so a large fleet is never cut off), and
1090
+ * the worker descriptors on a transport that can read them fleet-wide. Never throws: a failed read
1091
+ * of either leaves an empty snapshot marked not `ok`, which counts as the local registry only and
1092
+ * never orphans a subscriber. Concurrent callers share one read.
1093
+ */
1094
+ private refreshAnnouncedSubscriptions;
1095
+ /** Read the fleet's announced subscriptions again when the snapshot is older than the TTL. */
1096
+ private freshAnnouncedSubscriptions;
1097
+ /** When this engine last wrote its registry row; see {@link heartbeatLifecycleRegistry}. */
1098
+ private lifecycleRegistryBeatAt;
1099
+ /** When this engine last deleted stale registry rows; undefined before its first pass. */
1100
+ private lifecycleRegistryCleanedAt;
1101
+ /** How often an engine with a registry row looks for stale rows to delete. */
1102
+ private static readonly LIFECYCLE_REGISTRY_CLEANUP_MS;
1103
+ /**
1104
+ * This engine's lifecycle registry entry: each workflow registered here that subscribes to an
1105
+ * event, with the events it subscribes to. Undefined when none does.
1106
+ */
1107
+ private lifecycleRegistryEntry;
1108
+ /**
1109
+ * Write this engine's row of the lifecycle subscription registry (see
1110
+ * {@link LIFECYCLE_REGISTRY_WORKFLOW}), at most once per {@link LIFECYCLE_REGISTRY_HEARTBEAT_MS}:
1111
+ * an update of the existing row, or a create when it is missing (first beat, or deleted as stale
1112
+ * after a long pause). Once a minute it also deletes rows whose heartbeat stopped
1113
+ * {@link LIFECYCLE_REGISTRY_STALE_MS} ago: the ones that went stale in the last
1114
+ * {@link LIFECYCLE_REGISTRY_STALE_MS}, a bounded range of the `created_at` index, and on its first
1115
+ * pass every stale row, for the ones left while no engine ran. The row is created tagged
1116
+ * {@link LIFECYCLE_REGISTRY_TAG}, which retention keeps. A no-op for an engine without
1117
+ * subscriptions. Its store calls are marked as heartbeats for observability tools. Never throws.
1118
+ */
1119
+ heartbeatLifecycleRegistry(): Promise<void>;
1120
+ /**
1121
+ * The runs started by `runId` settling (see {@link publishLifecycle}), read from the run's own
1122
+ * tags, so it needs no subscriber registry. Empty for a run that has not settled or started
1123
+ * nobody. Pass the run itself when it is already loaded, to skip reading it again.
1124
+ */
1125
+ triggeredRuns(source: string | WorkflowRun): Promise<string[]>;
1126
+ /** The live path of {@link publishSettled}: right after this instance settled the run. */
1127
+ private publishLifecycle;
1128
+ /**
1129
+ * Take the run lease for this instance's turn, as `tryLockRun` at `nowMs`; when the lease is held
1130
+ * by a lifecycle publish (see {@link withLifecycleLease}), which only ever holds a settled run for
1131
+ * the length of its publish (an explicit retry can land in it), wait for that publish to let go,
1132
+ * at most `leaseMs`.
1133
+ */
1134
+ private lockRunPastLifecyclePublish;
1135
+ /** The waits between attempts to take a settled run's lifecycle lease (see {@link withLifecycleLease}). */
1136
+ private static readonly LIFECYCLE_LEASE_RETRY_MS;
1137
+ /**
1138
+ * Run `fn` holding the run lease of the settled run `runId` under an owner unique to this call, so
1139
+ * every read-start-write of a run's lifecycle tags (live publish, sweeps, heal, republish, on any
1140
+ * instance or twice in this one) runs one at a time. A refused lease is asked for again after each
1141
+ * of {@link LIFECYCLE_LEASE_RETRY_MS}; still refused, resolves `{ acquired: false }` without
1142
+ * running `fn`. The lease is released when `fn` settles, and expires after `leaseMs` if this
1143
+ * process dies first.
1144
+ */
1145
+ private withLifecycleLease;
1146
+ /**
1147
+ * Publish a settled run's lifecycle event (`run.completed:<workflow>` / `run.failed:<workflow>`)
1148
+ * to the subscribers it owes it to that this instance can deliver to (registered here, or announced
1149
+ * and started by name; see {@link deliverableSubscribers}), and settle their part of the marker:
1150
+ * drop each one's {@link LIFECYCLE_OWED_TAG_PREFIX} tag, record each started one as a
1151
+ * `durable:triggered:<workflow>` tag (a coalescing one as `durable:coalesced:<workflow>`), and drop
1152
+ * {@link LIFECYCLE_PENDING_TAG} once no owed tag is left.
1153
+ *
1154
+ * A run marked pending with no owed tag has an unresolved owed set (see
1155
+ * {@link LIFECYCLE_PENDING_TAG}). With a successful read of the fleet's subscriptions it is resolved
1156
+ * here: every subscriber of the ending (see {@link lifecycleSubscribers}) the run's tags do not
1157
+ * record as started or failed becomes owed, and the ones nobody here delivers to keep an owed tag.
1158
+ * Without one, the subscribers registered here are started and the run stays unresolved.
1159
+ *
1160
+ * `republish` publishes to every subscriber of the ending (or only `subscriber`, which must be one),
1161
+ * owed or not: the ones this instance can deliver to are started, and the others are marked owed
1162
+ * again (with the pending tag) for the sweep of an instance that can. `heal` names subscribers of
1163
+ * the ending the run was never marked for (see {@link healLifecycleEvents}): they are owed now, and
1164
+ * the ones this instance delivers to are started.
1165
+ * `nowMs` (the sweep's clock) also drops the owed tag of a subscriber that is neither registered
1166
+ * here nor announced by a live worker once the run settled {@link LIFECYCLE_ORPHAN_MS} ago,
1167
+ * reporting it as `run.trigger_failed`.
1168
+ *
1169
+ * Runs under the run's lifecycle lease (see {@link withLifecycleLease}) and reads the run again
1170
+ * under it, so concurrent publishers never overwrite each other's tags; `busy` when the lease
1171
+ * stayed taken. Idempotent, since every subscriber run id is deterministic and a debounce/batch
1172
+ * accumulator drops an event it already holds. A subscriber whose `where` throws, whose input is
1173
+ * rejected or whose start fails, and an orphan the sweep drops, gets a
1174
+ * {@link LIFECYCLE_TRIGGER_FAILED_TAG_PREFIX} tag in the same tag write, and is not retried until a
1175
+ * republish; the write that records a subscriber reached removes its trigger-failed tag.
1176
+ * A debounce/batch subscriber routed into its accumulator gets a
1177
+ * {@link LIFECYCLE_COALESCED_TAG_PREFIX} tag in the same write instead of a triggered one; a
1178
+ * republish routes it again only when the run also carries its trigger-failed tag.
1179
+ * `run.triggered` and `run.trigger_failed` are emitted only for the subscribers whose triggered,
1180
+ * coalesced or trigger-failed tag this write added. A failure to write the marker leaves it, and
1181
+ * the sweep publishes the run again.
1182
+ */
1183
+ private publishSettled;
1184
+ /** {@link publishSettled} once it holds the run's lifecycle lease and has read `run` under it. */
1185
+ private publishSettledLeased;
1186
+ /**
1187
+ * The subscribers `run` owes its event to that nothing can deliver it to: not in `here` (what
1188
+ * this instance delivers to), announced by no live worker, and owed since
1189
+ * {@link LIFECYCLE_ORPHAN_MS} or longer. Empty while the fleet's announcements cannot be read,
1190
+ * since then nobody is known to be gone.
1191
+ */
1192
+ private orphanedSubscribers;
1193
+ /** How long a settled run's marker is left to the live publish before the sweep takes it over. */
1194
+ private static readonly LIFECYCLE_SWEEP_GRACE_MS;
1195
+ /** How many marked runs one lifecycle sweep reads at most, looking for ones it can act on. */
1196
+ private static readonly LIFECYCLE_SWEEP_SCAN_CAP;
1197
+ /**
1198
+ * Publish the lifecycle event of settled runs still marked {@link LIFECYCLE_PENDING_TAG} to the
1199
+ * subscribers this instance can deliver to that they still owe it to: a run whose settling process
1200
+ * died, or whose marker write failed, before publishing, or whose subscriber is registered only on
1201
+ * other instances. Also writes this instance's row of the lifecycle subscription registry (see
1202
+ * {@link heartbeatLifecycleRegistry}), refreshes the snapshot of the subscriptions the fleet
1203
+ * announces (registry rows and worker descriptors), drops
1204
+ * owed subscribers nothing can deliver to (see {@link orphanedSubscribers}) and wakes the
1205
+ * `ctx.waitForRun` waiters whose run already settled (see {@link sweepRunWaits}).
1206
+ *
1207
+ * Called by the worker poll loop. Reads the marked runs page by page and skips the ones it cannot
1208
+ * act on (every owed subscriber is delivered elsewhere and none is orphaned yet), so runs owed only
1209
+ * to other instances never hide the ones it can publish. Stops once it has published `limit` runs
1210
+ * or read {@link LIFECYCLE_SWEEP_SCAN_CAP} marked runs. A run settled in the last
1211
+ * {@link LIFECYCLE_SWEEP_GRACE_MS} is left to the live publish; a run another publisher holds
1212
+ * (see {@link withLifecycleLease}) is left to the next tick. Then heals the runs settled in the
1213
+ * last {@link LIFECYCLE_HEAL_WINDOW_MS} (see {@link healLifecycleEvents}). Returns how many marked
1214
+ * runs it published plus how many waiters it woke; healed runs are not counted.
1215
+ */
1216
+ sweepLifecycleEvents(nowMs?: number, limit?: number): Promise<number>;
1217
+ /** How many settled runs of one source workflow a heal pass reads at most. */
1218
+ private static readonly LIFECYCLE_HEAL_SCAN_CAP;
1219
+ /**
1220
+ * The (run, subscriber) pairs this engine already healed, with when each entry lapses (epoch ms):
1221
+ * a subscriber whose `where` declines the event leaves no tag, so without it every sweep would
1222
+ * offer it the event again for the whole heal window.
1223
+ */
1224
+ private readonly lifecycleHealed;
1225
+ /**
1226
+ * Deliver lifecycle events the terminal write never marked: for every source workflow some
1227
+ * subscriber in the current snapshot of the fleet's subscriptions (or registered here) listens to,
1228
+ * read its runs settled in the last {@link LIFECYCLE_HEAL_WINDOW_MS} (at most
1229
+ * {@link LIFECYCLE_HEAL_SCAN_CAP}, one listing per workflow) and publish each one to the
1230
+ * subscribers of its ending its tags record as neither owed, started, coalesced nor failed (see
1231
+ * {@link publishSettled}'s `heal`). That covers a subscriber announced just after the run
1232
+ * settled, while the settling instance's snapshot was stale. Each pair is healed at most once per
1233
+ * engine. A run whose owed set is unresolved is left to {@link sweepLifecycleEvents}. Needs a
1234
+ * successful read of the fleet's subscriptions; returns how many runs it published.
1235
+ */
1236
+ private healLifecycleEvents;
1237
+ /**
1238
+ * Up to `limit` settled runs marked {@link LIFECYCLE_PENDING_TAG}, past the sweep grace, that this
1239
+ * instance can act on: a run owing its event to a subscriber it delivers to, a run marked without
1240
+ * owed tags, or a run owing it to a subscriber nothing delivers to past
1241
+ * {@link LIFECYCLE_ORPHAN_MS}. Reads at most {@link LIFECYCLE_SWEEP_SCAN_CAP} marked runs, in
1242
+ * pages, all read before any is acted on, since publishing a run takes it out of the listing.
1243
+ */
1244
+ private lifecycleActionableRuns;
1245
+ /**
1246
+ * Publish settled runs' lifecycle events again, whether or not they are still marked: one run
1247
+ * (`{ runId }`), or a workflow's settled runs (`{ workflow, kind, since, until, limit }`, read in
1248
+ * pages of the store's listing, filtered by when the run settled). `subscriber` restricts it to
1249
+ * one subscribing workflow, which must subscribe to the run's ending (registered here or announced
1250
+ * by a live worker for the run's namespace): otherwise the run is reported in `failed` with
1251
+ * `subscriber <name> does not subscribe to <event>` and nothing is marked. The subscribers
1252
+ * registered on this instance are started; any other (announced by a live worker) is marked owed
1253
+ * again, so the sweep of an instance that registers it starts it, and is not counted in
1254
+ * `started`. The same path as the live publish, so a subscriber whose run already exists is not
1255
+ * started again; a subscriber that debounces or batches is not routed again once the run carries
1256
+ * its coalesced tag, unless it also carries its trigger-failed tag, since an accumulator that
1257
+ * already fired would start the target again. A run another
1258
+ * publisher holds past the lease retries is reported in `failed` with subscriber `*`. A
1259
+ * subscriber registered after the runs settled (more than {@link LIFECYCLE_HEAL_WINDOW_MS} after,
1260
+ * past what the sweep heals) is backfilled this way. A workflow target's `namespace` narrows it to
1261
+ * that namespace; on an engine bound to another namespace it publishes nothing.
1262
+ */
1263
+ republishLifecycle(target: RepublishLifecycleTarget): Promise<RepublishLifecycleResult>;
1264
+ /**
1265
+ * Wake the `ctx.waitForRun` waiters on `runId`, which just settled, and on the earlier runs of its
1266
+ * continue-as-new chain, with the event of the run that ends the chain (none yet when `runId`
1267
+ * handed off). One waiter listing per settle. Best effort: a waiter missed here (this process dies
1268
+ * first) is woken by {@link sweepRunWaits}. Internal `__` runs have no waiters, so they are
1269
+ * skipped without a read.
1270
+ */
1271
+ private notifyRunWaiters;
1272
+ /** How many waited-on runs one {@link sweepRunWaits} reads at most. */
1273
+ private static readonly RUN_WAIT_SWEEP_SCAN_CAP;
1274
+ /** Where the next {@link sweepRunWaits} starts in the list of waited-on runs. */
1275
+ private runWaitSweepCursor;
1276
+ /**
1277
+ * Wake the `ctx.waitForRun` waiters (up to `limit`) whose run, or the run ending its
1278
+ * continue-as-new chain, has already settled: the recovery for a process that died between
1279
+ * settling a run and waking its waiters. Waiters on runs still in flight are skipped, reading at
1280
+ * most {@link RUN_WAIT_SWEEP_SCAN_CAP} waited-on runs per call; the next call starts where this one
1281
+ * stopped, so waiters on long-running runs never hide the ones that can be woken. Returns how many
1282
+ * it woke.
1283
+ */
1284
+ sweepRunWaits(limit?: number): Promise<number>;
927
1285
  /** Events waiting for the control-plane publish in flight, in emit order (see {@link broadcastEvents}). */
928
1286
  private readonly eventsToBroadcast;
929
1287
  private broadcastingEvents;
@@ -999,6 +1357,21 @@ export declare class WorkflowEngine {
999
1357
  * instead of the whole history, and carries an `etag` that `options.ifNoneMatch` can match.
1000
1358
  */
1001
1359
  getRunDetail(runId: string, options?: RunDetailOptions): Promise<RunDetail | null>;
1360
+ /**
1361
+ * What a run detail reads beyond the run row and its checkpoints: the subscribers its lifecycle
1362
+ * event goes to (of its namespace) and where it stands for each, its workflow's lifecycle links,
1363
+ * and the `ctx.waitForRun` waits on either side of it with the other run's status. A few reads per
1364
+ * detail: the run's own waiters while it is suspended, the waiters on it (one prefix read), and
1365
+ * one batched read of every linked run.
1366
+ */
1367
+ private lifecycleDetail;
1368
+ /**
1369
+ * The subscribers registered here that coalesce `run`'s lifecycle event (debounce/batch) and
1370
+ * accept it, each with the accumulator run the event went to: the subscription's `where`, `input`
1371
+ * and `key` applied to the event as `startSubscribers` does. Undefined when the key cannot be
1372
+ * computed. A subscriber whose `where` declines the event is left out.
1373
+ */
1374
+ private coalescedSubscribers;
1002
1375
  /** One page of a summarized fan's members (see `loadFanMembers`), versioned like {@link getRunDetail}. */
1003
1376
  getFanMembers(runId: string, group: string, query: FanPageQuery): Promise<FanPage | null>;
1004
1377
  private detailSource;
@@ -1149,6 +1522,7 @@ export declare class WorkflowEngine {
1149
1522
  /** Drop `gate` once `tail` settles, unless another execution queued behind it meanwhile. */
1150
1523
  private closeGateAfter;
1151
1524
  private resumeRunNow;
1525
+ private resumeLoadedRun;
1152
1526
  /**
1153
1527
  * Deferred re-drive after a suspended settle whose waited checkpoint(s) already settled — the
1154
1528
  * delivery's own resume no-oped against this turn's still-held lease (see the WorkflowSuspended
@@ -1413,6 +1787,12 @@ export declare class WorkflowEngine {
1413
1787
  * {@link resumeDueTimers}.
1414
1788
  */
1415
1789
  runPending(nowMs?: number): Promise<RunResult[]>;
1790
+ /**
1791
+ * Run `claimed` (pending runs this instance just leased, read as locked): every turn starts at
1792
+ * once, and each re-enters the lease taken for it without reading the run again. A run this
1793
+ * instance cannot resume is skipped and logged, as {@link resumeLeased} does; its lease lapses.
1794
+ */
1795
+ private runClaimed;
1416
1796
  /**
1417
1797
  * Resolve once `runId` reaches a settled state — terminal (completed/failed/cancelled/dead) or
1418
1798
  * suspended (handed off to a timer/signal/event). The async counterpart to dispatch: pair it with
@@ -1449,7 +1829,9 @@ export declare class WorkflowEngine {
1449
1829
  * match the payload satisfies (fan-out, vs `signal`'s point-to-point token).
1450
1830
  * 2. **Starts** a fresh run of every workflow registered with `onEvent: [name]`, passing the
1451
1831
  * payload as input. Idempotent by `evt:<id>:<workflow>` — pass `opts.id` to dedupe redeliveries
1452
- * of the same logical event (default: a fresh uuid, so each publish triggers once).
1832
+ * of the same logical event (default: a fresh uuid, so each publish triggers once). A workflow
1833
+ * a non-Node worker announces as subscribing to `name` (a Python `on_event=[...]`) is started
1834
+ * the same way, by name, when this engine can reach it (see {@link announcedRemoteSubscribers}).
1453
1835
  *
1454
1836
  * Reliable (buffered) delivery — mirrors `signalWithStart`'s reliability contract, so document it
1455
1837
  * exactly: a publish that resumes ≥1 live waiter, OR routes into an `eventBatch` accumulator /
@@ -1804,7 +2186,8 @@ export declare class WorkflowEngine {
1804
2186
  * settled it), so report what is persisted rather than writing over it.
1805
2187
  */
1806
2188
  /**
1807
- * A run's first execution: `pending` -> `running`. A singleton run not yet admitted is stamped
2189
+ * A run's first execution: `pending` -> `running`, resolving whether it moved it (a run cancelled
2190
+ * or deleted since it was read did not move). A singleton run not yet admitted is stamped
1808
2191
  * {@link SINGLETON_TRACKED_TAG} in the same write, BEFORE it competes for a slot, so a run this
1809
2192
  * replica is about to admit or deny is never mistaken for a holder admitted by a replica older than
1810
2193
  * 0.51 (see `legacySingletonHolders`).
@@ -2109,6 +2492,13 @@ export declare class WorkflowEngine {
2109
2492
  * capable+compatible worker has appeared, else re-parks with a fresh `wakeAt` (design §7.5). */
2110
2493
  private dueBlockedRuns;
2111
2494
  private callRemote;
2495
+ /**
2496
+ * {@link callRemote}'s first dispatch of a step, written with
2497
+ * {@link StateStore.insertCheckpointIfAbsent}: resolves whether it wrote the `pending` row and
2498
+ * dispatched. `false` when the position already has a row, or when no live worker can take the
2499
+ * step (the read-first path then decides, so a step that is in fact done is never blocked).
2500
+ */
2501
+ private dispatchIfAbsent;
2112
2502
  /**
2113
2503
  * `ctx.all` over a `@Step`: one durable step per input, dispatched together, joined once. The ctx
2114
2504
  * reserves the whole position block up front, so replay re-derives the same seqs.
@@ -2148,6 +2538,27 @@ export declare class WorkflowEngine {
2148
2538
  * {@link redispatchPending}. Carries the current trace/context so a re-dispatch is traceable.
2149
2539
  */
2150
2540
  private dispatchRemoteTask;
2541
+ /**
2542
+ * Whether this turn of `run` may run in a sticky continuation (see `sticky-continuation.ts`). Not
2543
+ * while the engine drains, nor with a compensating cancel requested for the run.
2544
+ */
2545
+ private stickyEligible;
2546
+ /**
2547
+ * The continuation this instance parked for `run`, if it is still the run's latest turn: the run
2548
+ * is `suspended` with the very stamp this instance's suspend wrote, on the same body and version.
2549
+ * Any other settle in between (another instance's turn, a retry, a cancel) changed the stamp, and
2550
+ * the run replays instead. Taking it removes it from the cache.
2551
+ */
2552
+ private takeContinuation;
2553
+ /** Keep `cont` (parked, detached) for the run's next turn here, stamped with its suspend write. */
2554
+ private keepContinuation;
2555
+ /**
2556
+ * The checkpoints of the steps `cont` is parked on, as a replay would see them: in one read when
2557
+ * the store batches, and without the input of a step that completed (what
2558
+ * {@link StateStore.getCheckpointsForSettle} leaves out). A step still open or failed is read
2559
+ * whole, since a re-dispatch or a retry sends its input again.
2560
+ */
2561
+ private readParked;
2151
2562
  /** A turn's read of its run's checkpoints: {@link StateStore.listCheckpointsForReplay}, or every
2152
2563
  * column through {@link StateStore.listCheckpoints} on a store without it. */
2153
2564
  private replayCheckpoints;
@@ -2258,6 +2669,15 @@ export declare class WorkflowEngine {
2258
2669
  * replays every write of the batch, so a second would only replay it again.
2259
2670
  */
2260
2671
  private completeRemoteResults;
2672
+ /**
2673
+ * {@link completeRemoteResults} on a store with {@link StateStore.settleResultsIfPending}: the
2674
+ * results settle their `pending` rows and read them back, with their run's status, in one call,
2675
+ * instead of reading the rows and the run and then writing. An entry that settled nothing (a
2676
+ * redelivery, a step gone, a late result for a finished run) takes the single-result path, which
2677
+ * reads first and re-drives, drops or salvages. A settled `completed` row is also handed to the
2678
+ * run's parked body, if this instance holds one, so its next turn does not read it again.
2679
+ */
2680
+ private settleResultsReturning;
2261
2681
  /**
2262
2682
  * Settle pending steps, each only while its row is still `pending`; per entry, whether it wrote
2263
2683
  * or the error that kept it from writing. Through {@link StateStore.settleCheckpointsIfPending}