@lostgradient/weft 0.11.0 → 0.13.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 (242) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/api.d.ts +2 -0
  3. package/dist/cli/api.js +3 -4
  4. package/dist/cli/generated/operation-catalog.snapshot.json +5167 -0
  5. package/dist/cli/generated/operation-client.generated.d.ts +119 -10
  6. package/dist/cli/generated/operation-client.generated.js +74 -1
  7. package/dist/cli/index.js +1 -0
  8. package/dist/cli/operation-catalog-snapshot.d.ts +11 -2
  9. package/dist/cli/operation-catalog-snapshot.js +14 -5
  10. package/dist/cli/operation-client-runtime.d.ts +29 -0
  11. package/dist/cli-main.js +227 -401
  12. package/dist/client/client-storage.d.ts +24 -0
  13. package/dist/client/client-storage.js +14 -0
  14. package/dist/client/handle-delegation.d.ts +3 -3
  15. package/dist/client/handle-delegation.js +2 -2
  16. package/dist/client/http-client-requests.d.ts +5 -3
  17. package/dist/client/http-client-requests.js +20 -2
  18. package/dist/client/http-client-storage.d.ts +3 -0
  19. package/dist/client/http-client-storage.js +110 -0
  20. package/dist/client/http-client.d.ts +9 -6
  21. package/dist/client/http-client.js +13 -18
  22. package/dist/client/http-operations.d.ts +3 -1
  23. package/dist/client/http-operations.js +84 -2
  24. package/dist/client/http-request.d.ts +35 -6
  25. package/dist/client/http-request.js +31 -10
  26. package/dist/client/in-process-operations.d.ts +2 -2
  27. package/dist/client/in-process-operations.js +2 -2
  28. package/dist/client/index.d.ts +6 -0
  29. package/dist/client/index.js +18 -0
  30. package/dist/client/interface.d.ts +26 -26
  31. package/dist/client/local.d.ts +9 -6
  32. package/dist/client/local.js +9 -4
  33. package/dist/client/search-params.js +10 -6
  34. package/dist/client/start-body.d.ts +3 -1
  35. package/dist/client/start-body.js +9 -0
  36. package/dist/connection.d.ts +8 -0
  37. package/dist/connection.js +41 -17
  38. package/dist/core/bulk-workflow-filter.d.ts +2 -1
  39. package/dist/core/bulk-workflow-filter.js +5 -1
  40. package/dist/core/engine/async-activity-records.d.ts +12 -1
  41. package/dist/core/engine/async-activity-records.js +81 -2
  42. package/dist/core/engine/bulk-operations-purge.js +16 -1
  43. package/dist/core/engine/bulk-operations-shared.js +2 -0
  44. package/dist/core/engine/callback-creators-core.js +2 -1
  45. package/dist/core/engine/callback-creators-schedule.d.ts +2 -1
  46. package/dist/core/engine/callback-creators-schedule.js +3 -3
  47. package/dist/core/engine/checkpoint-reads.js +11 -1
  48. package/dist/core/engine/child-workflow.js +15 -6
  49. package/dist/core/engine/construction.js +3 -0
  50. package/dist/core/engine/disposal.js +2 -0
  51. package/dist/core/engine/engine-internal-types.d.ts +4 -1
  52. package/dist/core/engine/fenced-write.d.ts +5 -3
  53. package/dist/core/engine/finalizer-status.d.ts +10 -0
  54. package/dist/core/engine/finalizer-status.js +82 -0
  55. package/dist/core/engine/index.d.ts +69 -2
  56. package/dist/core/engine/index.js +71 -8
  57. package/dist/core/engine/internals.d.ts +3 -0
  58. package/dist/core/engine/lease-health.d.ts +64 -0
  59. package/dist/core/engine/lease-health.js +0 -0
  60. package/dist/core/engine/lease-manager.d.ts +4 -2
  61. package/dist/core/engine/lease-manager.js +32 -4
  62. package/dist/core/engine/lifecycle/recovered-services.js +0 -2
  63. package/dist/core/engine/lifecycle/shared.d.ts +28 -0
  64. package/dist/core/engine/lifecycle/start-batch.js +12 -0
  65. package/dist/core/engine/lifecycle/start.d.ts +1 -1
  66. package/dist/core/engine/lifecycle/start.js +34 -5
  67. package/dist/core/engine/lifecycle/transition.js +19 -9
  68. package/dist/core/engine/operations-coordination.d.ts +1 -12
  69. package/dist/core/engine/operations-coordination.js +57 -10
  70. package/dist/core/engine/operations-speculate.d.ts +1 -1
  71. package/dist/core/engine/operations-speculate.js +19 -5
  72. package/dist/core/engine/registration.js +14 -0
  73. package/dist/core/engine/schedule-handle.d.ts +4 -4
  74. package/dist/core/engine/schedule-handle.js +2 -2
  75. package/dist/core/engine/schedule-overlap.d.ts +11 -0
  76. package/dist/core/engine/schedule-overlap.js +50 -0
  77. package/dist/core/engine/schedule-run.d.ts +11 -3
  78. package/dist/core/engine/schedule-run.js +27 -4
  79. package/dist/core/engine/schedule-timer.js +10 -1
  80. package/dist/core/engine/schedules.d.ts +5 -4
  81. package/dist/core/engine/schedules.js +62 -56
  82. package/dist/core/engine/state-utilities.js +1 -1
  83. package/dist/core/engine/storage-io.d.ts +12 -0
  84. package/dist/core/engine/storage-io.js +49 -10
  85. package/dist/core/engine/termination/finalizer-claim.d.ts +8 -6
  86. package/dist/core/engine/termination/finalizer-claim.js +2 -1
  87. package/dist/core/engine/termination/finalizer.js +10 -14
  88. package/dist/core/engine/timeline-coordinator-constants.d.ts +3 -0
  89. package/dist/core/engine/timeline-coordinator-constants.js +1 -0
  90. package/dist/core/engine/timeline-coordinator-detail.d.ts +13 -0
  91. package/dist/core/engine/timeline-coordinator-detail.js +76 -0
  92. package/dist/core/engine/validation/schedule-options.d.ts +8 -1
  93. package/dist/core/engine/validation/schedule-options.js +18 -0
  94. package/dist/core/engine/validation/schedule.d.ts +1 -1
  95. package/dist/core/engine/validation/schedule.js +27 -3
  96. package/dist/core/engine/validation.js +66 -2
  97. package/dist/core/engine/workflow-state-stream.d.ts +5 -1
  98. package/dist/core/engine/workflow-state-stream.js +37 -14
  99. package/dist/core/events/schedule-events.d.ts +3 -4
  100. package/dist/core/events/workflow-events.d.ts +2 -1
  101. package/dist/core/list-filter-validation.d.ts +3 -0
  102. package/dist/core/list-filter-validation.js +11 -0
  103. package/dist/core/registry-snapshot.d.ts +32 -0
  104. package/dist/core/registry-snapshot.js +28 -0
  105. package/dist/core/review/index.d.ts +4 -4
  106. package/dist/core/types/activity.d.ts +52 -5
  107. package/dist/core/types/bulk.d.ts +1 -0
  108. package/dist/core/types/definition-schema-to-json.d.ts +11 -0
  109. package/dist/core/types/definition-schema-to-json.js +11 -2
  110. package/dist/core/types/list-options.d.ts +6 -0
  111. package/dist/core/types/options.d.ts +6 -5
  112. package/dist/core/types/reviews.d.ts +2 -2
  113. package/dist/core/types/schedules.d.ts +45 -2
  114. package/dist/core/types/services-resolution.d.ts +5 -5
  115. package/dist/core/types/state.d.ts +64 -0
  116. package/dist/core/types/workflow-log.d.ts +3 -2
  117. package/dist/core/types/workflow-observability.d.ts +50 -0
  118. package/dist/core/types/workflow-observability.js +0 -0
  119. package/dist/core/types/workflow-registry.d.ts +7 -0
  120. package/dist/core/types.d.ts +1 -0
  121. package/dist/core/types.js +1 -0
  122. package/dist/http.js +2 -0
  123. package/dist/index.d.ts +4 -3
  124. package/dist/indexeddb.js +1 -0
  125. package/dist/json-schema.js +3 -3
  126. package/dist/mcp/cli.js +63 -61
  127. package/dist/mcp/http.d.ts +7 -2
  128. package/dist/mcp/session.d.ts +3 -3
  129. package/dist/mcp/session.js +3 -2
  130. package/dist/mcp/stdio.d.ts +7 -2
  131. package/dist/observability/index.js +2 -2
  132. package/dist/runtime/portable.d.ts +33 -0
  133. package/dist/runtime/portable.js +17 -6
  134. package/dist/server/access-policy-metadata.d.ts +92 -0
  135. package/dist/server/access-policy-metadata.js +78 -0
  136. package/dist/server/engine-event-feed-backend.d.ts +22 -3
  137. package/dist/server/fault-to-http.d.ts +3 -7
  138. package/dist/server/fault-to-http.js +2 -34
  139. package/dist/server/fleet-event-feed.d.ts +65 -0
  140. package/dist/server/handler/index.d.ts +11 -2
  141. package/dist/server/handler/index.js +8 -1
  142. package/dist/server/handler/route-dispatch.d.ts +10 -3
  143. package/dist/server/handler/route-dispatch.js +19 -4
  144. package/dist/server/handler.js +1 -56
  145. package/dist/server/index.d.ts +13 -2
  146. package/dist/server/index.js +88 -64
  147. package/dist/server/interactive-operations.js +2 -0
  148. package/dist/server/openapi-error-responses.js +55 -4
  149. package/dist/server/openapi.d.ts +3 -1
  150. package/dist/server/openapi.js +4 -0
  151. package/dist/server/openrpc-document-schema.d.ts +64 -0
  152. package/dist/server/openrpc-document-schema.js +6 -26
  153. package/dist/server/openrpc.d.ts +4 -0
  154. package/dist/server/openrpc.js +9 -33
  155. package/dist/server/operation-fault.d.ts +24 -7
  156. package/dist/server/operation-fault.js +65 -13
  157. package/dist/server/operations/aggregate-workflows.d.ts +6 -0
  158. package/dist/server/operations/aggregate-workflows.js +3 -2
  159. package/dist/server/operations/async-activity.d.ts +14 -0
  160. package/dist/server/operations/async-activity.js +88 -4
  161. package/dist/server/operations/bulk-filter-helpers.d.ts +3 -0
  162. package/dist/server/operations/bulk-filter-helpers.js +27 -3
  163. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +6 -0
  164. package/dist/server/operations/bulk-signal-workflows.d.ts +6 -0
  165. package/dist/server/operations/get-stream-chunks.js +2 -7
  166. package/dist/server/operations/get-system-lease.d.ts +19 -0
  167. package/dist/server/operations/get-system-lease.js +48 -0
  168. package/dist/server/operations/get-workflow-observability.d.ts +24 -0
  169. package/dist/server/operations/get-workflow-observability.js +80 -0
  170. package/dist/server/operations/get-workflow-result.js +2 -2
  171. package/dist/server/operations/get-workflow.d.ts +2 -2
  172. package/dist/server/operations/list-filter-query-extractor.js +15 -0
  173. package/dist/server/operations/list-workflows.d.ts +6 -0
  174. package/dist/server/operations/list-workflows.js +12 -3
  175. package/dist/server/operations/operation-helpers.d.ts +9 -18
  176. package/dist/server/operations/operation-helpers.js +5 -23
  177. package/dist/server/operations/purge-workflows.d.ts +3 -0
  178. package/dist/server/operations/recover-all.js +2 -1
  179. package/dist/server/operations/single-workflow-tag-mutation.js +2 -2
  180. package/dist/server/operations/sse-stream.js +2 -7
  181. package/dist/server/operations/storage-capabilities.d.ts +34 -0
  182. package/dist/server/operations/storage-capabilities.js +38 -0
  183. package/dist/server/operations/storage.js +8 -17
  184. package/dist/server/operations/stream-workflow-sse.js +3 -9
  185. package/dist/server/operations/submit-review-decision.d.ts +2 -0
  186. package/dist/server/operations/submit-review-decision.js +19 -2
  187. package/dist/server/operations/update-schedule.d.ts +8 -0
  188. package/dist/server/operations/update-schedule.js +70 -7
  189. package/dist/server/operations/workflow-events-sse.js +2 -2
  190. package/dist/server/replay-live-feed-internals.d.ts +19 -0
  191. package/dist/server/replay-live-feed-internals.js +76 -0
  192. package/dist/server/rest-binding.d.ts +7 -6
  193. package/dist/server/rest-bindings.d.ts +2 -13
  194. package/dist/server/rest-bindings.js +19 -10
  195. package/dist/server/runtime/authentication-bridge.d.ts +2 -2
  196. package/dist/server/serve-internals.js +3 -3
  197. package/dist/server/task-queue.d.ts +7 -5
  198. package/dist/server/workflow-event-feed.d.ts +100 -11
  199. package/dist/server/workflow-event-feed.js +1 -140
  200. package/dist/service-worker/index.d.ts +8 -2
  201. package/dist/service-worker/index.js +37 -56
  202. package/dist/service-worker/setup.d.ts +6 -2
  203. package/dist/storage/auto.js +1 -1
  204. package/dist/storage/bun-sql.js +3 -3
  205. package/dist/storage/capabilities.d.ts +1 -1
  206. package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
  207. package/dist/storage/cloudflare-durable-object-sql.js +0 -0
  208. package/dist/storage/cloudflare-value-codec.d.ts +67 -0
  209. package/dist/storage/cloudflare-value-codec.js +46 -0
  210. package/dist/storage/cloudflare.d.ts +140 -0
  211. package/dist/storage/cloudflare.js +127 -0
  212. package/dist/storage/compressed-storage.js +1 -1
  213. package/dist/storage/http.js +200 -2
  214. package/dist/storage/index.d.ts +8 -1
  215. package/dist/storage/indexeddb.js +311 -1
  216. package/dist/storage/interface.d.ts +16 -4
  217. package/dist/storage/interface.js +1 -1
  218. package/dist/storage/key-prefixes.d.ts +1 -1
  219. package/dist/storage/key-prefixes.js +4 -0
  220. package/dist/storage/lmdb.js +1 -1
  221. package/dist/storage/memory.js +1 -1
  222. package/dist/storage/neon.js +4 -4
  223. package/dist/storage/node-sqlite.js +3 -3
  224. package/dist/storage/postgres-key-value-queries.d.ts +6 -0
  225. package/dist/storage/postgres-key-value-queries.js +2 -3
  226. package/dist/storage/postgres.js +4 -4
  227. package/dist/storage/resolve.js +1 -1
  228. package/dist/storage/scoped-storage.js +1 -1
  229. package/dist/storage/sql-identifier.d.ts +35 -0
  230. package/dist/storage/sql-identifier.js +5 -0
  231. package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
  232. package/dist/storage/sqlite-key-value-queries.js +3 -3
  233. package/dist/storage/testing.js +1 -1
  234. package/dist/storage/turso.js +2 -2
  235. package/dist/storage/typed-storage.js +138 -2
  236. package/dist/storage/web-extension.js +298 -1
  237. package/dist/testing/index.js +21 -60
  238. package/dist/version.d.ts +1 -1
  239. package/dist/version.js +1 -1
  240. package/dist/web-extension.js +1 -0
  241. package/dist/worker/protocol.js +1 -1
  242. package/package.json +8 -3
