eve 0.48.0 → 0.49.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 (189) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/channel/channel-address.js +1 -1
  3. package/dist/src/channel/types.d.ts +10 -0
  4. package/dist/src/cli/commands/registry-metadata.d.ts +30 -0
  5. package/dist/src/cli/commands/registry-metadata.js +1 -1
  6. package/dist/src/cli/commands/registry-package.d.ts +6 -0
  7. package/dist/src/cli/commands/registry-package.js +1 -1
  8. package/dist/src/cli/commands/registry-pnpm-build-policy-flow.d.ts +30 -0
  9. package/dist/src/cli/commands/registry-pnpm-build-policy-flow.js +1 -0
  10. package/dist/src/cli/commands/registry-pnpm-build-policy.d.ts +12 -0
  11. package/dist/src/cli/commands/registry-pnpm-build-policy.js +6 -0
  12. package/dist/src/cli/commands/registry-recovery.d.ts +4 -0
  13. package/dist/src/cli/commands/registry-recovery.js +1 -1
  14. package/dist/src/cli/commands/registry.d.ts +1 -1
  15. package/dist/src/cli/commands/registry.js +1 -1
  16. package/dist/src/cli/dev/tui/setup-commands.js +2 -2
  17. package/dist/src/compiled/.vendor-stamp.json +7 -7
  18. package/dist/src/compiled/@workflow/core/attribute-changes.d.ts +2 -4
  19. package/dist/src/compiled/@workflow/core/capabilities.d.ts +10 -9
  20. package/dist/src/compiled/@workflow/core/capture-stack.d.ts +1 -1
  21. package/dist/src/compiled/@workflow/core/class-serialization.d.ts +8 -8
  22. package/dist/src/compiled/@workflow/core/context-violation-error.d.ts +5 -5
  23. package/dist/src/compiled/@workflow/core/create-hook.d.ts +2 -2
  24. package/dist/src/compiled/@workflow/core/describe-error.d.ts +7 -7
  25. package/dist/src/compiled/@workflow/core/encryption.d.ts +5 -5
  26. package/dist/src/compiled/@workflow/core/events-consumer.d.ts +49 -14
  27. package/dist/src/compiled/@workflow/core/flushable-stream.d.ts +4 -4
  28. package/dist/src/compiled/@workflow/core/global.d.ts +4 -2
  29. package/dist/src/compiled/@workflow/core/index.d.ts +1 -1
  30. package/dist/src/compiled/@workflow/core/index.js +2 -2
  31. package/dist/src/compiled/@workflow/core/log-format.d.ts +2 -2
  32. package/dist/src/compiled/@workflow/core/private.d.ts +40 -78
  33. package/dist/src/compiled/@workflow/core/private.js +1 -1
  34. package/dist/src/compiled/@workflow/core/replay-payload-cache.d.ts +13 -17
  35. package/dist/src/compiled/@workflow/core/runtime/compute-instance.d.ts +4 -3
  36. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +27 -27
  37. package/dist/src/compiled/@workflow/core/runtime/count-step-started-events.d.ts +7 -7
  38. package/dist/src/compiled/@workflow/core/runtime/deployment-guard.d.ts +7 -7
  39. package/dist/src/compiled/@workflow/core/runtime/get-port-lazy.d.ts +2 -2
  40. package/dist/src/compiled/@workflow/core/runtime/get-world-lazy.d.ts +2 -2
  41. package/dist/src/compiled/@workflow/core/runtime/helpers.d.ts +30 -49
  42. package/dist/src/compiled/@workflow/core/runtime/quickjs-entrypoint.d.ts +21 -10
  43. package/dist/src/compiled/@workflow/core/runtime/quickjs-runtime.d.ts +31 -15
  44. package/dist/src/compiled/@workflow/core/runtime/quickjs-serde.d.ts +10 -10
  45. package/dist/src/compiled/@workflow/core/runtime/replay-budget.d.ts +4 -4
  46. package/dist/src/compiled/@workflow/core/runtime/replay-recovery-reporter.d.ts +1 -1
  47. package/dist/src/compiled/@workflow/core/runtime/resume-hook.d.ts +20 -12
  48. package/dist/src/compiled/@workflow/core/runtime/resume-latency.d.ts +37 -30
  49. package/dist/src/compiled/@workflow/core/runtime/run-id-time.d.ts +2 -2
  50. package/dist/src/compiled/@workflow/core/runtime/run.d.ts +22 -5
  51. package/dist/src/compiled/@workflow/core/runtime/runs.d.ts +4 -4
  52. package/dist/src/compiled/@workflow/core/runtime/start.d.ts +3 -3
  53. package/dist/src/compiled/@workflow/core/runtime/step-executor.d.ts +14 -14
  54. package/dist/src/compiled/@workflow/core/runtime/step-latency.d.ts +42 -25
  55. package/dist/src/compiled/@workflow/core/runtime/step-ownership.d.ts +6 -6
  56. package/dist/src/compiled/@workflow/core/runtime/step-single-flight.d.ts +1 -1
  57. package/dist/src/compiled/@workflow/core/runtime/suspension-handler.d.ts +38 -24
  58. package/dist/src/compiled/@workflow/core/runtime/unserializable-step.d.ts +34 -0
  59. package/dist/src/compiled/@workflow/core/runtime/vm-mode.d.ts +2 -2
  60. package/dist/src/compiled/@workflow/core/runtime/wait-continuation.d.ts +31 -9
  61. package/dist/src/compiled/@workflow/core/runtime/wait-until.d.ts +1 -1
  62. package/dist/src/compiled/@workflow/core/runtime/world-compatibility.d.ts +20 -9
  63. package/dist/src/compiled/@workflow/core/runtime/world-init.d.ts +5 -5
  64. package/dist/src/compiled/@workflow/core/runtime.js +3 -3
  65. package/dist/src/compiled/@workflow/core/sealed-box.d.ts +7 -7
  66. package/dist/src/compiled/@workflow/core/serialization/codec-devalue-vm.d.ts +1 -1
  67. package/dist/src/compiled/@workflow/core/serialization/codec-devalue.d.ts +1 -1
  68. package/dist/src/compiled/@workflow/core/serialization/codec.d.ts +4 -4
  69. package/dist/src/compiled/@workflow/core/serialization/compression.d.ts +12 -11
  70. package/dist/src/compiled/@workflow/core/serialization/encryption.d.ts +7 -7
  71. package/dist/src/compiled/@workflow/core/serialization/format.d.ts +6 -6
  72. package/dist/src/compiled/@workflow/core/serialization/hardened.d.ts +23 -22
  73. package/dist/src/compiled/@workflow/core/serialization/index.d.ts +1 -1
  74. package/dist/src/compiled/@workflow/core/serialization/reducers/class-vm.d.ts +1 -1
  75. package/dist/src/compiled/@workflow/core/serialization/reducers/step-function-vm.d.ts +1 -1
  76. package/dist/src/compiled/@workflow/core/serialization/types.d.ts +5 -5
  77. package/dist/src/compiled/@workflow/core/serialization/workflow-vm.d.ts +1 -1
  78. package/dist/src/compiled/@workflow/core/serialization-format.d.ts +11 -11
  79. package/dist/src/compiled/@workflow/core/serialization.d.ts +18 -19
  80. package/dist/src/compiled/@workflow/core/source-map.d.ts +1 -1
  81. package/dist/src/compiled/@workflow/core/step/context-storage.d.ts +5 -5
  82. package/dist/src/compiled/@workflow/core/step/writable-stream.d.ts +3 -3
  83. package/dist/src/compiled/@workflow/core/symbols.d.ts +2 -2
  84. package/dist/src/compiled/@workflow/core/telemetry/semantic-conventions.d.ts +71 -20
  85. package/dist/src/compiled/@workflow/core/telemetry.d.ts +15 -3
  86. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  87. package/dist/src/compiled/@workflow/core/vm/script-cache.d.ts +9 -11
  88. package/dist/src/compiled/@workflow/core/workflow/abort-controller.d.ts +1 -1
  89. package/dist/src/compiled/@workflow/core/workflow/set-attributes.d.ts +1 -1
  90. package/dist/src/compiled/@workflow/core/workflow/world-init-stub.d.ts +1 -1
  91. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  92. package/dist/src/compiled/@workflow/errors/index.d.ts +18 -18
  93. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  94. package/dist/src/compiled/@workflow/world/analytics.d.ts +3 -2
  95. package/dist/src/compiled/@workflow/world/attributes-validation.d.ts +28 -0
  96. package/dist/src/compiled/@workflow/world/attributes.d.ts +8 -107
  97. package/dist/src/compiled/@workflow/world/env-config.d.ts +5 -5
  98. package/dist/src/compiled/@workflow/world/event-metadata.d.ts +132 -0
  99. package/dist/src/compiled/@workflow/world/events.d.ts +42 -96
  100. package/dist/src/compiled/@workflow/world/hooks.d.ts +12 -11
  101. package/dist/src/compiled/@workflow/world/index.d.ts +3 -3
  102. package/dist/src/compiled/@workflow/world/interfaces.d.ts +94 -39
  103. package/dist/src/compiled/@workflow/world/queue.d.ts +29 -22
  104. package/dist/src/compiled/@workflow/world/runs.d.ts +20 -0
  105. package/dist/src/compiled/@workflow/world/slot-identity.d.ts +3 -3
  106. package/dist/src/compiled/@workflow/world/spec-version.d.ts +82 -15
  107. package/dist/src/compiled/@workflow/world/ulid.d.ts +2 -2
  108. package/dist/src/compiled/@workflow/world-local/index.js +5 -5
  109. package/dist/src/compiled/@workflow/world-vercel/index.js +12 -12
  110. package/dist/src/compiled/_chunks/workflow/{attribute-changes-DXbpwBdw.js → attribute-changes-BwAadFfB.js} +3 -3
  111. package/dist/src/compiled/_chunks/workflow/debug-log-CVPstO1D.js +1 -0
  112. package/dist/src/compiled/_chunks/workflow/dist-CgbVZs5l.js +40 -0
  113. package/dist/src/compiled/_chunks/workflow/dist-DjojkP5e.js +3 -0
  114. package/dist/src/compiled/_chunks/workflow/frames-CZ9eHShe.js +3 -0
  115. package/dist/src/compiled/_chunks/workflow/global-singleton-BNinwFrL.js +1 -0
  116. package/dist/src/compiled/_chunks/workflow/private-CZFMABEf.js +1 -0
  117. package/dist/src/compiled/_chunks/workflow/resume-hook-BToMtPbH.js +1 -0
  118. package/dist/src/compiled/_chunks/workflow/run-DzyAoGh4.js +1 -0
  119. package/dist/src/compiled/_chunks/workflow/sleep-3_A5uOp-.js +1 -0
  120. package/dist/src/compiled/_chunks/workflow/undici-DfxNX5og.js +95 -0
  121. package/dist/src/compiled/_chunks/workflow/wait-until-BtySPYD0.js +15 -0
  122. package/dist/src/compiled/_chunks/workflow/ws-transport-PnVVxT0R.js +1 -0
  123. package/dist/src/eve-channel/index.js +1 -1
  124. package/dist/src/execution/agent-handle-dispatch.js +1 -1
  125. package/dist/src/execution/continuation-conflict-step.d.ts +10 -0
  126. package/dist/src/execution/continuation-conflict-step.js +1 -0
  127. package/dist/src/execution/delegated-parent-notification.js +1 -1
  128. package/dist/src/execution/dispatch-runtime-actions-shared.d.ts +7 -29
  129. package/dist/src/execution/dispatch-runtime-actions-shared.js +1 -1
  130. package/dist/src/execution/dispatch-runtime-actions-start.d.ts +33 -0
  131. package/dist/src/execution/dispatch-runtime-actions-start.js +1 -0
  132. package/dist/src/execution/dispatch-runtime-actions-step.js +1 -1
  133. package/dist/src/execution/dispatch-turn-step.js +1 -1
  134. package/dist/src/execution/dynamic-connections.js +1 -1
  135. package/dist/src/execution/inline-turn.js +1 -1
  136. package/dist/src/execution/next-driver-action.d.ts +5 -4
  137. package/dist/src/execution/proxied-deliver-step.js +1 -1
  138. package/dist/src/execution/runtime-errors.d.ts +0 -12
  139. package/dist/src/execution/runtime-errors.js +1 -1
  140. package/dist/src/execution/session-timeout-steps.js +1 -1
  141. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  142. package/dist/src/execution/stable-workflow-names.d.ts +2 -2
  143. package/dist/src/execution/subagent-start-local.js +1 -1
  144. package/dist/src/execution/tasks/parent/dispatch-task-step.js +1 -1
  145. package/dist/src/execution/tasks/parent/run-parent.js +1 -1
  146. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  147. package/dist/src/execution/tool-run/start.js +1 -1
  148. package/dist/src/execution/tools/subagent/local.js +1 -1
  149. package/dist/src/execution/turn-workflow.js +1 -1
  150. package/dist/src/execution/workflow-entry.d.ts +5 -1
  151. package/dist/src/execution/workflow-entry.js +1 -1
  152. package/dist/src/execution/workflow-runtime.d.ts +12 -12
  153. package/dist/src/execution/workflow-runtime.js +1 -1
  154. package/dist/src/execution/workflow-steps.js +1 -1
  155. package/dist/src/harness/tool-loop.js +1 -1
  156. package/dist/src/internal/application/package.js +1 -1
  157. package/dist/src/internal/workflow/development-world-client.js +1 -1
  158. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  159. package/dist/src/public/channels/slack/slackChannel.d.ts +7 -1
  160. package/dist/src/public/channels/slack/slackChannel.js +1 -1
  161. package/dist/src/setup/flows/registry.js +1 -1
  162. package/dist/src/setup/scaffold/connections/catalog.js +1 -1
  163. package/dist/src/setup/scaffold/create/project.js +1 -1
  164. package/docs/channels/custom.mdx +4 -0
  165. package/docs/channels/eve.mdx +12 -8
  166. package/docs/channels/mcp.mdx +4 -1
  167. package/docs/guides/instrumentation.md +1 -1
  168. package/docs/install-integrations.mdx +2 -0
  169. package/docs/instructions.mdx +1 -1
  170. package/docs/memory/custom-provider.md +214 -0
  171. package/docs/memory/file.md +129 -0
  172. package/docs/memory/meta.json +5 -0
  173. package/docs/memory/overview.mdx +282 -0
  174. package/docs/patterns/multi-tenant-memory.md +12 -20
  175. package/package.json +8 -8
  176. package/dist/src/compiled/_chunks/workflow/dist-CqvWKNQv.js +0 -3
  177. package/dist/src/compiled/_chunks/workflow/dist-Dk0aQqFO.js +0 -40
  178. package/dist/src/compiled/_chunks/workflow/dist-DpYyXbJv.js +0 -1
  179. package/dist/src/compiled/_chunks/workflow/env-config-L94XftKz.js +0 -1
  180. package/dist/src/compiled/_chunks/workflow/frames-Dl2Hcma4.js +0 -3
  181. package/dist/src/compiled/_chunks/workflow/private-CuQ4HTFC.js +0 -1
  182. package/dist/src/compiled/_chunks/workflow/resume-hook-E-Nkx8q_.js +0 -1
  183. package/dist/src/compiled/_chunks/workflow/run-cIeex2KF.js +0 -1
  184. package/dist/src/compiled/_chunks/workflow/sleep-CxFsdxB_.js +0 -1
  185. package/dist/src/compiled/_chunks/workflow/spec-version-DBUVfF_R.js +0 -1
  186. package/dist/src/compiled/_chunks/workflow/undici-DznrXKcM.js +0 -95
  187. package/dist/src/compiled/_chunks/workflow/wait-until-BZHmJCAW.js +0 -15
  188. package/dist/src/compiled/_chunks/workflow/ws-transport-Ct6yyRWZ.js +0 -1
  189. package/docs/memory.md +0 -408
