@proompteng/temporal-bun-sdk 0.3.0 → 0.5.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 (170) hide show
  1. package/README.md +54 -11
  2. package/dist/src/bin/replay-command.d.ts.map +1 -1
  3. package/dist/src/bin/replay-command.js +6 -2
  4. package/dist/src/bin/replay-command.js.map +1 -1
  5. package/dist/src/bin/temporal-bun.d.ts +1 -1
  6. package/dist/src/bin/temporal-bun.d.ts.map +1 -1
  7. package/dist/src/bin/temporal-bun.js +75 -1
  8. package/dist/src/bin/temporal-bun.js.map +1 -1
  9. package/dist/src/client/layer.d.ts +2 -2
  10. package/dist/src/client/layer.d.ts.map +1 -1
  11. package/dist/src/client/retries.d.ts.map +1 -1
  12. package/dist/src/client/retries.js +27 -3
  13. package/dist/src/client/retries.js.map +1 -1
  14. package/dist/src/client/serialization.d.ts +4 -1
  15. package/dist/src/client/serialization.d.ts.map +1 -1
  16. package/dist/src/client/serialization.js +2 -2
  17. package/dist/src/client/serialization.js.map +1 -1
  18. package/dist/src/client.d.ts +12 -4
  19. package/dist/src/client.d.ts.map +1 -1
  20. package/dist/src/client.js +270 -63
  21. package/dist/src/client.js.map +1 -1
  22. package/dist/src/common/payloads/codecs.d.ts +38 -0
  23. package/dist/src/common/payloads/codecs.d.ts.map +1 -0
  24. package/dist/src/common/payloads/codecs.js +174 -0
  25. package/dist/src/common/payloads/codecs.js.map +1 -0
  26. package/dist/src/common/payloads/converter.d.ts +52 -3
  27. package/dist/src/common/payloads/converter.d.ts.map +1 -1
  28. package/dist/src/common/payloads/converter.js +340 -2
  29. package/dist/src/common/payloads/converter.js.map +1 -1
  30. package/dist/src/common/payloads/failure.d.ts +5 -6
  31. package/dist/src/common/payloads/failure.d.ts.map +1 -1
  32. package/dist/src/common/payloads/failure.js +3 -52
  33. package/dist/src/common/payloads/failure.js.map +1 -1
  34. package/dist/src/common/payloads/index.d.ts +1 -0
  35. package/dist/src/common/payloads/index.d.ts.map +1 -1
  36. package/dist/src/common/payloads/index.js +1 -0
  37. package/dist/src/common/payloads/index.js.map +1 -1
  38. package/dist/src/common/payloads/json-codec.d.ts.map +1 -1
  39. package/dist/src/common/payloads/json-codec.js +4 -6
  40. package/dist/src/common/payloads/json-codec.js.map +1 -1
  41. package/dist/src/config.d.ts +25 -0
  42. package/dist/src/config.d.ts.map +1 -1
  43. package/dist/src/config.js +111 -1
  44. package/dist/src/config.js.map +1 -1
  45. package/dist/src/interceptors/client.d.ts +28 -0
  46. package/dist/src/interceptors/client.d.ts.map +1 -0
  47. package/dist/src/interceptors/client.js +190 -0
  48. package/dist/src/interceptors/client.js.map +1 -0
  49. package/dist/src/interceptors/types.d.ts +33 -0
  50. package/dist/src/interceptors/types.d.ts.map +1 -0
  51. package/dist/src/interceptors/types.js +44 -0
  52. package/dist/src/interceptors/types.js.map +1 -0
  53. package/dist/src/interceptors/worker.d.ts +22 -0
  54. package/dist/src/interceptors/worker.d.ts.map +1 -0
  55. package/dist/src/interceptors/worker.js +177 -0
  56. package/dist/src/interceptors/worker.js.map +1 -0
  57. package/dist/src/observability/index.d.ts +4 -0
  58. package/dist/src/observability/index.d.ts.map +1 -1
  59. package/dist/src/observability/index.js +4 -0
  60. package/dist/src/observability/index.js.map +1 -1
  61. package/dist/src/observability/metrics.d.ts.map +1 -1
  62. package/dist/src/observability/metrics.js +70 -10
  63. package/dist/src/observability/metrics.js.map +1 -1
  64. package/dist/src/observability/opentelemetry.d.ts +20 -0
  65. package/dist/src/observability/opentelemetry.d.ts.map +1 -0
  66. package/dist/src/observability/opentelemetry.js +334 -0
  67. package/dist/src/observability/opentelemetry.js.map +1 -0
  68. package/dist/src/proto/temporal/api/activity/v1/message_pb.js +1 -1
  69. package/dist/src/proto/temporal/api/batch/v1/message_pb.js +1 -1
  70. package/dist/src/proto/temporal/api/command/v1/message_pb.js +1 -1
  71. package/dist/src/proto/temporal/api/common/v1/message_pb.js +1 -1
  72. package/dist/src/proto/temporal/api/deployment/v1/message_pb.js +1 -1
  73. package/dist/src/proto/temporal/api/enums/v1/batch_operation_pb.js +1 -1
  74. package/dist/src/proto/temporal/api/enums/v1/command_type_pb.js +1 -1
  75. package/dist/src/proto/temporal/api/enums/v1/common_pb.js +1 -1
  76. package/dist/src/proto/temporal/api/enums/v1/deployment_pb.js +1 -1
  77. package/dist/src/proto/temporal/api/enums/v1/event_type_pb.js +1 -1
  78. package/dist/src/proto/temporal/api/enums/v1/failed_cause_pb.js +1 -1
  79. package/dist/src/proto/temporal/api/enums/v1/namespace_pb.js +1 -1
  80. package/dist/src/proto/temporal/api/enums/v1/nexus_pb.js +1 -1
  81. package/dist/src/proto/temporal/api/enums/v1/query_pb.js +1 -1
  82. package/dist/src/proto/temporal/api/enums/v1/reset_pb.js +1 -1
  83. package/dist/src/proto/temporal/api/enums/v1/schedule_pb.js +1 -1
  84. package/dist/src/proto/temporal/api/enums/v1/task_queue_pb.js +1 -1
  85. package/dist/src/proto/temporal/api/enums/v1/update_pb.js +1 -1
  86. package/dist/src/proto/temporal/api/enums/v1/workflow_pb.js +1 -1
  87. package/dist/src/proto/temporal/api/errordetails/v1/message_pb.js +1 -1
  88. package/dist/src/proto/temporal/api/export/v1/message_pb.js +1 -1
  89. package/dist/src/proto/temporal/api/failure/v1/message_pb.js +1 -1
  90. package/dist/src/proto/temporal/api/filter/v1/message_pb.js +1 -1
  91. package/dist/src/proto/temporal/api/history/v1/message_pb.js +1 -1
  92. package/dist/src/proto/temporal/api/namespace/v1/message_pb.js +1 -1
  93. package/dist/src/proto/temporal/api/nexus/v1/message_pb.js +1 -1
  94. package/dist/src/proto/temporal/api/operatorservice/v1/request_response_pb.js +1 -1
  95. package/dist/src/proto/temporal/api/operatorservice/v1/service_pb.js +1 -1
  96. package/dist/src/proto/temporal/api/protocol/v1/message_pb.js +1 -1
  97. package/dist/src/proto/temporal/api/query/v1/message_pb.js +1 -1
  98. package/dist/src/proto/temporal/api/replication/v1/message_pb.js +1 -1
  99. package/dist/src/proto/temporal/api/rules/v1/message_pb.js +1 -1
  100. package/dist/src/proto/temporal/api/sdk/v1/enhanced_stack_trace_pb.js +1 -1
  101. package/dist/src/proto/temporal/api/sdk/v1/task_complete_metadata_pb.js +1 -1
  102. package/dist/src/proto/temporal/api/sdk/v1/user_metadata_pb.js +1 -1
  103. package/dist/src/proto/temporal/api/sdk/v1/worker_config_pb.js +1 -1
  104. package/dist/src/proto/temporal/api/sdk/v1/workflow_metadata_pb.js +1 -1
  105. package/dist/src/proto/temporal/api/taskqueue/v1/message_pb.js +1 -1
  106. package/dist/src/proto/temporal/api/update/v1/message_pb.js +1 -1
  107. package/dist/src/proto/temporal/api/version/v1/message_pb.js +1 -1
  108. package/dist/src/proto/temporal/api/worker/v1/message_pb.js +1 -1
  109. package/dist/src/proto/temporal/api/workflow/v1/message_pb.js +1 -1
  110. package/dist/src/proto/temporal/api/workflowservice/v1/request_response_pb.js +1 -1
  111. package/dist/src/proto/temporal/api/workflowservice/v1/service_pb.js +1 -1
  112. package/dist/src/runtime/cli-layer.d.ts +4 -3
  113. package/dist/src/runtime/cli-layer.d.ts.map +1 -1
  114. package/dist/src/runtime/cli-layer.js +5 -2
  115. package/dist/src/runtime/cli-layer.js.map +1 -1
  116. package/dist/src/runtime/effect-layers.d.ts +9 -0
  117. package/dist/src/runtime/effect-layers.d.ts.map +1 -1
  118. package/dist/src/runtime/effect-layers.js +15 -0
  119. package/dist/src/runtime/effect-layers.js.map +1 -1
  120. package/dist/src/worker/runtime.d.ts +6 -0
  121. package/dist/src/worker/runtime.d.ts.map +1 -1
  122. package/dist/src/worker/runtime.js +853 -70
  123. package/dist/src/worker/runtime.js.map +1 -1
  124. package/dist/src/worker/sticky-cache.d.ts +15 -0
  125. package/dist/src/worker/sticky-cache.d.ts.map +1 -1
  126. package/dist/src/worker/sticky-cache.js +26 -0
  127. package/dist/src/worker/sticky-cache.js.map +1 -1
  128. package/dist/src/worker/update-protocol.d.ts.map +1 -1
  129. package/dist/src/worker/update-protocol.js +15 -0
  130. package/dist/src/worker/update-protocol.js.map +1 -1
  131. package/dist/src/worker.js +1 -0
  132. package/dist/src/worker.js.map +1 -1
  133. package/dist/src/workflow/commands.d.ts +40 -3
  134. package/dist/src/workflow/commands.d.ts.map +1 -1
  135. package/dist/src/workflow/commands.js +161 -7
  136. package/dist/src/workflow/commands.js.map +1 -1
  137. package/dist/src/workflow/context.d.ts +59 -2
  138. package/dist/src/workflow/context.d.ts.map +1 -1
  139. package/dist/src/workflow/context.js +345 -16
  140. package/dist/src/workflow/context.js.map +1 -1
  141. package/dist/src/workflow/definition.js.map +1 -1
  142. package/dist/src/workflow/determinism.d.ts +9 -0
  143. package/dist/src/workflow/determinism.d.ts.map +1 -1
  144. package/dist/src/workflow/determinism.js +145 -16
  145. package/dist/src/workflow/determinism.js.map +1 -1
  146. package/dist/src/workflow/errors.d.ts +3 -0
  147. package/dist/src/workflow/errors.d.ts.map +1 -1
  148. package/dist/src/workflow/errors.js +6 -0
  149. package/dist/src/workflow/errors.js.map +1 -1
  150. package/dist/src/workflow/executor.d.ts +6 -0
  151. package/dist/src/workflow/executor.d.ts.map +1 -1
  152. package/dist/src/workflow/executor.js +147 -18
  153. package/dist/src/workflow/executor.js.map +1 -1
  154. package/dist/src/workflow/inbound.d.ts +1 -0
  155. package/dist/src/workflow/inbound.d.ts.map +1 -1
  156. package/dist/src/workflow/inbound.js +1 -0
  157. package/dist/src/workflow/inbound.js.map +1 -1
  158. package/dist/src/workflow/index.d.ts +5 -0
  159. package/dist/src/workflow/index.d.ts.map +1 -1
  160. package/dist/src/workflow/index.js +3 -0
  161. package/dist/src/workflow/index.js.map +1 -1
  162. package/dist/src/workflow/log.d.ts +18 -0
  163. package/dist/src/workflow/log.d.ts.map +1 -0
  164. package/dist/src/workflow/log.js +36 -0
  165. package/dist/src/workflow/log.js.map +1 -0
  166. package/dist/src/workflow/replay.d.ts +31 -3
  167. package/dist/src/workflow/replay.d.ts.map +1 -1
  168. package/dist/src/workflow/replay.js +763 -67
  169. package/dist/src/workflow/replay.js.map +1 -1
  170. package/package.json +4 -3