@@ -15,6 +15,7 @@ import {
15
15
  matchesListFilter
16
16
  } from "./state-utilities.js";
17
17
  import { decodeWorkflowState } from "./validation.js";
18
+ import { MAX_LIST_SCAN_ROWS, WorkflowListScanCapExceededError } from "./workflow-indexes.js";
18
19
  const ATTRIBUTE_SCAN_CONCURRENCY = 8;
19
20
  export async function* streamMatchingWorkflowStates(internals, filter) {
20
21
  const normalizedTagFilters = normalizeWorkflowTags(filter?.tags), constrainedIds = await resolveConstrainedIds(internals, filter, normalizedTagFilters);
@@ -54,10 +55,9 @@ async function readSearchAttributesForFilter(internals, workflowId, filter) {
54
55
  return decodeSearchAttributeRecord(await internals.storage.get(KEYS.attribute(workflowId)));
55
56
  }
56
57
  export async function resolveConstrainedIds(internals, filter, normalizedTagFilters) {
57
- const attributeFilters = filter?.attributes, hasAttributeFilters = attributeFilters !== void 0 && attributeFilters.length > 0, hasTagFilters = normalizedTagFilters !== void 0 && normalizedTagFilters.length > 0;
58
- if (!hasAttributeFilters && !hasTagFilters)
58
+ const attributeFilters = filter?.attributes, queries = buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters, filter?.scheduleId, filter?.parentWorkflowId, filter?.parentWorkflowExecutionToken);
59
+ if (queries.length === 0)
59
60
  return null;
60
- const queries = buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters);
61
61
  return intersectIdentifierSets(await runConstrainedIdQueries(queries));