@@ -41,7 +41,7 @@ export interface StepExecutorParams {
41
41
  * Pre-claimed inline start: the suspension handler committed (or lost) this
42
42
  * step's `step_created` + `step_started` pair inside its batched fan-out
43
43
  * write, so the start this executor would otherwise send has already been
44
- * decided. `owned: false` returns `{ type: 'skipped' }` before any write —
44
+ * decided. `owned: false` returns `{ type: 'skipped' }` before any write,
45
45
  * the same outcome as losing the lazy claim's atomic create. `owned: true`
46
46
  * skips both start paths (no start write at all) and runs the body against
47
47
  * the claimed step. Mutually exclusive with `lazyStepInput`: the input
@@ -51,11 +51,11 @@ export interface StepExecutorParams {
51
51
  /**
52
52
  * Inline step ownership: the queue message ID of the invocation this
53
53
  * executeStep call runs in (from the queue handler's meta). When set, the
54
- * `step_started` this call sends is stamped with it — on the lazy paths
54
+ * `step_started` this call sends is stamped with it: on the lazy paths
55
55
  * (where `lazyStepInput` is present) and on the owned-recovery
56
56
  * payload-less start (where it is not; the re-stamp keeps a recovered
57
57
  * step readable as owned by this message, since ownership derives from
58
- * the LATEST start — so a recovery start is never bare). Wake
58
+ * the LATEST start, so a recovery start is never bare). Wake
59
59
  * replays that observe an actively-owned step suppress the immediate
60
60
  * requeue and enqueue a delayed backstop instead. Omitted on the
61
61
  * background-step path, whose bare start intentionally clears ownership
@@ -82,7 +82,7 @@ export interface StepExecutorParams {
82
82
  * It matters most on the lazy inline path, where the `step_started` claim is
83
83
  * the step's FIRST durable write (its `step_created` is deferred): without a
84
84
  * seed the claim would name no position at all, and a replay working from a
85
- * stale view could claim — and then commit — a step scheduled without
85
+ * stale view could claim (and then commit) a step scheduled without
86
86
  * observing an event it never loaded.
87
87
  *
88
88
  * A World that fences rejects a stale claim with `PreconditionFailedError`
@@ -100,7 +100,7 @@ export interface StepExecutorParams {
100
100
  * stale-sensitive (an open hook means an out-of-band event can make the
101
101
  * scheduling view stale): the guard's 412 fence can reject a stale claim's
102
102
  * durable writes, but it cannot un-run a body that optimistic start began
103
- * before the claim settled — awaiting the claim extends the fence to user
103
+ * before the claim settled; awaiting the claim extends the fence to user
104
104
  * code. Wins over `forceOptimisticStart` and the env flag.
105
105
  */
106
106
  suppressOptimisticStart?: boolean;
@@ -116,8 +116,8 @@ export interface StepExecutorParams {
116
116
  * Turbo mode only: a promise that resolves once the backgrounded
117
117
  * `run_started` has landed. When set, the lazy/optimistic `step_started` is
118
118
  * chained on it so the step is never created before its run exists. The body
119
- * still runs immediately against locally-synthesized state — only the network
120
- * write waits — so the `run_started` round-trip overlaps the body. `undefined`
119
+ * still runs immediately against locally-synthesized state (only the network
120
+ * write waits), so the `run_started` round-trip overlaps the body. `undefined`
121
121
  * outside turbo, where `run_started` was already awaited up front.
122
122
  */
123
123
  runReadyBarrier?: Promise<unknown>;
@@ -126,7 +126,7 @@ export interface StepExecutorParams {
126
126
  * by the orchestrator. When set, this executor computes the final values
127
127
  * against the wall clock taken immediately before user code runs and
128
128
  * attaches them to the step's terminal event. Set only for the first step of
129
- * an inline batch, and only on first-attempt executions that qualify — see
129
+ * an inline batch, and only on first-attempt executions that qualify; see
130
130
  * runtime/step-latency.ts.
131
131
  */
132
132
  latencyTracking?: StepLatencyTracking;
@@ -136,12 +136,12 @@ export interface StepExecutorParams {
136
136
  * executor closes the measurement against the `step_started` claim and the
137
137
  * wall clock taken immediately before user code, and attaches the total plus
138
138
  * its phase breakdown to this step's `step.execute` span. Set by the runtime
139
- * for exactly one step per resumption — see runtime/resume-latency.ts.
139
+ * for exactly one step per resumption; see runtime/resume-latency.ts.
140
140
  */
141
141
  resumeTracking?: ResumeTtrTracking;
142
142
  /**
143
143
  * Authoritative attempt number for this execution, used to bound retries
144
- * against `maxRetries` BEFORE the body runs. In order to also catch
144
+ * against `maxRetries` BEFORE the body runs. To also catch
145
145
  * step timeouts (which we can't have catch handlers for), we determine
146
146
  * the attempt count based as follows:
147
147
  * - Inline (combined handler): the number of `step_started` events already
@@ -171,13 +171,13 @@ export type PreclaimedInlineStart = {
171
171
  */
172
172
  step: StartedStep;
173
173
  /**
174
- * `Date.now()` taken right before the batch POST that carried the pair
175
- * — the claim's "start POST sent" instant, anchoring RSFS exactly like
174
+ * `Date.now()` taken right before the batch POST that carried the pair:
175
+ * the claim's "start POST sent" instant, anchoring RSFS exactly like
176
176
  * the lazy claim's own POST would.
177
177
  */
178
178
  batchPostSentAtMs?: number;
179
179
  /**
180
- * `Date.now()` taken right after that batch POST returned — the
180
+ * `Date.now()` taken right after that batch POST returned: the
181
181
  * claim's completion instant (T6 of the hook-resume TTR window).
182
182
  */
183
183
  claimCompletedAtMs?: number;
@@ -233,7 +233,7 @@ export type StepExecutionResult = {
233
233
  * Executes a single step: creates step_started event, hydrates input,
234
234
  * runs the step function, creates step_completed/step_failed/step_retrying events.
235
235
  *
236
- * Does NOT queue workflow continuation messages — the caller decides what to do next.
236
+ * Does NOT queue workflow continuation messages; the caller decides what to do next.
237
237
  * Used by the combined workflow handler for step execution.
238
238
  */
239
239
  export declare function executeStep(params: StepExecutorParams): Promise<StepExecutionResult>;
@@ -7,11 +7,11 @@ import type { Event } from '#compiled/@workflow/world/index.js';
7
7
  * beginning to execute. STSO (step-to-step overhead) measures the previous
8
8
  * step's terminal event → the next step's body beginning to execute. RSFS
9
9
  * (run-started-to-first-step) measures the `run_started` response landing →
10
- * the first step's start POST being issued — a sub-window of TTFS that
10
+ * the first step's start POST being issued, a sub-window of TTFS that
11
11
  * isolates replay overhead from the run-creation queue hop; `finalSchedulingReplay`
12
12
  * is the synchronous workflow-function-execution duration of only the FINAL
13
13
  * replay pass within that window (the pass that reached and scheduled the
14
- * first step) — it is NOT accumulated across earlier pre-first-step passes
14
+ * first step); it is NOT accumulated across earlier pre-first-step passes
15
15
  * (see {@link StepLatencyTracking.replayMs}), so it must not be read as "the
16
16
  * replay portion of RSFS". All are attached to the step's terminal
17
17
  * event so a backend can emit latency metrics from the event write alone,
@@ -42,7 +42,7 @@ export interface StepLatencyTracking {
42
42
  * workflow-body `setAttributes` before the first step
43
43
  * resolves through an extra replay (see the `hasAttributeEvents` branch in
44
44
  * runtime.ts), so everything from this write until the step body runs is
45
- * the duration of the setAttributes call — which is subtracted by ending
45
+ * the duration of the setAttributes call, which is subtracted by ending
46
46
  * the measurement at the point where the step would otherwise have been
47
47
  * scheduled.
48
48
  */
@@ -59,7 +59,7 @@ export interface StepLatencyTracking {
59
59
  eventCount?: number;
60
60
  /**
61
61
  * Epoch ms the `run_started` response was received/parsed by the SDK.
62
- * Present only when the step qualifies for RSFS — the same eligibility as
62
+ * Present only when the step qualifies for RSFS: the same eligibility as
63
63
  * TTFS (see {@link computeStepLatencyTracking}), plus a recoverable
64
64
  * anchor. In turbo mode, `run_started` is backgrounded rather than
65
65
  * awaited, so this is stamped at the point the runtime synthesizes the
@@ -74,21 +74,30 @@ export interface StepLatencyTracking {
74
74
  * Wall-clock ms this invocation's synchronous workflow-function replay
75
75
  * took: from calling `runWorkflow` to it throwing the suspension that
76
76
  * scheduled this batch. Excludes awaited network I/O (the suspension's
77
- * event commits, the step's own start POST). Present only alongside
78
- * `rsfsAnchorMs`.
77
+ * event commits, the step's own start POST). Reported for every batch that
78
+ * qualifies for TTFS or STSO, not just the first step.
79
79
  *
80
- * This is the FINAL replay pass only — the invocation that reached and
81
- * scheduled the first step. Valid RSFS paths can replay more than once
82
- * before the first step (e.g. a workflow-body `setAttributes()` detour
83
- * replays twice), and a redelivery omits earlier invocations' replay work
84
- * entirely; this value is not accumulated across those earlier passes.
85
- * Do not read it as "the replay portion of RSFS" — RSFS
86
- * ({@link rsfsAnchorMs}) covers the whole run_started-to-first-step
87
- * window, this covers only the last pass.
80
+ * This is the FINAL pass only: the one that reached and scheduled this
81
+ * batch. Valid RSFS paths can replay more than once before the first step
82
+ * (e.g. a workflow-body `setAttributes()` detour replays twice), and a
83
+ * redelivery omits earlier invocations' replay work entirely; this value is
84
+ * not accumulated across those earlier passes. Do not read it as "the
85
+ * replay portion of RSFS": RSFS ({@link rsfsAnchorMs}) covers the whole
86
+ * run_started-to-first-step window, this covers only the last pass.
87
+ *
88
+ * On a retained resume this measures the resume, not a replay, which is
89
+ * why the distribution is bimodal and why {@link retained} is reported
90
+ * alongside it as the dimension that separates the two modes.
88
91
  */
89
92
  replayMs?: number;
90
93
  /** Whether turbo mode is active for this invocation. */
91
94
  turbo: boolean;
95
+ /**
96
+ * Whether a retained VM session served this batch, as opposed to a full
97
+ * replay from the event log. Reported as a `retained` entry in
98
+ * {@link StepLatencyEventData.optimizations}, alongside `turbo`.
99
+ */
100
+ retained: boolean;
92
101
  }
93
102
  /** Latency telemetry attached to a step's terminal event's `eventData`. */
94
103
  export interface StepLatencyEventData {
@@ -99,12 +108,18 @@ export interface StepLatencyEventData {
99
108
  /** Client-measured run_started → first step's start POST, ms. */
100
109
  rsfs?: number;
101
110
  /**
102
- * Client-measured wall-clock ms of the FINAL replay pass that scheduled
103
- * the first step (see {@link StepLatencyTracking.replayMs}) — not
104
- * accumulated across earlier pre-first-step passes, so it must not be
105
- * read as "the replay portion of `rsfs`".
111
+ * Client-measured wall-clock ms of the FINAL pass that scheduled this
112
+ * batch (see {@link StepLatencyTracking.replayMs}); not accumulated
113
+ * across earlier passes, so it must not be read as "the replay portion of
114
+ * `rsfs`". Present whenever `ttfs` or `stso` is.
106
115
  */
107
116
  finalSchedulingReplay?: number;
117
+ /**
118
+ * Active runtime modes for this measurement: `turbo`, `lazyStepStart`,
119
+ * `optimisticStart`, and `retained` (a retained VM served the batch; its
120
+ * absence means a full replay). The server turns each known entry into a
121
+ * bounded boolean tag on every latency metric.
122
+ */
108
123
  optimizations?: string[];
109
124
  }
110
125
  /**
@@ -131,13 +146,13 @@ export declare function computeStepLatencyTracking(params: {
131
146
  runCreatedAtMs: number | undefined;
132
147
  /**
133
148
  * Epoch ms the `run_started` response was received/parsed by the SDK (or,
134
- * under turbo, the instant the run was synthesized locally — see
149
+ * under turbo, the instant the run was synthesized locally; see
135
150
  * {@link StepLatencyTracking.rsfsAnchorMs}). Absent disqualifies RSFS.
136
151
  */
137
152
  runStartedReceivedAtMs: number | undefined;
138
153
  /**
139
154
  * Wall-clock ms this suspension's `runWorkflow` call spent executing
140
- * synchronously before throwing — the FINAL replay pass only, not
155
+ * synchronously before throwing: the FINAL replay pass only, not
141
156
  * accumulated across earlier passes. See
142
157
  * {@link StepLatencyTracking.replayMs}.
143
158
  */
@@ -148,7 +163,7 @@ export declare function computeStepLatencyTracking(params: {
148
163
  * The accumulator's value as of the suspension that wrote the run's first
149
164
  * attr_set (its hook phase runs before its attr writes). When the
150
165
  * measurement ends at the attr write, only hook time from before that
151
- * point may be subtracted — later hook writes fall outside the measured
166
+ * point may be subtracted; later hook writes fall outside the measured
152
167
  * window. Undefined when no attr suspension happened in this invocation
153
168
  * (e.g. the attr_set was loaded from a redelivery's snapshot, where no
154
169
  * same-invocation hook time precedes it).
@@ -157,25 +172,27 @@ export declare function computeStepLatencyTracking(params: {
157
172
  /**
158
173
  * Whether the suspension that scheduled this batch also created waits.
159
174
  * Those `wait_created` writes are not in `events` yet (they were committed
160
- * by this very suspension pass), so they must be reported separately. A
175
+ * by this suspension pass), so they must be reported separately. A
161
176
  * wait disqualifies both measurements.
162
177
  */
163
178
  suspensionHasWaits: boolean;
164
179
  /**
165
180
  * Whether the suspension that scheduled this batch also created hooks
166
- * (also not in `events` yet). Hooks keep TTFS eligible — their measured
167
- * write time is subtracted via `preStepBlockingMs` — but disqualify STSO,
181
+ * (also not in `events` yet). Hooks keep TTFS eligible (their measured
182
+ * write time is subtracted via `preStepBlockingMs`) but disqualify STSO,
168
183
  * which is only meaningful for a pure back-to-back step gap.
169
184
  */
170
185
  suspensionCreatedHooks: boolean;
171
186
  /** Whether turbo mode is active for this invocation. */
172
187
  turbo: boolean;
188
+ /** See {@link StepLatencyTracking.retained}. */
189
+ retained: boolean;
173
190
  }): StepLatencyTracking | undefined;
174
191
  /**
175
192
  * Compute the latency telemetry to attach to the step's terminal event.
176
193
  * Called by `executeStep` with the wall-clock timestamp taken immediately
177
194
  * before user step code runs. Returns undefined when there is nothing to
178
- * report (no tracking, or a retry attempt — retries measure neither TTFS nor
195
+ * report (no tracking, or a retry attempt; retries measure neither TTFS nor
179
196
  * STSO).
180
197
  */
181
198
  export declare function computeStepLatencyEventData(params: {
@@ -18,13 +18,13 @@ export declare function isStepOwnershipActive(step: StepInvocationQueueItem): bo
18
18
  /**
19
19
  * Seconds left on an owned step's liveness lease, anchored at its latest
20
20
  * `step_started`. 0 means the lease has expired (or the start timestamp is
21
- * missing — the degraded mode for worlds whose events lack usable
21
+ * missing, the degraded mode for worlds whose events lack usable
22
22
  * timestamps), in which case dispatch falls back to the immediate enqueue.
23
23
  *
24
24
  * The result is clamped to the configured lease: `lastStartedAt` is the
25
25
  * server-stamped event `createdAt` while `nowMs` is the local clock, so a
26
26
  * client running behind the server would otherwise compute a remainder
27
- * LONGER than the lease itself — and with the lease tuned to the 900s cap,
27
+ * LONGER than the lease itself, and with the lease tuned to the 900s cap,
28
28
  * a `delaySeconds` above the queue's per-message maximum, which SQS-backed
29
29
  * worlds reject outright (the wake replay's enqueue would throw and ride
30
30
  * the redelivery loop). The clamp makes skew strictly harmless; remaining
@@ -34,14 +34,14 @@ export declare function stepLeaseRemainingSeconds(step: StepInvocationQueueItem,
34
34
  /**
35
35
  * Idempotency key for the delayed backstop wake of an inline-owned step.
36
36
  *
37
- * The key is scoped to the current OWNERSHIP EPOCH — the timestamp of the
38
- * latest `step_started` — not just the correlation ID. Within one epoch,
37
+ * The key is scoped to the current OWNERSHIP EPOCH (the timestamp of the
38
+ * latest `step_started`), not just the correlation ID. Within one epoch,
39
39
  * every wake replay derives the same key, so fan-out stays capped at one
40
40
  * pending backstop per step. But when owner recovery re-stamps the step
41
41
  * (queue redelivery of the owning message → new `step_started` → new
42
42
  * `lastStartedAt`), the key CHANGES. This is load-bearing for liveness:
43
43
  * queues dedupe an idempotency key for the lifetime of the original
44
- * message — including while a delivery of it is in flight — so a backstop
44
+ * message (including while a delivery of it is in flight), so a backstop
45
45
  * that fires during a refreshed lease and tries to re-arm under a fixed key
46
46
  * would dedupe against ITSELF and be dropped, leaving no escape hatch if
47
47
  * the recovered owner later dies without further redeliveries. The epoch
@@ -56,7 +56,7 @@ export declare function stepLeaseRemainingSeconds(step: StepInvocationQueueItem,
56
56
  * The key must also never be the step message's own `idempotencyKey`
57
57
  * (the bare correlation ID): the owner's retry handoff enqueues the step
58
58
  * under that key with a short backoff, and a pending backstop sharing it
59
- * would absorb the retry — turning a 1s backoff into a full-lease stall.
59
+ * would absorb the retry, turning a 1s backoff into a full-lease stall.
60
60
  */
61
61
  export declare function backstopIdempotencyKey(step: StepInvocationQueueItem): string;
62
62
  /**
@@ -4,7 +4,7 @@ import type { StepExecutionResult } from './step-executor.js';
4
4
  * already in flight in this process. The winner's result is returned to the
5
5
  * winner; a loser awaits the winner's settlement (success OR failure) and
6
6
  * then returns `{ type: 'skipped' }` so its caller acks without running the
7
- * body. A winner failure is not propagated to the loser — the winner's own
7
+ * body. A winner failure is not propagated to the loser: the winner's own
8
8
  * queue message redelivers and drives the retry, so exactly one message
9
9
  * keeps owning the outcome.
10
10
  */
@@ -1,6 +1,7 @@
1
1
  import type { Span } from '#compiled/@opentelemetry/api/index.js';
2
2
  import { type SerializedData, type TraceCarrier, type ValidQueueName, type WorkflowRun, type World } from '#compiled/@workflow/world/index.js';
3
3
  import type { StepInvocationQueueItem, WorkflowSuspension } from '../global.js';
4
+ import { type GuestCodeExecution } from '../serialization/hardened.js';
4
5
  import { type LoadedEventLog } from './helpers.js';
5
6
  import { ReplayRecoveryReporter } from './replay-recovery-reporter.js';
6
7
  import type { PreclaimedInlineStart } from './step-executor.js';
@@ -13,7 +14,7 @@ export interface SuspensionHandlerParams {
13
14
  /**
14
15
  * The runtime's loaded event log. Every event creation this suspension makes
15
16
  * names the position it was derived from, so a backend that has recorded
16
- * events the replay did not see can report them back on the write — or, if
17
+ * events the replay did not see can report them back on the write, or, if
17
18
  * it would rather refuse than report, reject it with a 412. A rejection is
18
19
  * not retried here: the event's correlation id was minted by *this* replay's
19
20
  * seeded sequence, so re-committing it against a corrected log would persist
@@ -35,14 +36,14 @@ export interface SuspensionHandlerParams {
35
36
  replayRecoveryReporter?: ReplayRecoveryReporter;
36
37
  /**
37
38
  * Resilient step dispatch: when provided (and the per-step eligibility gates
38
- * pass — see the step ops below), each newly created non-inline step's
39
+ * pass, see the step ops below), each newly created non-inline step's
39
40
  * `step_created` write is parallelized with its step-execution queue
40
41
  * publish, and the queue message carries the serialized step input
41
42
  * (`stepInput`) so the consumer can idempotently re-ensure the event if the
42
43
  * direct write failed transiently. Steps queued this way are reported in
43
44
  * {@link SuspensionHandlerResult.queuedStepCorrelationIds} so the caller
44
45
  * skips them in its own dispatch pass. Omitted by callers that must not
45
- * queue (terminal drain, tests) — creates then behave exactly as before.
46
+ * queue (terminal drain, tests); creates then behave exactly as before.
46
47
  */
47
48
  stepDispatch?: {
48
49
  /** The unified workflow queue this run's step messages are published to. */
@@ -57,9 +58,9 @@ export interface SuspensionHandlerParams {
57
58
  * Inline step ownership: the queue message ID of the invocation this
58
59
  * suspension runs in (the queue handler's meta). When present AND the
59
60
  * batched fan-out engages, the lazy-inline steps' deferred writes are
60
- * folded into the batch as `step_created` + `step_started` pairs — the
61
+ * folded into the batch as `step_created` + `step_started` pairs (the
61
62
  * started row stamped with this ID, exactly like the lazy claim it
62
- * replaces — pre-claiming the steps the caller is about to run inline. See
63
+ * replaces), pre-claiming the steps the caller is about to run inline. See
63
64
  * {@link SuspensionHandlerResult.inlineClaims}. Callers that never
64
65
  * inline-execute (terminal drain) omit it, keeping their lazy steps on the
65
66
  * plain deferred path.
@@ -69,8 +70,8 @@ export interface SuspensionHandlerParams {
69
70
  * Lets the batched fan-out return before every chunk has committed: only
70
71
  * the chunk carrying the pre-claimed inline pairs gates the handler's
71
72
  * return (its claims are what the caller starts bodies from), while the
72
- * trailing chunks' commits — and every chunk's in-flush step-message
73
- * publishes — ride {@link SuspensionHandlerResult.deferredBatchWork}. A
73
+ * trailing chunks' commits (and every chunk's in-flush step-message
74
+ * publishes) ride {@link SuspensionHandlerResult.deferredBatchWork}. A
74
75
  * caller that opts in MUST await that promise before acking its delivery:
75
76
  * the durability contract ("every create durable before ack") moves from
76
77
  * the handler's return to that join, and nothing else re-drives a lost
@@ -90,16 +91,28 @@ export interface SuspensionHandlerResult {
90
91
  * Correlation IDs for which this suspension call actually wrote the
91
92
  * step_created event (as opposed to catching EntityConflictError because
92
93
  * a concurrent handler wrote it first). Only the handler that wrote the
93
- * step_created event should queue / inline-execute the step — this
94
+ * step_created event should queue / inline-execute the step; this
94
95
  * guarantees a single owner per step, even when multiple handlers race
95
96
  * into the same batch boundary.
96
97
  */
97
98
  createdStepCorrelationIds: Set<string>;
99
+ /**
100
+ * Correlation IDs of steps whose arguments failed to serialize. Each was
101
+ * finalized here as `step_created` (with a placeholder input; the real
102
+ * input is precisely what refused to serialize) followed by `step_failed`
103
+ * carrying the SerializationError, so the next replay rejects the step's
104
+ * promise and a try/catch around the step call observes the error,
105
+ * exactly like a step-body failure. No step-execution message is
106
+ * dispatched for these, so the caller MUST force an in-process replay:
107
+ * when the failed step was the only pending work, nothing else will ever
108
+ * re-invoke the run to observe the terminal event.
109
+ */
110
+ failedStepCorrelationIds: Set<string>;
98
111
  /**
99
112
  * Correlation IDs of steps this suspension call already published
100
113
  * step-execution queue messages for, via resilient step dispatch (the
101
114
  * `step_created` write parallelized with a `stepInput`-carrying queue
102
- * publish). The caller MUST NOT dispatch these again — the message is
115
+ * publish). The caller MUST NOT dispatch these again: the message is
103
116
  * already out (a duplicate would be deduped by its idempotency key, but
104
117
  * costs a wasted round-trip). Empty when {@link SuspensionHandlerParams.stepDispatch}
105
118
  * was not provided or no step was eligible.
@@ -134,12 +147,12 @@ export interface SuspensionHandlerResult {
134
147
  * Pre-claimed inline starts, by correlation id: the per-step verdicts of
135
148
  * the `step_created` + `step_started` pairs the batched fan-out committed
136
149
  * for the lazy-inline steps. A step with an entry here is passed to
137
- * `executeStep` as `preclaimedStart` INSTEAD of `lazyStepInput` — its
150
+ * `executeStep` as `preclaimedStart` INSTEAD of `lazyStepInput`: its
138
151
  * input already rode the pair, and the claim is settled: `owned: true`
139
152
  * carries the started attempt-1 entity (input re-attached) so the body
140
153
  * runs straight off the batch commit with no start write of its own;
141
154
  * `owned: false` lost the pair's atomic create-claim to a concurrent
142
- * writer, and executeStep returns `skipped` without running the body —
155
+ * writer, and executeStep returns `skipped` without running the body,
143
156
  * the same outcome as losing the lazy claim. Empty whenever the fold did
144
157
  * not engage (batching off, no `ownerMessageId`, or the lone-inline case,
145
158
  * which keeps the optimistic lazy path and its claim/body overlap).
@@ -147,20 +160,20 @@ export interface SuspensionHandlerResult {
147
160
  * Crash window: the pair commits before the caller runs the body, so a
148
161
  * crash between them leaves a started step stamped with this message's
149
162
  * ID. Redelivery of the same message re-executes it via the owned-recovery
150
- * path — the exact machinery the lazy claim's crash window already uses.
163
+ * path, the exact machinery the lazy claim's crash window already uses.
151
164
  */
152
165
  inlineClaims: Map<string, PreclaimedInlineStart>;
153
166
  /**
154
167
  * The highest slot the batched fan-out committed, when it ran. The batch's
155
168
  * own events are not in the caller's loaded log (the next reload picks
156
169
  * them up), so the caller folds this ceiling into the slot snapshot it
157
- * hands the inline executions — otherwise every inline terminal write
170
+ * hands the inline executions; otherwise every inline terminal write
158
171
  * would name a pre-batch position and be answered with a skipped-slot
159
172
  * report echoing the events this suspension just wrote. Under
160
173
  * {@link SuspensionHandlerParams.allowDeferredBatchWork} this covers the
161
174
  * chunks that had committed by the handler's return (always the pair
162
175
  * chunk); a trailing chunk that commits later is echoed back on the
163
- * terminal writes like any foreign event — reports the executor reads for
176
+ * terminal writes like any foreign event: reports the executor reads for
164
177
  * position and discards.
165
178
  *
166
179
  * So the echo is only fully suppressed for a SINGLE-chunk fold. On a
@@ -168,7 +181,7 @@ export interface SuspensionHandlerResult {
168
181
  * chunks are still in flight, and an inline terminal write issued in that
169
182
  * window still names a position below them and still draws a report for
170
183
  * their events. Bounded (trailing chunks only, large fan-outs only) and
171
- * self-correcting on the next reload — recorded so a report seen there
184
+ * self-correcting on the next reload, and recorded so a report seen there
172
185
  * reads as expected rather than as a bug.
173
186
  */
174
187
  batchCommittedSlotCeiling?: number;
@@ -178,7 +191,7 @@ export interface SuspensionHandlerResult {
178
191
  * trailing work exists: the commits of every chunk except the pair chunk,
179
192
  * plus every chunk's step-message publishes (each chained on ITS OWN
180
193
  * chunk's commit, so publish-after-create holds per step). The caller
181
- * MUST await it before acking — a rejection here is a failed suspension
194
+ * MUST await it before acking: a rejection here is a failed suspension
182
195
  * write and fails the delivery exactly as it would have at the handler's
183
196
  * return. Steps whose messages this work publishes are already in
184
197
  * {@link queuedStepCorrelationIds} at return time.
@@ -216,17 +229,18 @@ export interface SuspensionHandlerResult {
216
229
  * durably creating the user's hooks doesn't count as runtime overhead.
217
230
  */
218
231
  hookCreationMs: number;
219
- /**
220
- * Whether serializing this suspension's new step inputs was passive (did
221
- * not execute workflow-owned code such as getters, proxy traps, or custom
222
- * serializers). `false` means the retained VM may have diverged from what
223
- * a cold replay would compute, so the caller must demote to replay.
224
- */
225
- retainedStepInputsSafe: boolean;
232
+ /** Exact number of workflow-code executions observed during serialization. */
233
+ serializationBlockerCount: number;
234
+ /** Bounded sample used only for retention diagnostics. */
235
+ serializationBlockers: SuspensionSerializationBlocker[];
236
+ }
237
+ export interface SuspensionSerializationBlocker extends GuestCodeExecution {
238
+ source: 'step_input' | 'hook_metadata' | 'hook_abort';
239
+ correlationId: string;
226
240
  }
227
241
  /**
228
242
  * Handles a workflow suspension by processing all pending operations (hooks, steps, waits).
229
- * Creates events for all operations but does NOT queue step messages — returns the pending
243
+ * Creates events for all operations but does NOT queue step messages; returns the pending
230
244
  * steps so the caller can decide which to execute inline vs queue to background.
231
245
  *
232
246
  * Processing order:
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared shape for finalizing a step whose arguments failed to serialize
3
+ * (used by both the node:vm suspension handler and the QuickJS entrypoint).
4
+ *
5
+ * The world requires a `step_created` before any terminal step event, and
6
+ * the step's real input is precisely what refused to serialize, so the
7
+ * finalization writes a placeholder input. The marker string makes the
8
+ * placeholder distinguishable from a genuine zero-argument step in
9
+ * `workflow inspect steps` and the observability UI: a reader sees
10
+ * "input unavailable" instead of "no arguments".
11
+ */
12
+ export declare const UNSERIALIZABLE_STEP_INPUT_MARKER = "[input unavailable: step argument serialization failed]";
13
+ /**
14
+ * The placeholder value serialized into the failed step's `step_created`
15
+ * input. Matches the `{ args, closureVars, thisVal }` triple
16
+ * `dehydrateStepArguments` / the QuickJS bootstrap produce for real steps,
17
+ * so every consumer hydrates it uniformly, plus the structural flag the
18
+ * step executor checks before running user code (see
19
+ * {@link isUnserializableStepInputPlaceholder}).
20
+ */
21
+ export declare function unserializableStepInputPlaceholder(): Record<string, unknown>;
22
+ /**
23
+ * Whether a hydrated step input is the finalization placeholder.
24
+ *
25
+ * Finalization writes `step_created` (placeholder) and `step_failed` as two
26
+ * separate durable writes; a crash or transient failure between them leaves
27
+ * a pending step whose stored input is the placeholder. Redelivery then
28
+ * dispatches that step through normal crash recovery: the executor calls
29
+ * this before running user code and completes the intended failure (a fatal
30
+ * SerializationError → `step_failed`) instead of silently invoking the step
31
+ * body with placeholder arguments.
32
+ */
33
+ export declare function isUnserializableStepInputPlaceholder(hydratedInput: unknown): boolean;
34
+ //# sourceMappingURL=unserializable-step.d.ts.map
@@ -24,7 +24,7 @@ export type WorkflowVmMode = (typeof WORKFLOW_VMS)[number];
24
24
  *
25
25
  * Returns the configured engine, or `undefined` if unset/empty.
26
26
  * Throws {@link WorkflowRuntimeError} if the value is set but not one of
27
- * the known engines — catching misconfiguration early is better than
27
+ * the known engines, since catching misconfiguration early is better than
28
28
  * silently falling back to the default.
29
29
  */
30
30
  export declare function getWorkflowVmFromEnv(env?: NodeJS.ProcessEnv): WorkflowVmMode | undefined;
@@ -40,5 +40,5 @@ export declare function getWorkflowVmFromEnv(env?: NodeJS.ProcessEnv): WorkflowV
40
40
  * Throws if `WORKFLOW_VM` or `executionContext.workflowVm` is set to an
41
41
  * unknown value.
42
42
  */
43
- export declare function useQuickJSVm(workflowRun: WorkflowRun): boolean;
43
+ export declare function useQuickJSVm(workflowRun: Pick<WorkflowRun, 'executionContext'>): boolean;
44
44
  //# sourceMappingURL=vm-mode.d.ts.map
@@ -10,7 +10,7 @@
10
10
  * The continuation is keyed on the wait's correlationId: while a wait is
11
11
  * pending, every replay pass over the run re-observes it (e.g., once per
12
12
  * step completion in `Promise.all([steps..., sleep()])`), and without
13
- * dedupe each pass would enqueue another delayed continuation — each one
13
+ * dedupe each pass would enqueue another delayed continuation: each one
14
14
  * a spurious full replay when the wait elapses, and each a fresh message
15
15
  * that resets the delivery-attempt runaway guard. A key is attached in
16
16
  * all cases: some worlds (e.g. world-postgres) serialize key-less
@@ -29,7 +29,7 @@
29
29
  * situations exist, each with its own key variation:
30
30
  *
31
31
  * - Waits longer than the maximum queue delay are chained: the delay is
32
- * clamped to `WAIT_CONTINUATION_MAX_DELAY_SECONDS` (23h — VQS messages
32
+ * clamped to `WAIT_CONTINUATION_MAX_DELAY_SECONDS` (23h: VQS messages
33
33
  * have a 24h retention limit, and one hour of buffer matches
34
34
  * world-vercel's own clamp for delayed re-enqueues), so the
35
35
  * continuation intentionally fires early, re-observes the wait, and
@@ -37,7 +37,7 @@
37
37
  * (`ceil(timeoutSeconds / maxDelay)`): stable for every re-observation
38
38
  * within the same hop window (so passes dedupe), decremented at each
39
39
  * hop delivery (so the chain always advances). Worlds without a delay
40
- * limit (world-postgres, world-local) simply take the same ≤23h hops.
40
+ * limit (world-postgres, world-local) take the same ≤23h hops.
41
41
  *
42
42
  * - Near-elapsed waits (≤2s remaining) get a second-bucketed suffix. A
43
43
  * continuation delivered marginally early (clock skew between the
@@ -50,11 +50,25 @@
50
50
  * Mid-range waits (more than the near-elapsed threshold, at most one
51
51
  * hop) use the bare correlationId: every re-observation targets the same
52
52
  * deadline, so deduping to the first message is semantically lossless.
53
- * Host clock skew beyond the near-elapsed threshold could in principle
54
- * deliver such a continuation early enough to re-observe its wait and
55
- * lose the re-enqueue to the burnt key; the threshold is the skew
56
- * tolerance we accept for the benefit of exactly-one continuation per
57
- * wait.
53
+ *
54
+ * That last case used to be the one hole in the scheme, and it was not
55
+ * theoretical. Any delivery early enough to re-observe its own wait as
56
+ * pending burns the bare key on the way in: the re-enqueue is dropped by
57
+ * the dedupe window, nothing else is scheduled to wake the run, and no
58
+ * backstop exists for a wait the way inline step ownership provides one
59
+ * for a step. The run sleeps forever. Waits over the threshold have zero
60
+ * tolerance for it, which is why an infrastructure change in delivery
61
+ * timing was able to strand runs across every published SDK version at
62
+ * once, all of them keyed this way.
63
+ *
64
+ * So the key is no longer derived from the wait alone. A continuation
65
+ * carries the wait it was armed for and its attempt number
66
+ * ({@link WorkflowInvokePayload.waitContinuation}), and an invocation
67
+ * that recognizes itself as the continuation for a wait that is still
68
+ * pending arms the next one at `attempt + 1`. Attempts advance ONLY when
69
+ * an early delivery actually happens, so the normal path is untouched:
70
+ * attempt 0 keys exactly as before, and every re-observation within one
71
+ * attempt still collapses to a single message.
58
72
  */
59
73
  /**
60
74
  * Maximum `delaySeconds` for a single wait-continuation message. Waits
@@ -79,6 +93,14 @@ export interface WaitContinuationDispatch {
79
93
  * message. `timeoutSeconds` is the time until the wait's `resumeAt`
80
94
  * (floored at 1s by the suspension handler); `waitCorrelationId`
81
95
  * identifies the wait so repeated suspension passes dedupe.
96
+ *
97
+ * `attempt` is the number of continuations already spent on this wait, taken
98
+ * from the incoming message when this invocation IS one of them (see
99
+ * {@link WorkflowInvokePayload.waitContinuation}). It only ever moves when a
100
+ * continuation arrived before its wait elapsed, which is exactly when the
101
+ * previous key is spent and re-using it would drop the message. Attempt 0
102
+ * keys identically to the scheme before attempts existed, so the ordinary
103
+ * path — arm once, deliver once, complete — is byte-for-byte unchanged.
82
104
  */
83
- export declare function getWaitContinuationDispatch(timeoutSeconds: number, waitCorrelationId: string, now?: number): WaitContinuationDispatch;
105
+ export declare function getWaitContinuationDispatch(timeoutSeconds: number, waitCorrelationId: string, now?: number, attempt?: number): WaitContinuationDispatch;
84
106
  //# sourceMappingURL=wait-continuation.d.ts.map
@@ -3,7 +3,7 @@ export declare function waitUntil(promise: Promise<unknown>): void;
3
3
  * Schedule a background promise via `waitUntil`, guaranteeing that the
4
4
  * promise handed to `waitUntil` can never reject. Nothing consumes a
5
5
  * `waitUntil` promise, so a rejection surfaces as an `unhandledRejection`
6
- * and can crash the process — even when the same underlying error is
6
+ * and can crash the process, even when the same underlying error is
7
7
  * correctly handled by an awaited copy elsewhere.
8
8
  *
9
9
  * Expected client-disconnect errors (`AbortError` / `ResponseAborted`)