@@ -2,12 +2,13 @@ import { create } from '@bufbuild/protobuf';
2
2
  import { Effect } from 'effect';
3
3
  import { durationToMillis } from '../common/duration';
4
4
  import { decodePayloadsToValues, encodeValuesToPayloads } from '../common/payloads';
5
- import { PayloadsSchema } from '../proto/temporal/api/common/v1/message_pb';
5
+ import { PayloadsSchema, } from '../proto/temporal/api/common/v1/message_pb';
6
6
  import { EventType } from '../proto/temporal/api/enums/v1/event_type_pb';
7
7
  import { ParentClosePolicy, TimeoutType, WorkflowIdReusePolicy } from '../proto/temporal/api/enums/v1/workflow_pb';
8
8
  import { intentsEqual, stableStringify } from './determinism';
9
9
  export const DETERMINISM_MARKER_NAME = 'temporal-bun-sdk/determinism';
10
- const DETERMINISM_MARKER_SCHEMA_VERSION = 1;
10
+ const DETERMINISM_MARKER_SCHEMA_VERSION_V1 = 1;
11
+ const DETERMINISM_MARKER_SCHEMA_VERSION_V2 = 2;
11
12
  const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
12
13
  /**
13
14
  * Encode a workflow determinism snapshot into a marker payload map suitable for
@@ -15,13 +16,25 @@ const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
15
16
  */