62
62
  }
63
63
  export async function queryAttributeIndex(internals, filter) {
@@ -68,29 +68,52 @@ export async function queryAttributeIndex(internals, filter) {
68
68
  await collectRangeAttributeMatches(internals, filter, prefix, ids);
69
69
  return ids;
70
70
  }
71
- function buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters) {
71
+ function buildConstrainedIdQueries(internals, normalizedTagFilters, attributeFilters, scheduleId, parentWorkflowId, parentWorkflowExecutionToken) {
72
72
  return [
73
+ ...scheduleId === void 0 ? [] : [() => queryScheduleRunIndex(internals, scheduleId)],
74
+ ...parentWorkflowId === void 0 ? [] : [() => queryChildWorkflowIndex(internals, parentWorkflowId, parentWorkflowExecutionToken)],
73
75
  ...normalizedTagFilters?.map((tag) => () => queryTagIndex(internals, tag)) ?? [],
74
76
  ...attributeFilters?.map((attributeFilter) => () => queryAttributeIndex(internals, attributeFilter)) ?? []
75
77
  ];
76
78
  }
79
+ export async function queryChildWorkflowIndex(internals, parentWorkflowId, parentWorkflowExecutionToken) {
80
+ const workflowIds = new Set, prefix = KEYS.childWorkflowByParentPrefix(parentWorkflowId, parentWorkflowExecutionToken);
81
+ for await (const [key] of internals.storage.scan(prefix)) {
82
+ if (workflowIds.size >= MAX_LIST_SCAN_ROWS)
83
+ throw new WorkflowListScanCapExceededError(MAX_LIST_SCAN_ROWS);
84
+ const encodedWorkflowId = key.slice(prefix.length).split(":").at(-1);
85
+ if (encodedWorkflowId === void 0)
86
+ continue;
87
+ const workflowId = tryDecodeStorageKeyComponent(encodedWorkflowId);
88
+ if (workflowId !== null)
89
+ workflowIds.add(workflowId);
90
+ }
91
+ return workflowIds;
92
+ }
93
+ export async function queryScheduleRunIndex(internals, scheduleId) {
94
+ const workflowIds = new Set, prefix = KEYS.scheduleRunBySchedulePrefix(scheduleId);
95
+ for await (const [key] of internals.storage.scan(prefix)) {
96
+ if (workflowIds.size >= MAX_LIST_SCAN_ROWS)
97
+ throw new WorkflowListScanCapExceededError(MAX_LIST_SCAN_ROWS);
98
+ const encodedWorkflowId = key.slice(prefix.length), workflowId = tryDecodeStorageKeyComponent(encodedWorkflowId);
99
+ if (workflowId !== null)
100
+ workflowIds.add(workflowId);
101
+ }
102
+ return workflowIds;
103
+ }
77
104
  async function runConstrainedIdQueries(queries) {
78
- const idSets = Array.from({ length: queries.length });
79
105
  let nextIndex = 0;
80
106
  const runWorker = async () => {
107
+ const workerResults = [];
81
108
  while (nextIndex < queries.length) {
82
109
  const currentIndex = nextIndex;
83
110
  nextIndex += 1;
84
- idSets[currentIndex] = await queries[currentIndex]();
111
+ workerResults.push([currentIndex, await queries[currentIndex]()]);
85
112
  }
86
- }, workerLimit = Math.max(1, Math.min(ATTRIBUTE_SCAN_CONCURRENCY, queries.length));
87
- await Promise.all(Array.from({ length: workerLimit }, () => runWorker()));
88
- return idSets.map(requireConstrainedIdSet);
89
- }
90
- function requireConstrainedIdSet(idSet) {
91
- if (idSet === void 0)
92
- throw Error("Attribute index query did not produce a workflow ID set.");
93
- return idSet;
113
+ return workerResults;
114
+ }, workerLimit = Math.max(1, Math.min(ATTRIBUTE_SCAN_CONCURRENCY, queries.length)), completedResults = (await Promise.all(Array.from({ length: workerLimit }, () => runWorker()))).flat();
115
+ completedResults.sort(([leftIndex], [rightIndex]) => leftIndex - rightIndex);
116
+ return completedResults.map(([, idSet]) => idSet);
94
117
  }
95
118
  async function collectExactAttributeMatches(internals, filter, attributeName, ids) {
96
119
  const exactValues = attributeFilterExactValues(filter);
@@ -38,10 +38,9 @@ export declare class ScheduleFiredEvent extends Event {
38
38
  /** Wall-clock time the run was launched, from the engine's injected clock. */
39
39
  readonly firedAt: number;
40
40
  /**
41
- * The scheduled grid timestamp the occurrence was due. `undefined` for a run
42
- * that drained from the `queue` overlap policy: a queued occurrence is tracked
43
- * only as a count (`queuedRuns`), so its original due timestamp is not retained
44
- * and cannot be reported when the run finally launches.
41
+ * The scheduled grid timestamp the occurrence was due. Queue overlap retains
42
+ * this timestamp on the durable queue entry and reports it when the run drains.
43
+ * It is undefined only when an internal caller did not supply an occurrence.
45
44
  */
46
45
  readonly occurrence: number | undefined;
47
46
  constructor(scheduleId: string, workflowId: string, firedAt: number, occurrence?: number);
@@ -241,7 +241,8 @@ export declare class WorkflowRecoverySkippedEvent extends Event {
241
241
  * dead-letter horizon is reached. Per-attempt observability — fires on every retry.
242
242
  * - `'dead-lettered'`: the retry horizon was reached without success; the external
243
243
  * resource may be leaked. The durable record is `KEYS.teardownDeadLetter`, which
244
- * survives purge so the leak is auditable after the workflow record is gone.
244
+ * survives purge so `getFinalizerStatus()` remains auditable after the workflow
245
+ * record is gone.
245
246
  *
246
247
  * @example
247
248
  * ```ts
@@ -20,6 +20,9 @@ import { WeftError } from './weft-error.ts';
20
20
  export declare const listFilterObjectSchema: z.ZodObject<{
21
21
  status: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<WorkflowStatus, unknown, z.core.$ZodTypeInternals<WorkflowStatus, unknown>>, z.ZodArray<z.ZodType<WorkflowStatus, unknown, z.core.$ZodTypeInternals<WorkflowStatus, unknown>>>]>>;
22
22
  type: z.ZodOptional<z.ZodString>;
23
+ scheduleId: z.ZodOptional<z.ZodString>;
24
+ parentWorkflowId: z.ZodOptional<z.ZodString>;
25
+ parentWorkflowExecutionToken: z.ZodOptional<z.ZodString>;
23
26
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
27
  attributes: z.ZodOptional<z.ZodArray<z.ZodObject<{
25
28
  key: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
@@ -35,6 +35,9 @@ const WORKFLOW_STATUSES = [
35
35
  export const listFilterObjectSchema = z.object({
36
36
  status: z.union([workflowStatusSchema, z.array(workflowStatusSchema)]).optional(),
37
37
  type: z.string().min(1).optional(),
38
+ scheduleId: z.string().min(1).optional(),
39
+ parentWorkflowId: z.string().min(1).optional(),
40
+ parentWorkflowExecutionToken: z.string().min(1).optional(),
38
41
  tags: z.array(z.string()).optional(),
39
42
  attributes: z.array(attributeFilterSchema).optional(),
40
43
  limit: z.number().int().min(1).max(1000).optional(),
@@ -70,6 +73,14 @@ export function normalizeListFilter(input) {
70
73
  const result = listFilterObjectSchema.safeParse(input ?? {});
71
74
  if (!result.success)
72
75
  throw new ListFilterValidationError(result.error.issues.map(flattenIssue));
76
+ if (result.data.parentWorkflowExecutionToken !== void 0 && result.data.parentWorkflowId === void 0)
77
+ throw new ListFilterValidationError([
78
+ {
79
+ path: ["parentWorkflowExecutionToken"],
80
+ message: "parentWorkflowExecutionToken requires parentWorkflowId",
81
+ code: "custom"
82
+ }
83
+ ]);
73
84
  return result.data;
74
85
  }
75
86
 
@@ -1,3 +1,25 @@
1
+ /**
2
+ * Pure builder that captures a snapshot of the engine's locally-registered
3
+ * workflows and activities, with their JSON Schemas. This is the data source
4
+ * behind the `GET /v1/registry` REST endpoint and (later) the MCP server.
5
+ *
6
+ * The output is a plain object designed to be safe for JSON serialization.
7
+ * Absent metadata fields are omitted (never `null`, never `{}`); converter
8
+ * exceptions surface as a typed {@link RegistrySchemaConversionError} (see
9
+ * its JSDoc for what reaches the wire vs server logs).
10
+ *
11
+ * **Ordering guarantee.** Builder code inserts workflow, activity, signal,
12
+ * update, and query keys in alphabetical (codepoint) order. Workflow and
13
+ * activity names cannot be integer-like (the name grammar requires a leading
14
+ * letter or underscore), but signal/update/query names accept any string, so
15
+ * integer-like message names could theoretically be reordered by JS engines
16
+ * despite the explicit sort. Clients that want to protect themselves from
17
+ * future registry sources should still sort `Object.keys(...)` before
18
+ * presenting or diffing snapshot entries.
19
+ *
20
+ * @module core/registry-snapshot
21
+ */
22
+ import type { ActivityMetadata } from './activity-registry.ts';
1
23
  import type { Engine } from './engine.ts';
2
24
  import { WeftError } from './weft-error.ts';
3
25
  /**
@@ -6,12 +28,20 @@ import { WeftError } from './weft-error.ts';
6
28
  * versions with a clear upgrade message.
7
29
  */
8
30
  export declare const REGISTRY_VERSION = 1;
31
+ /** Schema metadata reported for a statically registered workflow message. */
32
+ export type RegistryMessageEntry = {
33
+ inputSchema?: Record<string, unknown>;
34
+ outputSchema?: Record<string, unknown>;
35
+ };
9
36
  /** Metadata reported per workflow in a registry snapshot. */
10
37
  export type RegistryWorkflowEntry = {
11
38
  inputSchema?: Record<string, unknown>;
12
39
  outputSchema?: Record<string, unknown>;
13
40
  description?: string;
14
41
  tags?: ReadonlyArray<string>;
42
+ signals?: Record<string, RegistryMessageEntry>;
43
+ updates?: Record<string, RegistryMessageEntry>;
44
+ queries?: Record<string, RegistryMessageEntry>;
15
45
  };
16
46
  /**
17
47
  * Metadata reported per activity in a registry snapshot.
@@ -31,6 +61,8 @@ export type RegistryActivityEntry = {
31
61
  /** Engine assigns a default queue when none is specified, so this is always populated. */
32
62
  queue: string;
33
63
  description?: string;
64
+ retry?: ActivityMetadata['retry'];
65
+ timeout?: ActivityMetadata['timeout'];
34
66
  };
35
67
  /**
36
68
  * Snapshot of every locally-registered workflow and activity, suitable for
@@ -49,8 +49,32 @@ function buildWorkflowEntry(definition) {
49
49
  entry.description = definition.description;
50
50
  if (definition.tags.length > 0)
51
51
  entry.tags = [...definition.tags];
52
+ addWorkflowMessageEntries(entry, definition);
52
53
  return entry;
53
54
  }
55
+ function addWorkflowMessageEntries(entry, definition) {
56
+ if (definition.signals !== void 0 && Object.keys(definition.signals).length > 0)
57
+ entry.signals = buildMessageEntries(definition.type, "signal", definition.signals);
58
+ if (definition.updates !== void 0 && Object.keys(definition.updates).length > 0)
59
+ entry.updates = buildMessageEntries(definition.type, "update", definition.updates);
60
+ if (definition.queries !== void 0 && Object.keys(definition.queries).length > 0)
61
+ entry.queries = buildMessageEntries(definition.type, "query", definition.queries);
62
+ }
63
+ function buildMessageEntries(workflowType, messageKind, definitions) {
64
+ const entries = Object.create(null);
65
+ for (const name of Object.keys(definitions).toSorted()) {
66
+ const definition = definitions[name];
67
+ if (definition === void 0)
68
+ continue;
69
+ const entry = {}, entityName = `${workflowType}.${messageKind}.${name}`;
70
+ if (definition.inputSchema !== void 0)
71
+ entry.inputSchema = convertSchema("workflow", entityName, "inputSchema", definition.inputSchema);
72
+ if (definition.outputSchema !== void 0)
73
+ entry.outputSchema = convertSchema("workflow", entityName, "outputSchema", definition.outputSchema);
74
+ entries[name] = entry;
75
+ }
76
+ return entries;
77
+ }
54
78
  function buildActivityEntry(metadata) {
55
79
  const entry = { queue: metadata.queue };
56
80
  if (metadata.inputSchema !== void 0)
@@ -59,6 +83,10 @@ function buildActivityEntry(metadata) {
59
83
  entry.outputSchema = convertSchema("activity", metadata.name, "outputSchema", metadata.outputSchema);
60
84
  if (metadata.description !== void 0)
61
85
  entry.description = metadata.description;
86
+ if (metadata.retry !== void 0)
87
+ entry.retry = metadata.retry;
88
+ if (metadata.timeout !== void 0)
89
+ entry.timeout = metadata.timeout;
62
90
  return entry;
63
91
  }
64
92
  function convertSchema(entityKind, entityName, field, schema) {
@@ -43,7 +43,7 @@ export interface ReviewRequest {
43
43
  * The `decision` field is the binary outcome; `sectionDecisions` carries
44
44
  * per-section verdicts when `allowPartial` is enabled on the request.
45
45
  */
46
- export interface ReviewDecision {
46
+ export interface ReviewDecisionRecord {
47
47
  reviewId: string;
48
48
  decision: 'approved' | 'rejected' | 'needs-changes';
49
49
  reviewer: string;
@@ -119,7 +119,7 @@ export interface HumanReviewOptions extends ReviewOptions {
119
119
  }
120
120
  /**
121
121
  * The decision payload returned to the workflow from `ctx.review()`.
122
- * Alias for {@link ReviewDecision}.
122
+ * Alias for {@link ReviewDecisionRecord}.
123
123
  *
124
124
  * @example
125
125
  * ```ts
@@ -134,7 +134,7 @@ export interface HumanReviewOptions extends ReviewOptions {
134
134
  * void result;
135
135
  * ```
136
136
  */
137
- export type HumanReviewResult = ReviewDecision;
137
+ export type HumanReviewResult = ReviewDecisionRecord;
138
138
  /**
139
139
  * The action returned by {@link ReviewCoordinator.checkEscalations} when an
140
140
  * escalation step has fired. Either reassigns the review to a new owner or
@@ -253,7 +253,7 @@ export declare class ReviewCoordinator {
253
253
  /** Create a review request and persist it. */
254
254
  createReview(workflowId: string, options: ReviewOptions): Promise<ReviewRequest>;
255
255
  /** Submit a review decision. */
256
- submitDecision(reviewId: string, decision: Omit<ReviewDecision, 'reviewId' | 'timestamp'>): Promise<ReviewDecision>;
256
+ submitDecision(reviewId: string, decision: Omit<ReviewDecisionRecord, 'reviewId' | 'timestamp'>): Promise<ReviewDecisionRecord>;
257
257
  /** Get a pending review. */
258
258
  getReview(workflowId: string, reviewId: string): Promise<ReviewRequest | null>;
259
259
  /** List pending reviews. */
@@ -116,16 +116,18 @@ export interface ActivityContext {
116
116
  * `client.activity.*` methods).
117
117
  *
118
118
  * The durable task token is announced on the engine as an
119
- * `activity:async-pending` event (listen for it to receive the token).
120
- * The token is deterministic and survives engine restart: it is re-minted
121
- * identically when the activity replays after recovery.
119
+ * `activity:async-pending` event and remains discoverable through
120
+ * `engine.listPendingAsyncActivities(workflowId)` or
121
+ * `client.activity.listPending(workflowId)`. The token is deterministic and
122
+ * survives engine restart: it is re-minted identically on recovery.
122
123
  *
123
124
  * Security: the token is a deterministic identifier, NOT a secret. When the
124
125
  * completion endpoint is reachable by untrusted callers, anyone who can infer
125
126
  * a workflow id can forge this activity's result or error. Treat the completed
126
127
  * value as hostile external input (validate it as you would a signal payload),
127
- * and gate the mutating surface with `serve({ auth })` if completions must not
128
- * be anonymous.
128
+ * The transport query requires `workflows:read`; completion and failure require
129
+ * `workflows:write`. Configure `serve({ auth })` with those scopes before
130
+ * exposing the routes.
129
131
  *
130
132
  * `completeAsync()` never returns normally — it throws an internal sentinel
131
133
  * that the engine recognizes to park the activity. Call it as the last
@@ -148,6 +150,51 @@ export interface ActivityContext {
148
150
  */
149
151
  completeAsync(): never;
150
152
  }
153
+ /**
154
+ * Durable metadata for one activity awaiting out-of-band completion.
155
+ * @example
156
+ * ```ts
157
+ * import type { PendingAsyncActivityInfo } from '@lostgradient/weft';
158
+ * declare const pending: PendingAsyncActivityInfo; console.log(pending.token, pending.activityName);
159
+ * ```
160
+ */
161
+ export interface PendingAsyncActivityInfo {
162
+ readonly token: string;
163
+ readonly operationId: string;
164
+ readonly activityName: string;
165
+ readonly step: number;
166
+ readonly attempt: number;
167
+ readonly createdAt: number;
168
+ }
169
+ /**
170
+ * Bounded cursor options for listing a workflow's pending async activities.
171
+ * @example
172
+ * ```ts
173
+ * import type { PendingAsyncActivityListOptions } from '@lostgradient/weft';
174
+ * const options: PendingAsyncActivityListOptions = { limit: 25 };
175
+ * void options;
176
+ * ```
177
+ */
178
+ export interface PendingAsyncActivityListOptions {
179
+ /** Maximum records to inspect for this page. Defaults to 50 and cannot exceed 200. */
180
+ readonly limit?: number;
181
+ /** Opaque continuation cursor returned by the previous page. */
182
+ readonly cursor?: string;
183
+ }
184
+ /**
185
+ * One deterministic page of pending async activities.
186
+ * @example
187
+ * ```ts
188
+ * import type { PendingAsyncActivityPage } from '@lostgradient/weft';
189
+ * const page: PendingAsyncActivityPage = { items: [] };
190
+ * void page;
191
+ * ```
192
+ */
193
+ export interface PendingAsyncActivityPage {
194
+ readonly items: PendingAsyncActivityInfo[];
195
+ /** Present when another page of durable records may remain. */
196
+ readonly nextCursor?: string;
197
+ }
151
198
  /**
152
199
  * Per-invocation overrides when calling an activity from a workflow via
153
200
  * `ctx.run(activity, input, options)`. Any field overrides the
@@ -52,6 +52,7 @@ export type BulkOperationPrincipal = {
52
52
  export type BulkOperationFilterSummary = {
53
53
  status?: WorkflowStatus | WorkflowStatus[];
54
54
  type?: string;
55
+ scheduleId?: string;
55
56
  tags?: string[];
56
57
  attributes?: Array<{
57
58
  key: string;
@@ -1,4 +1,15 @@
1
1
  import type { DefinitionSchema } from './definition-schema.ts';
2
+ /**
3
+ * Reset the module-level cached Valibot `toJsonSchema` converter.
4
+ *
5
+ * `cachedValibotConverter` is a process-global singleton shared by every test
6
+ * file that imports this module in the same `bun test` run, so whether it is
7
+ * already warm when a given test executes depends on unrelated test order.
8
+ * Tests that need to exercise the cache-miss path deterministically (e.g. the
9
+ * "no `process.getBuiltinModule`" error) must reset it first.
10
+ * @internal Test-only.
11
+ */
12
+ export declare function resetValibotConverterCacheForTesting(): void;
2
13
  /**
3
14
  * Direction parameter for {@link definitionSchemaToJsonSchema}. `"input"`
4
15
  * produces the JSON Schema describing the validator's accepted input;
@@ -1,6 +1,9 @@
1
- import { createRequire } from "node:module";
2
1
  import { z } from "zod";
2
+ import { tryLoadNodeBuiltin } from "../../runtime/portable.js";
3
3
  let cachedValibotConverter;
4
+ export function resetValibotConverterCacheForTesting() {
5
+ cachedValibotConverter = void 0;
6
+ }
4
7
  export function definitionSchemaToJsonSchema(schema, direction = "input") {
5
8
  const standard = schema["~standard"], vendor = standard.vendor;
6
9
  if (vendor === "zod")
@@ -28,10 +31,16 @@ function convertValibot(schema) {
28
31
  const result = loadValibotConverter()(schema);
29
32
  return stripDialect(requirePlainObject(result, "valibot"));
30
33
  }
34
+ function loadNodeRequire() {
35
+ return tryLoadNodeBuiltin("node:module")?.createRequire(import.meta.url);
36
+ }
31
37
  export function loadValibotConverter(requireModule) {
32
- const shouldUseCache = requireModule === void 0, resolver = requireModule ?? createRequire(import.meta.url);
38
+ const shouldUseCache = requireModule === void 0;
33
39
  if (shouldUseCache && cachedValibotConverter !== void 0)
34
40
  return cachedValibotConverter;
41
+ const resolver = requireModule ?? loadNodeRequire();
42
+ if (resolver === void 0)
43
+ throw Error("definitionSchemaToJsonSchema: converting a Valibot schema requires Bun or Node 22.5+ (process.getBuiltinModule) to resolve `@valibot/to-json-schema`. Not available in browser or edge runtimes; attach a `~standard.jsonSchema` converter to the schema instead.");
35
44
  let valibotModule;
36
45
  try {
37
46
  valibotModule = resolver("@valibot/to-json-schema");
@@ -43,6 +43,12 @@ export interface ListFilter {
43
43
  * ```
44
44
  */
45
45
  type?: string;
46
+ /** Match workflow runs launched by this recurring schedule. */
47
+ scheduleId?: string;
48
+ /** Match direct child workflows launched by this parent workflow id. */
49
+ parentWorkflowId?: string;
50
+ /** Narrow `parentWorkflowId` to one concrete parent run generation. */
51
+ parentWorkflowExecutionToken?: string;
46
52
  /** Match workflows that carry every listed tag. */
47
53
  tags?: string[];
48
54
  /** Filter on indexed search attributes (equality, string any-of, or range). */
@@ -446,14 +446,15 @@ export interface EngineOptions<TServices = unknown> {
446
446
  resolveWorkflowServices?: (info: WorkflowServicesResolverInfo) => WorkflowServicesResolution<TServices> | Promise<WorkflowServicesResolution<TServices>>;
447
447
  /**
448
448
  * Optional host sink for `ctx.log` records. When provided, every non-replayed
449
- * record from inline workflow execution is routed here (into your pino / winston
450
- * / OpenTelemetry stack, etc.) **instead of** the console; when omitted, records
451
- * fall back to the matching `console` method, preserving the default behavior.
449
+ * record from inline and worker workflow execution is routed here (into your
450
+ * pino / winston / OpenTelemetry stack, etc.) **instead of** the console; when
451
+ * omitted, records fall back to the matching process console.
452
452
  *
453
453
  * Engine-scoped infrastructure: set once at construction, never per run. The sink
454
454
  * is not invoked for records suppressed during replay, so a recovered run does
455
- * not re-emit its replayed prefix. Worker-mode records still log to the worker
456
- * process's console routing them back to the host is tracked separately.
455
+ * not re-emit its replayed prefix. Weft does not persist these records or expose
456
+ * a log-history API; the sink is the integration point for retention and remote
457
+ * log queries.
457
458
  */
458
459
  onLog?: (record: WorkflowLogRecord) => void;
459
460
  }
@@ -1,4 +1,4 @@
1
- import type { ReviewDecision as CompletedReviewDecision, ReviewRequest } from '../review/index.ts';
1
+ import type { ReviewDecisionRecord, ReviewRequest } from '../review/index.ts';
2
2
  /**
3
3
  * Outcome of a human review step initiated by `ctx.waitForReview`. Pass as
4
4
  * the `decision` field in {@link SubmitReviewOptions} when calling
@@ -125,7 +125,7 @@ export interface PendingReviewEntry extends ReviewRequest {
125
125
  * void entry;
126
126
  * ```
127
127
  */
128
- export interface CompletedReviewEntry extends CompletedReviewDecision {
128
+ export interface CompletedReviewEntry extends ReviewDecisionRecord {
129
129
  status: 'completed';
130
130
  workflowId: string;
131
131
  artifact: unknown;
@@ -23,6 +23,29 @@ export type ScheduleStatus = 'active' | 'paused' | 'cancelled';
23
23
  * ```
24
24
  */
25
25
  export type ScheduleOverlapPolicy = 'skip' | 'queue' | 'cancel-running' | 'allow';
26
+ /**
27
+ * One occurrence waiting behind the active run of a `queue` overlap schedule.
28
+ * `workflowId` is reserved when the occurrence enters the durable queue and is
29
+ * used when it eventually starts. No workflow record exists for that id until
30
+ * the queue drains, so `engine.get(workflowId)` returns `null` while it waits.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import type { ScheduleQueuedRun } from '@lostgradient/weft';
35
+ *
36
+ * const queuedRun: ScheduleQueuedRun = {
37
+ * workflowId: 'daily-report-queued-1',
38
+ * queuedAt: Date.now(),
39
+ * };
40
+ * void queuedRun;
41
+ * ```
42
+ */
43
+ export interface ScheduleQueuedRun {
44
+ workflowId: string;
45
+ queuedAt: number;
46
+ /** Nominal cadence timestamp retained from the occurrence that was queued. */
47
+ occurrence?: number;
48
+ }
26
49
  /**
27
50
  * Recurrence specification for a schedule. A schedule fires either on a cron
28
51
  * cadence (`{ cron: '0 9 * * *' }`) or at a fixed interval
@@ -95,6 +118,24 @@ export interface ScheduleOptions {
95
118
  */
96
119
  jitter?: Duration;
97
120
  }
121
+ /**
122
+ * Mutable options accepted when updating an existing schedule. Omitted fields
123
+ * retain their persisted values. Schedule identity, workflow type, and input
124
+ * are intentionally excluded.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import { Engine, workflow, type ScheduleUpdateOptions } from '@lostgradient/weft';
129
+ *
130
+ * const engine = new Engine();
131
+ * engine.register(workflow({ name: 'report' }).execute(async function* () { return 'done'; }));
132
+ * await engine.schedule('report', null, '0 9 * * *', { id: 'daily-report' });
133
+ * const options: ScheduleUpdateOptions = { overlap: 'queue', jitter: '30s' };
134
+ * await engine.updateSchedule('daily-report', '30 9 * * *', options);
135
+ * engine[Symbol.dispose]();
136
+ * ```
137
+ */
138
+ export type ScheduleUpdateOptions = Pick<ScheduleOptions, 'description' | 'overlap' | 'backfill' | 'jitter'>;
98
139
  /**
99
140
  * Declarative recurring schedule definition returned by {@link schedule}. Supply
100
141
  * exactly one of `cron` (cron cadence) or `every` (fixed interval).
@@ -176,7 +217,8 @@ export interface ScheduleState {
176
217
  missedFireCount: number;
177
218
  nextFireAt: number | null;
178
219
  currentWorkflowId?: string;
179
- queuedRuns: number;
220
+ /** Ordered durable occurrences waiting behind `currentWorkflowId`. */
221
+ queuedRuns: ScheduleQueuedRun[];
180
222
  }
181
223
  /**
182
224
  * Lightweight summary of a recurring schedule returned by list operations.
@@ -208,7 +250,8 @@ export interface ScheduleSummary {
208
250
  missedFireCount: number;
209
251
  nextFireAt: number | null;
210
252
  currentWorkflowId?: string;
211
- queuedRuns: number;
253
+ /** Ordered durable occurrences waiting behind `currentWorkflowId`. */
254
+ queuedRuns: ScheduleQueuedRun[];
212
255
  }
213
256
  /**
214
257
  * Filter criteria for `engine.listSchedules`. All fields are optional.
@@ -50,11 +50,11 @@ export interface WorkflowServicesResolverLaunchOptions {
50
50
  }
51
51
  /**
52
52
  * Schedule context passed to {@link EngineOptions.resolveWorkflowServices} for
53
- * scheduled occurrences. New scheduled runs persist this context with the
54
- * workflow record, so a fresh-process recovery receives the same schedule id and
55
- * known occurrence timestamp as the live launch path. Runs from older stores
56
- * that predate the metadata may omit it, and queue-drained runs may omit
57
- * `occurrence` because their original grid timestamp was not retained.
53
+ * scheduled occurrences. Scheduled runs persist this context with the workflow
54
+ * record, so a fresh-process recovery receives the same schedule id and known
55
+ * occurrence timestamp as the live launch path. Queue entries retain the
56
+ * occurrence timestamp through drain; runs from older stores that predate this
57
+ * metadata may omit the context or occurrence.
58
58
  *
59
59
  * @example
60
60
  * ```ts
@@ -66,6 +66,10 @@ export interface WorkflowState {
66
66
  * tree.
67
67
  */
68
68
  executionStateOwnerId?: string;
69
+ /** Immediate parent workflow id for runs launched through `ctx.startChild()`. */
70
+ parentWorkflowId?: WorkflowId;
71
+ /** Concrete parent run token, used to distinguish stable-id reuse generations. */
72
+ parentWorkflowExecutionToken?: string;
69
73
  createdAt: number;
70
74
  startedAt?: number;
71
75
  updatedAt: number;
@@ -76,6 +80,30 @@ export interface WorkflowState {
76
80
  * another workflow checkpoint. Absent for workflows started normally.
77
81
  */
78
82
  forkedFrom?: ForkLineage;
83
+ /** Immediate terminal run displaced when this run was created with `start-new`. */
84
+ restartedFrom?: RestartLineage;
85
+ }
86
+ /**
87
+ * Immediate predecessor recorded when `onTerminalConflict: 'start-new'` reuses
88
+ * a stable workflow id. The execution token identifies the concrete displaced
89
+ * run because both runs share the same workflow id.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import type { RestartLineage } from '@lostgradient/weft';
94
+ *
95
+ * const lineage: RestartLineage = {
96
+ * workflowId: 'nightly-reconciliation',
97
+ * workflowExecutionToken: 'prior-run-token',
98
+ * replacedAt: Date.now(),
99
+ * };
100
+ * void lineage;
101
+ * ```
102
+ */
103
+ export interface RestartLineage {
104
+ workflowId: WorkflowId;
105
+ workflowExecutionToken?: string;
106
+ replacedAt: number;
79
107
  }
80
108
  /**
81
109
  * Lineage metadata recorded when a workflow was created by forking another
@@ -92,6 +120,32 @@ export interface ForkLineage {
92
120
  * whole workflow. Used in {@link WorkflowTimelineEntry}.
93
121
  */
94
122
  export type WorkflowTimelineStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'timed-out';
123
+ /**
124
+ * Bounded metadata describing one direct operation inside a timeline coordinator.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import type { WorkflowTimelineOperationDetail } from '@lostgradient/weft';
129
+ *
130
+ * const winner: WorkflowTimelineOperationDetail = {
131
+ * index: 0,
132
+ * operationId: 'race:0:0',
133
+ * operationType: 'activity',
134
+ * operationLabel: 'fetchPrimary',
135
+ * outcome: 'won',
136
+ * };
137
+ * void winner;
138
+ * ```
139
+ */
140
+ export type WorkflowTimelineOperationDetail = {
141
+ index: number;
142
+ key?: string;
143
+ operationId: string;
144
+ operationType: string;
145
+ operationLabel: string;
146
+ outcome: 'fulfilled' | 'rejected' | 'won' | 'lost';
147
+ errorSummary?: string;
148
+ };
95
149
  /**
96
150
  * A single chronological entry in a workflow's execution timeline, summarising
97
151
  * one operation (activity call, sleep, signal wait, etc.). Returned by
@@ -110,6 +164,16 @@ export type WorkflowTimelineEntry = {
110
164
  timestamp: number;
111
165
  status: WorkflowTimelineStatus;
112
166
  versionTuple?: WorkflowVersionTuple;
167
+ /** Direct branch metadata for `ctx.all`, `ctx.runAll`, and `ctx.race`. */
168
+ branches?: WorkflowTimelineOperationDetail[];
169
+ /** Number of direct branches omitted from `branches` by the durable size bound. */
170
+ branchesOmitted?: number;
171
+ /** Ordered direct operations yielded by `ctx.speculate`. */
172
+ children?: WorkflowTimelineOperationDetail[];
173
+ /** Number of direct speculative children omitted from `children` by the durable size bound. */
174
+ childrenOmitted?: number;
175
+ /** Whether the speculative child context committed or rolled back. */
176
+ speculationOutcome?: 'committed' | 'rolled-back';
113
177
  };
114
178
  /**
115
179
  * Options accepted by `ctx.state.session(key, options)`.