16
17
  export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPromise(async () => {
17
18
  const recordedAt = (input.recordedAt ?? new Date()).toISOString();
18
- const envelope = {
19
- schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION,
20
- workflow: input.info,
21
- determinismState: input.determinismState,
22
- lastEventId: normalizeEventId(input.lastEventId),
23
- recordedAtIso: recordedAt,
24
- };
19
+ const markerType = input.markerType ?? 'full';
20
+ if (markerType === 'delta' && !input.determinismDelta) {
21
+ throw new Error('Determinism marker delta payload missing');
22
+ }
23
+ const envelope = markerType === 'delta'
24
+ ? {
25
+ schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
26
+ workflow: input.info,
27
+ determinismDelta: input.determinismDelta,
28
+ lastEventId: normalizeEventId(input.lastEventId),
29
+ recordedAtIso: recordedAt,
30
+ }
31
+ : {
32
+ schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
33
+ workflow: input.info,
34
+ determinismState: input.determinismState,
35
+ lastEventId: normalizeEventId(input.lastEventId),
36
+ recordedAtIso: recordedAt,
37
+ };
25
38
  const payloads = await encodeValuesToPayloads(converter, [envelope]);
26
39
  if (!payloads || payloads.length === 0) {
27
40
  throw new Error('Failed to encode determinism marker payloads');
@@ -30,6 +43,20 @@ export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPr
30
43
  [DETERMINISM_MARKER_DETAIL_KEY]: create(PayloadsSchema, { payloads }),
31
44
  };
32
45
  });
46
+ export const measurePayloadsByteSize = (details) => {
47
+ let total = 0;
48
+ for (const payloads of Object.values(details)) {
49
+ for (const payload of payloads.payloads ?? []) {
50
+ total += payload.data?.byteLength ?? 0;
51
+ const metadata = payload.metadata ?? {};
52
+ for (const value of Object.values(metadata)) {
53
+ total += value?.byteLength ?? 0;
54
+ }
55
+ }
56
+ }
57
+ return total;
58
+ };
59
+ export const encodeDeterminismMarkerDetailsWithSize = (converter, input) => encodeDeterminismMarkerDetails(converter, input).pipe(Effect.map((details) => ({ details, sizeBytes: measurePayloadsByteSize(details) })));
33
60
  /**
34
61
  * Decode determinism snapshot metadata from a marker payload map. Returns
35
62
  * `undefined` when the supplied details do not contain a determinism snapshot.
@@ -57,10 +84,14 @@ export const decodeDeterminismMarkerEnvelope = (input) => Effect.tryPromise(asyn
57
84
  export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
58
85
  const events = sortHistoryEvents(intake.history ?? []);
59
86
  const shouldUseDeterminismMarker = intake.ignoreDeterminismMarker !== true;
60
- let markerSnapshot;
61
- const updateEntries = collectWorkflowUpdateEntries(events);
87
+ let markerState;
88
+ let markerLastEventId = null;
89
+ let markerEventId = null;
90
+ let markerEventIndex = null;
91
+ let markerInvalid = false;
92
+ let hasMarker = false;
62
93
  if (shouldUseDeterminismMarker) {
63
- for (const event of events) {
94
+ for (const [index, event] of events.entries()) {
64
95
  if (event.eventType !== EventType.MARKER_RECORDED) {
65
96
  continue;
66
97
  }
@@ -72,36 +103,82 @@ export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
72
103
  details: event.attributes.value.details,
73
104
  }), () => Effect.succeed(undefined));
74
105
  if (decoded) {
75
- markerSnapshot = decoded;
106
+ const applied = applyDeterminismMarker(markerState, decoded);
107
+ if (!applied) {
108
+ markerInvalid = true;
109
+ break;
110
+ }
111
+ markerState = applied;
112
+ markerLastEventId = decoded.lastEventId;
113
+ markerEventId = normalizeEventId(event.eventId);
114
+ markerEventIndex = index;
115
+ hasMarker = true;
76
116
  }
77
117
  }
78
118
  }
79
119
  const extractedFailureMetadata = extractFailureMetadata(events);
80
- const replayUpdateInvocations = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(events, intake.dataConverter));
81
- if (shouldUseDeterminismMarker && markerSnapshot) {
82
- let determinismState = cloneDeterminismState(markerSnapshot.determinismState);
83
- if (!determinismState.failureMetadata && extractedFailureMetadata) {
84
- determinismState = {
85
- ...determinismState,
86
- failureMetadata: extractedFailureMetadata,
87
- };
120
+ const historyLastEventId = resolveHistoryLastEventId(events) ?? null;
121
+ if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState && markerEventIndex !== null) {
122
+ const historyKinds = collectCommandKinds(events.slice(0, markerEventIndex + 1));
123
+ const markerKinds = markerState.commandHistory.map((entry) => entry.intent.kind);
124
+ if (!commandKindsMatch(markerKinds, historyKinds)) {
125
+ markerInvalid = true;
126
+ markerState = undefined;
88
127
  }
89
- if ((!determinismState.updates || determinismState.updates.length === 0) && updateEntries.length > 0) {
90
- determinismState = {
91
- ...determinismState,
92
- updates: updateEntries,
128
+ }
129
+ const eventsAfterMarker = shouldUseDeterminismMarker && hasMarker && markerEventIndex !== null ? events.slice(markerEventIndex + 1) : events;
130
+ const replayUpdateInvocationsAfterMarker = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(eventsAfterMarker, intake.dataConverter));
131
+ const updateEntriesAfterMarker = collectWorkflowUpdateEntries(eventsAfterMarker);
132
+ const replayUpdateInvocationsFull = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(events, intake.dataConverter));
133
+ const updateEntriesFull = collectWorkflowUpdateEntries(events);
134
+ const markerCoversHistory = markerEventId !== null && markerEventId === historyLastEventId;
135
+ const historyCommandCount = countHistoryCommandEvents(events);
136
+ if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState) {
137
+ if (markerCoversHistory || eventsAfterMarker.length === 0) {
138
+ let determinismState = cloneDeterminismState(markerState);
139
+ if (!determinismState.failureMetadata && extractedFailureMetadata) {
140
+ determinismState = {
141
+ ...determinismState,
142
+ failureMetadata: extractedFailureMetadata,
143
+ };
144
+ }
145
+ if ((!determinismState.updates || determinismState.updates.length === 0) &&
146
+ updateEntriesAfterMarker.length > 0) {
147
+ determinismState = {
148
+ ...determinismState,
149
+ updates: updateEntriesAfterMarker,
150
+ };
151
+ }
152
+ determinismState = mergePendingQueryRequests(determinismState, intake.queries);
153
+ const latestEventId = historyLastEventId ?? markerLastEventId ?? null;
154
+ if (determinismState.commandHistory.length !== historyCommandCount) {
155
+ return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
156
+ }
157
+ return {
158
+ determinismState,
159
+ lastEventId: latestEventId,
160
+ hasDeterminismMarker: true,
161
+ markerState: cloneDeterminismState(markerState),
162
+ ...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
93
163
  };
94
164
  }
95
- determinismState = mergePendingQueryRequests(determinismState, intake.queries);
96
- const latestEventId = resolveHistoryLastEventId(events) ?? markerSnapshot.lastEventId ?? null;
165
+ const replayed = yield* reconstructDeterminismState(eventsAfterMarker, intake, extractedFailureMetadata, updateEntriesAfterMarker, replayUpdateInvocationsAfterMarker, {
166
+ seedState: markerState,
167
+ historyLastEventId,
168
+ seedHistoryEvents: markerEventIndex !== null ? events.slice(0, markerEventIndex + 1) : undefined,
169
+ });
170
+ if (replayed.determinismState.commandHistory.length !== historyCommandCount) {
171
+ return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
172
+ }
97
173
  return {
98
- determinismState,
99
- lastEventId: latestEventId,
174
+ ...replayed,
100
175
  hasDeterminismMarker: true,
101
- ...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
176
+ markerState: cloneDeterminismState(markerState),
177
+ lastEventId: historyLastEventId ?? markerLastEventId ?? null,
178
+ ...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
102
179
  };
103
180
  }
104
- return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntries, replayUpdateInvocations);
181
+ return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
105
182
  });
106
183
  /**
107
184
  * Diff determinism state against freshly emitted intents to produce rich
@@ -220,6 +297,7 @@ export const cloneDeterminismState = (state) => ({
220
297
  })),
221
298
  randomValues: [...state.randomValues],
222
299
  timeValues: [...state.timeValues],
300
+ ...(state.logCount !== undefined ? { logCount: state.logCount } : {}),
223
301
  failureMetadata: state.failureMetadata ? { ...state.failureMetadata } : undefined,
224
302
  signals: state.signals ? state.signals.map((record) => ({ ...record })) : [],
225
303
  queries: state.queries ? state.queries.map((record) => ({ ...record })) : [],
@@ -384,12 +462,295 @@ const resolveCommandMismatchMetadata = (expectedEntry, actualEntry) => {
384
462
  eventType,
385
463
  };
386
464
  };
387
- const reconstructDeterminismState = (events, intake, failureMetadata, precomputedUpdates, replayUpdateInvocations = []) => Effect.gen(function* () {
388
- let sequence = 0;
389
- const commandHistory = [];
390
- const signalRecords = [];
391
- const queryRecords = [];
392
- const updates = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
465
+ const seedDeterminismEventMaps = (state, seedEvents) => {
466
+ const scheduledActivities = new Map();
467
+ const timersByStartEventId = new Map();
468
+ if (!state) {
469
+ if (seedEvents) {
470
+ seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
471
+ }
472
+ return { scheduledActivities, timersByStartEventId };
473
+ }
474
+ for (const entry of state.commandHistory) {
475
+ const eventId = entry.metadata?.eventId;
476
+ if (!eventId) {
477
+ continue;
478
+ }
479
+ if (entry.intent.kind === 'schedule-activity') {
480
+ scheduledActivities.set(eventId, entry.intent.activityId);
481
+ continue;
482
+ }
483
+ if (entry.intent.kind === 'start-timer') {
484
+ timersByStartEventId.set(eventId, entry.intent.timerId);
485
+ }
486
+ }
487
+ if (seedEvents) {
488
+ seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
489
+ }
490
+ return { scheduledActivities, timersByStartEventId };
491
+ };
492
+ const seedDeterminismEventMapsFromHistory = (scheduledActivities, timersByStartEventId, events) => {
493
+ for (const event of events) {
494
+ switch (event.eventType) {
495
+ case EventType.ACTIVITY_TASK_SCHEDULED: {
496
+ if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
497
+ break;
498
+ }
499
+ const activityId = event.attributes.value.activityId;
500
+ const eventId = normalizeEventId(event.eventId);
501
+ if (activityId && eventId && !scheduledActivities.has(eventId)) {
502
+ scheduledActivities.set(eventId, activityId);
503
+ }
504
+ break;
505
+ }
506
+ case EventType.TIMER_STARTED: {
507
+ if (event.attributes?.case !== 'timerStartedEventAttributes') {
508
+ break;
509
+ }
510
+ const timerId = event.attributes.value.timerId;
511
+ const eventId = normalizeEventId(event.eventId);
512
+ if (timerId && eventId && !timersByStartEventId.has(eventId)) {
513
+ timersByStartEventId.set(eventId, timerId);
514
+ }
515
+ break;
516
+ }
517
+ default:
518
+ break;
519
+ }
520
+ }
521
+ };
522
+ const countHistoryCommandEvents = (events) => {
523
+ const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
524
+ let count = 0;
525
+ for (const event of events) {
526
+ switch (event.eventType) {
527
+ case EventType.ACTIVITY_TASK_SCHEDULED: {
528
+ if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
529
+ break;
530
+ }
531
+ if (event.attributes.value.activityType?.name) {
532
+ count += 1;
533
+ }
534
+ break;
535
+ }
536
+ case EventType.TIMER_STARTED: {
537
+ if (event.attributes?.case !== 'timerStartedEventAttributes') {
538
+ break;
539
+ }
540
+ if (durationToMillis(event.attributes.value.startToFireTimeout) !== undefined) {
541
+ count += 1;
542
+ }
543
+ break;
544
+ }
545
+ case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
546
+ if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
547
+ break;
548
+ }
549
+ if (event.attributes.value.workflowType?.name) {
550
+ count += 1;
551
+ }
552
+ break;
553
+ }
554
+ case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
555
+ if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
556
+ break;
557
+ }
558
+ const scheduledEventId = normalizeBigintIdentifier(event.attributes.value.scheduledEventId);
559
+ if (scheduledEventId && scheduledActivities.has(scheduledEventId)) {
560
+ count += 1;
561
+ }
562
+ break;
563
+ }
564
+ case EventType.TIMER_CANCELED: {
565
+ if (event.attributes?.case !== 'timerCanceledEventAttributes') {
566
+ break;
567
+ }
568
+ const startedEventId = normalizeBigintIdentifier(event.attributes.value.startedEventId);
569
+ if (startedEventId && timersByStartEventId.has(startedEventId)) {
570
+ count += 1;
571
+ }
572
+ break;
573
+ }
574
+ case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
575
+ if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
576
+ break;
577
+ }
578
+ if (event.attributes.value.signalName) {
579
+ count += 1;
580
+ }
581
+ break;
582
+ }
583
+ case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
584
+ if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
585
+ break;
586
+ }
587
+ count += 1;
588
+ break;
589
+ }
590
+ case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
591
+ if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
592
+ break;
593
+ }
594
+ count += 1;
595
+ break;
596
+ }
597
+ case EventType.MARKER_RECORDED: {
598
+ if (event.attributes?.case !== 'markerRecordedEventAttributes') {
599
+ break;
600
+ }
601
+ if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
602
+ count += 1;
603
+ }
604
+ break;
605
+ }
606
+ case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
607
+ if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
608
+ break;
609
+ }
610
+ count += 1;
611
+ break;
612
+ }
613
+ case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
614
+ if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
615
+ break;
616
+ }
617
+ count += 1;
618
+ break;
619
+ }
620
+ default:
621
+ break;
622
+ }
623
+ }
624
+ return count;
625
+ };
626
+ const commandKindsMatch = (expected, actual) => {
627
+ if (expected.length !== actual.length) {
628
+ return false;
629
+ }
630
+ for (let index = 0; index < expected.length; index += 1) {
631
+ if (expected[index] !== actual[index]) {
632
+ return false;
633
+ }
634
+ }
635
+ return true;
636
+ };
637
+ const collectCommandKinds = (events) => {
638
+ const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
639
+ const kinds = [];
640
+ for (const event of events) {
641
+ switch (event.eventType) {
642
+ case EventType.ACTIVITY_TASK_SCHEDULED: {
643
+ if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
644
+ break;
645
+ }
646
+ if (event.attributes.value.activityType?.name) {
647
+ kinds.push('schedule-activity');
648
+ }
649
+ break;
650
+ }
651
+ case EventType.TIMER_STARTED: {
652
+ if (event.attributes?.case !== 'timerStartedEventAttributes') {
653
+ break;
654
+ }
655
+ if (event.attributes.value.timerId) {
656
+ kinds.push('start-timer');
657
+ }
658
+ break;
659
+ }
660
+ case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
661
+ if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
662
+ break;
663
+ }
664
+ if (event.attributes.value.workflowType?.name) {
665
+ kinds.push('start-child-workflow');
666
+ }
667
+ break;
668
+ }
669
+ case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
670
+ if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
671
+ break;
672
+ }
673
+ const scheduledKey = normalizeEventId(event.attributes.value.scheduledEventId);
674
+ if (scheduledKey && scheduledActivities.has(scheduledKey)) {
675
+ kinds.push('request-cancel-activity');
676
+ }
677
+ break;
678
+ }
679
+ case EventType.TIMER_CANCELED: {
680
+ if (event.attributes?.case !== 'timerCanceledEventAttributes') {
681
+ break;
682
+ }
683
+ const startedKey = normalizeEventId(event.attributes.value.startedEventId);
684
+ if (startedKey && timersByStartEventId.has(startedKey)) {
685
+ kinds.push('cancel-timer');
686
+ }
687
+ break;
688
+ }
689
+ case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
690
+ if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
691
+ break;
692
+ }
693
+ if (event.attributes.value.signalName) {
694
+ kinds.push('signal-external-workflow');
695
+ }
696
+ break;
697
+ }
698
+ case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
699
+ if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
700
+ break;
701
+ }
702
+ if (event.attributes.value.workflowExecution?.workflowId) {
703
+ kinds.push('request-cancel-external-workflow');
704
+ }
705
+ break;
706
+ }
707
+ case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
708
+ if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
709
+ break;
710
+ }
711
+ kinds.push('continue-as-new');
712
+ break;
713
+ }
714
+ case EventType.MARKER_RECORDED: {
715
+ if (event.attributes?.case !== 'markerRecordedEventAttributes') {
716
+ break;
717
+ }
718
+ if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
719
+ kinds.push('record-marker');
720
+ }
721
+ break;
722
+ }
723
+ case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
724
+ if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
725
+ break;
726
+ }
727
+ kinds.push('upsert-search-attributes');
728
+ break;
729
+ }
730
+ case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
731
+ if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
732
+ break;
733
+ }
734
+ kinds.push('modify-workflow-properties');
735
+ break;
736
+ }
737
+ default:
738
+ break;
739
+ }
740
+ }
741
+ return kinds;
742
+ };
743
+ const reconstructDeterminismState = (events, intake, failureMetadata, precomputedUpdates, replayUpdateInvocations = [], options) => Effect.gen(function* () {
744
+ const seededState = options?.seedState ? cloneDeterminismState(options.seedState) : undefined;
745
+ let sequence = seededState?.commandHistory.length ?? 0;
746
+ const commandHistory = seededState ? [...seededState.commandHistory] : [];
747
+ const signalRecords = seededState ? [...seededState.signals] : [];
748
+ const queryRecords = seededState ? [...seededState.queries] : [];
749
+ const updateEntries = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
750
+ const updates = seededState?.updates && seededState.updates.length > 0
751
+ ? [...seededState.updates, ...updateEntries]
752
+ : updateEntries;
753
+ const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(seededState, options?.seedHistoryEvents);
393
754
  for (const event of events) {
394
755
  switch (event.eventType) {
395
756
  case EventType.ACTIVITY_TASK_SCHEDULED: {
@@ -402,6 +763,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
402
763
  intent,
403
764
  metadata: buildCommandMetadata(event, event.attributes.value),
404
765
  });
766
+ const scheduledKey = normalizeEventId(event.eventId);
767
+ if (scheduledKey) {
768
+ scheduledActivities.set(scheduledKey, intent.activityId);
769
+ }
405
770
  sequence += 1;
406
771
  }
407
772
  break;
@@ -416,6 +781,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
416
781
  intent,
417
782
  metadata: buildCommandMetadata(event, event.attributes.value),
418
783
  });
784
+ const startKey = normalizeEventId(event.eventId);
785
+ if (startKey) {
786
+ timersByStartEventId.set(startKey, intent.timerId);
787
+ }
419
788
  sequence += 1;
420
789
  }
421
790
  break;
@@ -434,6 +803,34 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
434
803
  }
435
804
  break;
436
805
  }
806
+ case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
807
+ if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
808
+ break;
809
+ }
810
+ const intent = fromActivityTaskCancelRequested(event.attributes.value, sequence, scheduledActivities);
811
+ if (intent) {
812
+ commandHistory.push({
813
+ intent,
814
+ metadata: buildCommandMetadata(event, event.attributes.value),
815
+ });
816
+ sequence += 1;
817
+ }
818
+ break;
819
+ }
820
+ case EventType.TIMER_CANCELED: {
821
+ if (event.attributes?.case !== 'timerCanceledEventAttributes') {
822
+ break;
823
+ }
824
+ const intent = fromTimerCanceled(event.attributes.value, sequence, timersByStartEventId);
825
+ if (intent) {
826
+ commandHistory.push({
827
+ intent,
828
+ metadata: buildCommandMetadata(event, event.attributes.value),
829
+ });
830
+ sequence += 1;
831
+ }
832
+ break;
833
+ }
437
834
  case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
438
835
  if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
439
836
  break;
@@ -448,6 +845,20 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
448
845
  }
449
846
  break;
450
847
  }
848
+ case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
849
+ if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
850
+ break;
851
+ }
852
+ const intent = fromRequestCancelExternalWorkflow(event.attributes.value, sequence, intake);
853
+ if (intent) {
854
+ commandHistory.push({
855
+ intent,
856
+ metadata: buildCommandMetadata(event, event.attributes.value),
857
+ });
858
+ sequence += 1;
859
+ }
860
+ break;
861
+ }
451
862
  case EventType.WORKFLOW_EXECUTION_SIGNALED: {
452
863
  if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
453
864
  break;
@@ -481,15 +892,61 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
481
892
  }
482
893
  break;
483
894
  }
895
+ case EventType.MARKER_RECORDED: {
896
+ if (event.attributes?.case !== 'markerRecordedEventAttributes') {
897
+ break;
898
+ }
899
+ if (event.attributes.value.markerName === DETERMINISM_MARKER_NAME) {
900
+ break;
901
+ }
902
+ const intent = yield* fromMarkerRecorded(event.attributes.value, sequence, intake);
903
+ if (intent) {
904
+ commandHistory.push({
905
+ intent,
906
+ metadata: buildCommandMetadata(event, event.attributes.value),
907
+ });
908
+ sequence += 1;
909
+ }
910
+ break;
911
+ }
912
+ case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
913
+ if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
914
+ break;
915
+ }
916
+ const intent = yield* fromUpsertSearchAttributes(event.attributes.value, sequence, intake);
917
+ if (intent) {
918
+ commandHistory.push({
919
+ intent,
920
+ metadata: buildCommandMetadata(event, event.attributes.value),
921
+ });
922
+ sequence += 1;
923
+ }
924
+ break;
925
+ }
926
+ case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
927
+ if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
928
+ break;
929
+ }
930
+ const intent = yield* fromWorkflowPropertiesModified(event.attributes.value, sequence, intake);
931
+ if (intent) {
932
+ commandHistory.push({
933
+ intent,
934
+ metadata: buildCommandMetadata(event, event.attributes.value),
935
+ });
936
+ sequence += 1;
937
+ }
938
+ break;
939
+ }
484
940
  default:
485
941
  break;
486
942
  }
487
943
  }
944
+ const resolvedFailureMetadata = failureMetadata ?? seededState?.failureMetadata;
488
945
  let determinismState = {
489
946
  commandHistory,
490
- randomValues: [],
491
- timeValues: [],
492
- ...(failureMetadata ? { failureMetadata } : {}),
947
+ randomValues: seededState?.randomValues ?? [],
948
+ timeValues: seededState?.timeValues ?? [],
949
+ ...(resolvedFailureMetadata ? { failureMetadata: resolvedFailureMetadata } : {}),
493
950
  signals: signalRecords,
494
951
  queries: queryRecords,
495
952
  ...(updates.length > 0 ? { updates } : {}),
@@ -497,7 +954,7 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
497
954
  determinismState = mergePendingQueryRequests(determinismState, intake.queries);
498
955
  return {
499
956
  determinismState,
500
- lastEventId: resolveHistoryLastEventId(events),
957
+ lastEventId: options?.historyLastEventId ?? resolveHistoryLastEventId(events),
501
958
  hasDeterminismMarker: false,
502
959
  ...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
503
960
  };
@@ -622,6 +1079,85 @@ const fromContinueAsNew = (attributes, sequence, intake) => Effect.gen(function*
622
1079
  };
623
1080
  return intent;
624
1081
  });
1082
+ const fromActivityTaskCancelRequested = (attributes, sequence, scheduledActivities) => {
1083
+ const scheduledEventId = normalizeBigintIdentifier(attributes.scheduledEventId);
1084
+ if (!scheduledEventId) {
1085
+ return undefined;
1086
+ }
1087
+ const activityId = scheduledActivities.get(scheduledEventId);
1088
+ if (!activityId) {
1089
+ return undefined;
1090
+ }
1091
+ return {
1092
+ id: `cancel-activity-${sequence}`,
1093
+ kind: 'request-cancel-activity',
1094
+ sequence,
1095
+ activityId,
1096
+ scheduledEventId,
1097
+ };
1098
+ };
1099
+ const fromTimerCanceled = (attributes, sequence, timersByStartEventId) => {
1100
+ const startedEventId = normalizeBigintIdentifier(attributes.startedEventId);
1101
+ const timerId = attributes.timerId || (startedEventId ? timersByStartEventId.get(startedEventId) : undefined);
1102
+ if (!timerId) {
1103
+ return undefined;
1104
+ }
1105
+ return {
1106
+ id: `cancel-timer-${sequence}`,
1107
+ kind: 'cancel-timer',
1108
+ sequence,
1109
+ timerId,
1110
+ startedEventId,
1111
+ };
1112
+ };
1113
+ const fromRequestCancelExternalWorkflow = (attributes, sequence, intake) => {
1114
+ const workflowId = attributes.workflowExecution?.workflowId ?? intake.info.workflowId;
1115
+ if (!workflowId) {
1116
+ return undefined;
1117
+ }
1118
+ return {
1119
+ id: `cancel-external-${sequence}`,
1120
+ kind: 'request-cancel-external-workflow',
1121
+ sequence,
1122
+ namespace: attributes.namespace || intake.info.namespace,
1123
+ workflowId,
1124
+ runId: attributes.workflowExecution?.runId || undefined,
1125
+ childWorkflowOnly: attributes.childWorkflowOnly ?? false,
1126
+ reason: attributes.reason || undefined,
1127
+ };
1128
+ };
1129
+ const fromMarkerRecorded = (attributes, sequence, intake) => Effect.gen(function* () {
1130
+ const markerName = attributes.markerName || 'marker';
1131
+ const details = yield* decodeMarkerDetails(intake.dataConverter, attributes.details);
1132
+ return {
1133
+ id: `record-marker-${sequence}`,
1134
+ kind: 'record-marker',
1135
+ sequence,
1136
+ markerName,
1137
+ details,
1138
+ };
1139
+ });
1140
+ const fromUpsertSearchAttributes = (attributes, sequence, intake) => Effect.gen(function* () {
1141
+ const searchAttributes = yield* decodeSearchAttributes(intake.dataConverter, attributes.searchAttributes);
1142
+ if (!searchAttributes) {
1143
+ return undefined;
1144
+ }
1145
+ return {
1146
+ id: `upsert-search-attributes-${sequence}`,
1147
+ kind: 'upsert-search-attributes',
1148
+ sequence,
1149
+ searchAttributes,
1150
+ };
1151
+ });
1152
+ const fromWorkflowPropertiesModified = (attributes, sequence, intake) => Effect.gen(function* () {
1153
+ const memo = yield* decodeMemo(intake.dataConverter, attributes.upsertedMemo);
1154
+ return {
1155
+ id: `modify-workflow-properties-${sequence}`,
1156
+ kind: 'modify-workflow-properties',
1157
+ sequence,
1158
+ memo,
1159
+ };
1160
+ });
625
1161
  const collectWorkflowUpdateEntries = (events) => {
626
1162
  const updates = [];
627
1163
  for (const event of events) {
@@ -715,7 +1251,19 @@ const collectWorkflowUpdateEntries = (events) => {
715
1251
  return updates;
716
1252
  };
717
1253
  const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
718
- const invocations = [];
1254
+ const candidates = [];
1255
+ let order = 0;
1256
+ const parseSequencing = (value) => {
1257
+ if (!value) {
1258
+ return undefined;
1259
+ }
1260
+ try {
1261
+ return BigInt(value);
1262
+ }
1263
+ catch {
1264
+ return undefined;
1265
+ }
1266
+ };
719
1267
  for (const event of events) {
720
1268
  if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED) {
721
1269
  if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
@@ -731,25 +1279,8 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
731
1279
  dataConverter,
732
1280
  });
733
1281
  if (invocation) {
734
- invocations.push(invocation);
735
- }
736
- continue;
737
- }
738
- if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED) {
739
- if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
740
- continue;
741
- }
742
- const attrs = event.attributes.value;
743
- const invocation = await buildUpdateInvocationFromRequest({
744
- request: attrs.rejectedRequest,
745
- protocolInstanceId: attrs.protocolInstanceId,
746
- requestMessageId: attrs.rejectedRequestMessageId,
747
- sequencingEventId: attrs.rejectedRequestSequencingEventId,
748
- fallbackEventId: event.eventId,
749
- dataConverter,
750
- });
751
- if (invocation) {
752
- invocations.push(invocation);
1282
+ candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
1283
+ order += 1;
753
1284
  }
754
1285
  continue;
755
1286
  }
@@ -767,11 +1298,59 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
767
1298
  dataConverter,
768
1299
  });
769
1300
  if (invocation) {
770
- invocations.push(invocation);
1301
+ candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
1302
+ order += 1;
771
1303
  }
772
1304
  }
773
1305
  }
774
- return invocations;
1306
+ const byUpdateId = new Map();
1307
+ for (const candidate of candidates) {
1308
+ const updateId = candidate.invocation.updateId;
1309
+ const existing = byUpdateId.get(updateId);
1310
+ if (!existing) {
1311
+ byUpdateId.set(updateId, candidate);
1312
+ continue;
1313
+ }
1314
+ const existingSeq = existing.sequencing;
1315
+ const candidateSeq = candidate.sequencing;
1316
+ if (candidateSeq !== undefined && existingSeq !== undefined) {
1317
+ if (candidateSeq < existingSeq) {
1318
+ byUpdateId.set(updateId, candidate);
1319
+ }
1320
+ continue;
1321
+ }
1322
+ if (candidateSeq !== undefined && existingSeq === undefined) {
1323
+ byUpdateId.set(updateId, candidate);
1324
+ continue;
1325
+ }
1326
+ if (candidateSeq === undefined && existingSeq !== undefined) {
1327
+ continue;
1328
+ }
1329
+ if (candidate.order < existing.order) {
1330
+ byUpdateId.set(updateId, candidate);
1331
+ }
1332
+ }
1333
+ return [...byUpdateId.values()]
1334
+ .sort((left, right) => {
1335
+ const leftSeq = left.sequencing;
1336
+ const rightSeq = right.sequencing;
1337
+ if (leftSeq !== undefined && rightSeq !== undefined) {
1338
+ if (leftSeq < rightSeq) {
1339
+ return -1;
1340
+ }
1341
+ if (leftSeq > rightSeq) {
1342
+ return 1;
1343
+ }
1344
+ }
1345
+ else if (leftSeq !== undefined) {
1346
+ return -1;
1347
+ }
1348
+ else if (rightSeq !== undefined) {
1349
+ return 1;
1350
+ }
1351
+ return left.order - right.order;
1352
+ })
1353
+ .map((entry) => entry.invocation);
775
1354
  };
776
1355
  const buildUpdateInvocationFromRequest = async (input) => {
777
1356
  const req = input.request;
@@ -807,6 +1386,49 @@ const normalizeUpdateInvocationPayload = (values) => {
807
1386
  return values;
808
1387
  };
809
1388
  const decodePayloadArray = (converter, payloads) => Effect.tryPromise(async () => await decodePayloadsToValues(converter, payloads?.payloads ?? []));
1389
+ const decodeMarkerDetails = (converter, details) => Effect.tryPromise(async () => {
1390
+ if (!details || Object.keys(details).length === 0) {
1391
+ return undefined;
1392
+ }
1393
+ const decoded = {};
1394
+ for (const [key, payloads] of Object.entries(details)) {
1395
+ const values = await decodePayloadsToValues(converter, payloads?.payloads ?? []);
1396
+ if (values.length === 0) {
1397
+ continue;
1398
+ }
1399
+ decoded[key] = values.length === 1 ? values[0] : values;
1400
+ }
1401
+ return Object.keys(decoded).length > 0 ? decoded : undefined;
1402
+ });
1403
+ const decodeSearchAttributes = (converter, input) => Effect.tryPromise(async () => {
1404
+ const fields = input?.indexedFields;
1405
+ if (!fields || Object.keys(fields).length === 0) {
1406
+ return undefined;
1407
+ }
1408
+ const decoded = {};
1409
+ for (const [key, payload] of Object.entries(fields)) {
1410
+ const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
1411
+ if (values.length === 0) {
1412
+ continue;
1413
+ }
1414
+ decoded[key] = values.length === 1 ? values[0] : values;
1415
+ }
1416
+ return decoded;
1417
+ });
1418
+ const decodeMemo = (converter, memo) => Effect.tryPromise(async () => {
1419
+ const decoded = {};
1420
+ const fields = memo?.fields ?? {};
1421
+ for (const [key, payload] of Object.entries(fields)) {
1422
+ const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
1423
+ if (values.length === 0) {
1424
+ decoded[key] = undefined;
1425
+ }
1426
+ else {
1427
+ decoded[key] = values.length === 1 ? values[0] : values;
1428
+ }
1429
+ }
1430
+ return decoded;
1431
+ });
810
1432
  const convertRetryPolicy = (policy) => {
811
1433
  if (!policy) {
812
1434
  return undefined;
@@ -870,19 +1492,63 @@ const resolveOutcomeStatus = (outcome) => {
870
1492
  }
871
1493
  return undefined;
872
1494
  };
1495
+ const applyDeterminismDelta = (state, delta) => ({
1496
+ ...state,
1497
+ commandHistory: [...state.commandHistory, ...(delta.commandHistory ?? [])],
1498
+ randomValues: [...state.randomValues, ...(delta.randomValues ?? [])],
1499
+ timeValues: [...state.timeValues, ...(delta.timeValues ?? [])],
1500
+ signals: [...state.signals, ...(delta.signals ?? [])],
1501
+ queries: [...state.queries, ...(delta.queries ?? [])],
1502
+ ...(delta.updates
1503
+ ? { updates: [...(state.updates ?? []), ...(delta.updates ?? [])] }
1504
+ : state.updates
1505
+ ? { updates: [...state.updates] }
1506
+ : {}),
1507
+ ...(delta.logCount !== undefined ? { logCount: delta.logCount } : {}),
1508
+ ...(delta.failureMetadata ? { failureMetadata: delta.failureMetadata } : {}),
1509
+ });
1510
+ const applyDeterminismMarker = (state, marker) => {
1511
+ if (marker.schemaVersion === 1) {
1512
+ return marker.determinismState;
1513
+ }
1514
+ const base = marker.determinismState ?? state;
1515
+ if (!base) {
1516
+ return undefined;
1517
+ }
1518
+ if (!marker.determinismDelta) {
1519
+ return base;
1520
+ }
1521
+ return applyDeterminismDelta(base, marker.determinismDelta);
1522
+ };
873
1523
  const sanitizeDeterminismMarkerEnvelope = (input) => {
874
1524
  const schemaVersion = input.schemaVersion;
875
- if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION) {
1525
+ if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V1 &&
1526
+ schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V2) {
876
1527
  throw new Error(`Unsupported determinism marker schema version: ${String(schemaVersion)}`);
877
1528
  }
878
1529
  const workflow = sanitizeWorkflowInfo(input.workflow);
879
- const determinismState = sanitizeDeterminismState(input.determinismState);
880
1530
  const lastEventId = normalizeEventId(input.lastEventId);
881
1531
  const recordedAt = sanitizeRecordedAt(input.recordedAtIso);
1532
+ if (schemaVersion === DETERMINISM_MARKER_SCHEMA_VERSION_V1) {
1533
+ const determinismState = sanitizeDeterminismState(input.determinismState);
1534
+ return {
1535
+ schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
1536
+ workflow,
1537
+ determinismState,
1538
+ lastEventId,
1539
+ recordedAtIso: recordedAt,
1540
+ };
1541
+ }
1542
+ const determinismState = input.determinismState !== undefined ? sanitizeDeterminismState(input.determinismState) : undefined;
1543
+ const determinismDelta = input.determinismDelta !== undefined ? sanitizeDeterminismDelta(input.determinismDelta) : undefined;
1544
+ if (!determinismState && !determinismDelta) {
1545
+ throw new Error('Determinism marker missing determinism state or delta');
1546
+ }
882
1547
  return {
883
- schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION,
1548
+ schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
884
1549
  workflow,
885
- determinismState,
1550
+ ...(determinismState ? { determinismState } : {}),
1551
+ ...(determinismDelta ? { determinismDelta } : {}),
886
1552
  lastEventId,
887
1553
  recordedAtIso: recordedAt,
888
1554
  };
@@ -906,6 +1572,7 @@ const sanitizeDeterminismState = (value) => {
906
1572
  const commandHistoryRaw = value.commandHistory;
907
1573
  const randomValuesRaw = value.randomValues;
908
1574
  const timeValuesRaw = value.timeValues;
1575
+ const logCountRaw = value.logCount;
909
1576
  const signalsRaw = Array.isArray(value.signals) ? value.signals : [];
910
1577
  const queriesRaw = Array.isArray(value.queries) ? value.queries : [];
911
1578
  const updatesRaw = value.updates;
@@ -924,6 +1591,7 @@ const sanitizeDeterminismState = (value) => {
924
1591
  });
925
1592
  const randomValues = randomValuesRaw.map((val, index) => coerceNumber(val, `determinism.randomValues[${index}]`));
926
1593
  const timeValues = timeValuesRaw.map((val, index) => coerceNumber(val, `determinism.timeValues[${index}]`));
1594
+ const logCount = coerceOptionalNumber(logCountRaw, 'determinism.logCount');
927
1595
  const failureMetadata = sanitizeFailureMetadata(value.failureMetadata);
928
1596
  const signals = signalsRaw.map((record, index) => sanitizeSignalRecord(record, index));
929
1597
  const queries = queriesRaw.map((record, index) => sanitizeQueryRecord(record, index));
@@ -932,12 +1600,40 @@ const sanitizeDeterminismState = (value) => {
932
1600
  commandHistory,
933
1601
  randomValues,
934
1602
  timeValues,
1603
+ ...(logCount !== undefined ? { logCount } : {}),
935
1604
  ...(failureMetadata ? { failureMetadata } : {}),
936
1605
  signals,
937
1606
  queries,
938
1607
  ...(updates ? { updates } : {}),
939
1608
  };
940
1609
  };
1610
+ const sanitizeDeterminismDelta = (value) => {
1611
+ if (!isRecord(value)) {
1612
+ throw new Error('Determinism marker contained invalid determinism delta');
1613
+ }
1614
+ const commandHistoryRaw = value.commandHistory ?? [];
1615
+ const randomValuesRaw = value.randomValues ?? [];
1616
+ const timeValuesRaw = value.timeValues ?? [];
1617
+ const signalsRaw = value.signals ?? [];
1618
+ const queriesRaw = value.queries ?? [];
1619
+ if (!Array.isArray(commandHistoryRaw) ||
1620
+ !Array.isArray(randomValuesRaw) ||
1621
+ !Array.isArray(timeValuesRaw) ||
1622
+ !Array.isArray(signalsRaw) ||
1623
+ !Array.isArray(queriesRaw)) {
1624
+ throw new Error('Determinism marker contained invalid determinism delta shape');
1625
+ }
1626
+ return sanitizeDeterminismState({
1627
+ commandHistory: commandHistoryRaw,
1628
+ randomValues: randomValuesRaw,
1629
+ timeValues: timeValuesRaw,
1630
+ signals: signalsRaw,
1631
+ queries: queriesRaw,
1632
+ ...(value.updates !== undefined ? { updates: value.updates } : {}),
1633
+ ...(value.logCount !== undefined ? { logCount: value.logCount } : {}),
1634
+ ...(value.failureMetadata !== undefined ? { failureMetadata: value.failureMetadata } : {}),
1635
+ });
1636
+ };
941
1637
  const sanitizeCommandMetadata = (value, index) => {
942
1638
  if (value === undefined || value === null) {
943
1639
  return undefined;