@hexabot-ai/agentic 3.0.1-alpha.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 (176) hide show
  1. package/LICENSE.md +120 -0
  2. package/README.md +231 -0
  3. package/dist/cjs/action/abstract-action.js +141 -0
  4. package/dist/cjs/action/action.js +47 -0
  5. package/dist/cjs/action/action.types.js +7 -0
  6. package/dist/cjs/bindings/base-binding.js +278 -0
  7. package/dist/cjs/context.js +79 -0
  8. package/dist/cjs/dsl.types.js +318 -0
  9. package/dist/cjs/index.js +52 -0
  10. package/dist/cjs/runner-runtime-control.js +343 -0
  11. package/dist/cjs/step-executors/conditional-executor.js +63 -0
  12. package/dist/cjs/step-executors/loop-executor.js +187 -0
  13. package/dist/cjs/step-executors/parallel-executor.js +51 -0
  14. package/dist/cjs/step-executors/skip-helpers.js +29 -0
  15. package/dist/cjs/step-executors/task-executor.js +155 -0
  16. package/dist/cjs/step-executors/types.js +7 -0
  17. package/dist/cjs/suspension-rebuilder.js +301 -0
  18. package/dist/cjs/utils/deferred.js +17 -0
  19. package/dist/cjs/utils/naming.js +48 -0
  20. package/dist/cjs/utils/timeout.js +42 -0
  21. package/dist/cjs/utils/workflow-definition.js +102 -0
  22. package/dist/cjs/workflow-compiler.js +212 -0
  23. package/dist/cjs/workflow-event-emitter.js +40 -0
  24. package/dist/cjs/workflow-runner.js +448 -0
  25. package/dist/cjs/workflow-types.js +7 -0
  26. package/dist/cjs/workflow-values.js +99 -0
  27. package/dist/cjs/workflow.js +243 -0
  28. package/dist/esm/action/abstract-action.js +137 -0
  29. package/dist/esm/action/action.js +44 -0
  30. package/dist/esm/action/action.types.js +6 -0
  31. package/dist/esm/bindings/base-binding.js +273 -0
  32. package/dist/esm/context.js +75 -0
  33. package/dist/esm/dsl.types.js +309 -0
  34. package/dist/esm/index.js +16 -0
  35. package/dist/esm/runner-runtime-control.js +338 -0
  36. package/dist/esm/step-executors/conditional-executor.js +60 -0
  37. package/dist/esm/step-executors/loop-executor.js +182 -0
  38. package/dist/esm/step-executors/parallel-executor.js +48 -0
  39. package/dist/esm/step-executors/skip-helpers.js +25 -0
  40. package/dist/esm/step-executors/task-executor.js +152 -0
  41. package/dist/esm/step-executors/types.js +6 -0
  42. package/dist/esm/suspension-rebuilder.js +296 -0
  43. package/dist/esm/utils/deferred.js +14 -0
  44. package/dist/esm/utils/naming.js +42 -0
  45. package/dist/esm/utils/timeout.js +37 -0
  46. package/dist/esm/utils/workflow-definition.js +98 -0
  47. package/dist/esm/workflow-compiler.js +208 -0
  48. package/dist/esm/workflow-event-emitter.js +36 -0
  49. package/dist/esm/workflow-runner.js +444 -0
  50. package/dist/esm/workflow-types.js +6 -0
  51. package/dist/esm/workflow-values.js +89 -0
  52. package/dist/esm/workflow.js +236 -0
  53. package/dist/types/action/abstract-action.d.ts +54 -0
  54. package/dist/types/action/abstract-action.d.ts.map +1 -0
  55. package/dist/types/action/action.d.ts +23 -0
  56. package/dist/types/action/action.d.ts.map +1 -0
  57. package/dist/types/action/action.types.d.ts +55 -0
  58. package/dist/types/action/action.types.d.ts.map +1 -0
  59. package/dist/types/bindings/base-binding.d.ts +52 -0
  60. package/dist/types/bindings/base-binding.d.ts.map +1 -0
  61. package/dist/types/context.d.ts +105 -0
  62. package/dist/types/context.d.ts.map +1 -0
  63. package/dist/types/dsl.types.d.ts +220 -0
  64. package/dist/types/dsl.types.d.ts.map +1 -0
  65. package/dist/types/index.d.ts +18 -0
  66. package/dist/types/index.d.ts.map +1 -0
  67. package/dist/types/runner-runtime-control.d.ts +64 -0
  68. package/dist/types/runner-runtime-control.d.ts.map +1 -0
  69. package/dist/types/step-executors/conditional-executor.d.ts +13 -0
  70. package/dist/types/step-executors/conditional-executor.d.ts.map +1 -0
  71. package/dist/types/step-executors/loop-executor.d.ts +33 -0
  72. package/dist/types/step-executors/loop-executor.d.ts.map +1 -0
  73. package/dist/types/step-executors/parallel-executor.d.ts +14 -0
  74. package/dist/types/step-executors/parallel-executor.d.ts.map +1 -0
  75. package/dist/types/step-executors/skip-helpers.d.ts +12 -0
  76. package/dist/types/step-executors/skip-helpers.d.ts.map +1 -0
  77. package/dist/types/step-executors/task-executor.d.ts +13 -0
  78. package/dist/types/step-executors/task-executor.d.ts.map +1 -0
  79. package/dist/types/step-executors/types.d.ts +24 -0
  80. package/dist/types/step-executors/types.d.ts.map +1 -0
  81. package/dist/types/suspension-rebuilder.d.ts +67 -0
  82. package/dist/types/suspension-rebuilder.d.ts.map +1 -0
  83. package/dist/types/utils/deferred.d.ts +7 -0
  84. package/dist/types/utils/deferred.d.ts.map +1 -0
  85. package/dist/types/utils/naming.d.ts +24 -0
  86. package/dist/types/utils/naming.d.ts.map +1 -0
  87. package/dist/types/utils/timeout.d.ts +17 -0
  88. package/dist/types/utils/timeout.d.ts.map +1 -0
  89. package/dist/types/utils/workflow-definition.d.ts +3 -0
  90. package/dist/types/utils/workflow-definition.d.ts.map +1 -0
  91. package/dist/types/workflow-compiler.d.ts +12 -0
  92. package/dist/types/workflow-compiler.d.ts.map +1 -0
  93. package/dist/types/workflow-event-emitter.d.ts +70 -0
  94. package/dist/types/workflow-event-emitter.d.ts.map +1 -0
  95. package/dist/types/workflow-runner.d.ts +179 -0
  96. package/dist/types/workflow-runner.d.ts.map +1 -0
  97. package/dist/types/workflow-types.d.ts +195 -0
  98. package/dist/types/workflow-types.d.ts.map +1 -0
  99. package/dist/types/workflow-values.d.ts +34 -0
  100. package/dist/types/workflow-values.d.ts.map +1 -0
  101. package/dist/types/workflow.d.ts +75 -0
  102. package/dist/types/workflow.d.ts.map +1 -0
  103. package/examples/defs-bindings-agent/actions/ai-agent.ts +110 -0
  104. package/examples/defs-bindings-agent/actions/caculate-score.ts +49 -0
  105. package/examples/defs-bindings-agent/actions/index.ts +15 -0
  106. package/examples/defs-bindings-agent/bindings.ts +20 -0
  107. package/examples/defs-bindings-agent/context.ts +32 -0
  108. package/examples/defs-bindings-agent/workflow.ts +67 -0
  109. package/examples/defs-bindings-agent/workflow.yml +28 -0
  110. package/examples/full/actions/await-user-input.ts +46 -0
  111. package/examples/full/actions/call-llm.ts +133 -0
  112. package/examples/full/actions/create-ticket.ts +46 -0
  113. package/examples/full/actions/decision-router.ts +83 -0
  114. package/examples/full/actions/get-calendar-events.ts +55 -0
  115. package/examples/full/actions/get-user-profile.ts +72 -0
  116. package/examples/full/actions/index.ts +33 -0
  117. package/examples/full/actions/query-memory.ts +45 -0
  118. package/examples/full/actions/search-web.ts +53 -0
  119. package/examples/full/actions/send-email.ts +50 -0
  120. package/examples/full/context.ts +41 -0
  121. package/examples/full/workflow.ts +75 -0
  122. package/examples/full/workflow.yml +359 -0
  123. package/examples/loop/actions/await-reply.ts +47 -0
  124. package/examples/loop/actions/index.ts +19 -0
  125. package/examples/loop/actions/send-text-message.ts +40 -0
  126. package/examples/loop/context.ts +32 -0
  127. package/examples/loop/workflow.ts +66 -0
  128. package/examples/loop/workflow.yml +66 -0
  129. package/examples/suspend-resume/actions/format-reply.ts +43 -0
  130. package/examples/suspend-resume/actions/index.ts +13 -0
  131. package/examples/suspend-resume/actions/wait-for-user.ts +51 -0
  132. package/examples/suspend-resume/context.ts +32 -0
  133. package/examples/suspend-resume/workflow.ts +79 -0
  134. package/examples/suspend-resume/workflow.yml +29 -0
  135. package/package.json +60 -0
  136. package/src/__tests__/parser.test.ts +95 -0
  137. package/src/__tests__/suspension-rebuilder.test.ts +361 -0
  138. package/src/__tests__/test-helpers.ts +36 -0
  139. package/src/__tests__/validation.test.ts +526 -0
  140. package/src/__tests__/workflow-compiler.test.ts +715 -0
  141. package/src/__tests__/workflow-definition-path.test.ts +381 -0
  142. package/src/__tests__/workflow-event-emitter.test.ts +50 -0
  143. package/src/__tests__/workflow-runner.test.ts +1397 -0
  144. package/src/__tests__/workflow-values.test.ts +132 -0
  145. package/src/__tests__/workflow.test.ts +320 -0
  146. package/src/action/__tests__/abstract-action-timing.test.ts +183 -0
  147. package/src/action/__tests__/action.test.ts +473 -0
  148. package/src/action/abstract-action.ts +215 -0
  149. package/src/action/action.ts +83 -0
  150. package/src/action/action.types.ts +93 -0
  151. package/src/bindings/base-binding.ts +508 -0
  152. package/src/context.ts +179 -0
  153. package/src/dsl.types.ts +473 -0
  154. package/src/index.ts +114 -0
  155. package/src/runner-runtime-control.ts +544 -0
  156. package/src/step-executors/conditional-executor.test.ts +192 -0
  157. package/src/step-executors/conditional-executor.ts +83 -0
  158. package/src/step-executors/loop-executor.test.ts +303 -0
  159. package/src/step-executors/loop-executor.ts +299 -0
  160. package/src/step-executors/parallel-executor.test.ts +182 -0
  161. package/src/step-executors/parallel-executor.ts +77 -0
  162. package/src/step-executors/skip-helpers.ts +68 -0
  163. package/src/step-executors/task-executor.test.ts +257 -0
  164. package/src/step-executors/task-executor.ts +248 -0
  165. package/src/step-executors/types.ts +70 -0
  166. package/src/suspension-rebuilder.ts +504 -0
  167. package/src/utils/deferred.ts +23 -0
  168. package/src/utils/naming.ts +55 -0
  169. package/src/utils/timeout.ts +48 -0
  170. package/src/utils/workflow-definition.ts +173 -0
  171. package/src/workflow-compiler.ts +317 -0
  172. package/src/workflow-event-emitter.ts +86 -0
  173. package/src/workflow-runner.ts +603 -0
  174. package/src/workflow-types.ts +207 -0
  175. package/src/workflow-values.ts +159 -0
  176. package/src/workflow.ts +387 -0
@@ -0,0 +1,448 @@
1
+ "use strict";
2
+ /*
3
+ * Hexabot — Fair Core License (FCL-1.0-ALv2)
4
+ * Copyright (c) 2025 Hexastack.
5
+ * Full terms: see LICENSE.md.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.WorkflowRunner = void 0;
9
+ const runner_runtime_control_1 = require("./runner-runtime-control");
10
+ const conditional_executor_1 = require("./step-executors/conditional-executor");
11
+ const loop_executor_1 = require("./step-executors/loop-executor");
12
+ const parallel_executor_1 = require("./step-executors/parallel-executor");
13
+ const task_executor_1 = require("./step-executors/task-executor");
14
+ const suspension_rebuilder_1 = require("./suspension-rebuilder");
15
+ const workflow_event_emitter_1 = require("./workflow-event-emitter");
16
+ const workflow_values_1 = require("./workflow-values");
17
+ /**
18
+ * Executes a compiled workflow definition, tracking state, suspensions, and event hooks.
19
+ * Create a new instance per execution to avoid leaking state between runs.
20
+ */
21
+ class WorkflowRunner {
22
+ /**
23
+ * Create a new runner for a compiled workflow definition.
24
+ *
25
+ * @param compiled The compiled workflow to execute.
26
+ * @param options Optional runner configuration such as run id.
27
+ */
28
+ constructor(compiled, options) {
29
+ // Current lifecycle status of the workflow execution.
30
+ this.status = 'idle';
31
+ // Snapshots of action execution keyed by step id for inspection/resume.
32
+ this.snapshots = {};
33
+ // Detailed execution records for each task step, used by UI/telemetry.
34
+ this.stepLog = {};
35
+ this.compiled = compiled;
36
+ this.runId = options?.runId;
37
+ }
38
+ /**
39
+ * Get the current state of the workflow run.
40
+ *
41
+ * @returns The state of the workflow execution.
42
+ */
43
+ getState() {
44
+ return this.state;
45
+ }
46
+ /**
47
+ * Get the current lifecycle status of the workflow run.
48
+ *
49
+ * @returns The status of the workflow execution.
50
+ */
51
+ getStatus() {
52
+ return this.status;
53
+ }
54
+ /**
55
+ * Snapshot the current workflow status and action states.
56
+ *
57
+ * @returns A snapshot representing the workflow and each action's status.
58
+ */
59
+ getSnapshot() {
60
+ return {
61
+ status: this.status,
62
+ actions: { ...this.snapshots },
63
+ };
64
+ }
65
+ /**
66
+ * Read the per-step execution records captured for UI/telemetry.
67
+ *
68
+ * @returns A shallow copy of the step execution log.
69
+ */
70
+ getStepLog() {
71
+ return Object.fromEntries(Object.entries(this.stepLog).map(([id, record]) => [
72
+ id,
73
+ {
74
+ ...record,
75
+ context: record.context ? { ...record.context } : undefined,
76
+ error: record.error ? { ...record.error } : undefined,
77
+ },
78
+ ]));
79
+ }
80
+ /**
81
+ * Read the step currently being executed.
82
+ *
83
+ * @returns Metadata for the in-flight step, if any.
84
+ */
85
+ getCurrentStep() {
86
+ return this.currentStep;
87
+ }
88
+ /**
89
+ * Access the last payload supplied to a resume call.
90
+ *
91
+ * @returns The most recent resume data, or undefined if none.
92
+ */
93
+ getLastResumeData() {
94
+ return this.lastResumeData;
95
+ }
96
+ /**
97
+ * Begin executing the workflow from the first step.
98
+ * Returns a status object describing whether execution finished, suspended, or failed.
99
+ *
100
+ * @param args Input payload and context to seed execution.
101
+ * @returns Execution result including status and snapshot.
102
+ */
103
+ async start(args) {
104
+ this.status = 'running';
105
+ this.snapshots = {};
106
+ this.stepLog = {};
107
+ this.suspension = undefined;
108
+ this.lastResumeData = undefined;
109
+ this.currentStep = undefined;
110
+ this.context = args.context;
111
+ this.state = {
112
+ input: this.compiled.inputParser.parse(args.inputData ?? {}),
113
+ output: {},
114
+ iterationStack: [],
115
+ };
116
+ this.runtimeControl = new runner_runtime_control_1.RunnerRuntimeControl(this);
117
+ this.context.attachWorkflowRuntime(this.runtimeControl);
118
+ this.emit('hook:workflow:start', { runId: this.runId });
119
+ const state = this.state;
120
+ if (!state) {
121
+ throw new Error('Workflow state not initialized.');
122
+ }
123
+ return this.runExecution(() => this.executeFlow(this.compiled.flow, state, []));
124
+ }
125
+ /**
126
+ * Resume a previously suspended workflow using the supplied resume data.
127
+ *
128
+ * @param args Data provided to resume the suspended step.
129
+ * @returns Execution result including status and snapshot.
130
+ */
131
+ async resume(args) {
132
+ if (this.status !== 'suspended' || !this.suspension) {
133
+ throw new Error('Cannot resume a workflow that is not suspended.');
134
+ }
135
+ if (!this.context || !this.state) {
136
+ throw new Error('Workflow state is not initialized.');
137
+ }
138
+ this.status = 'running';
139
+ this.lastResumeData = args.resumeData;
140
+ const suspension = this.suspension;
141
+ return this.runExecution(() => suspension.continue(args.resumeData));
142
+ }
143
+ /**
144
+ * Execute the provided workflow operation and normalize suspension, finish, and failure handling.
145
+ *
146
+ * @param execute Function that advances workflow execution and may return a suspension.
147
+ * @returns Result of the execution attempt.
148
+ */
149
+ async runExecution(execute) {
150
+ try {
151
+ const suspension = await execute();
152
+ if (suspension) {
153
+ this.suspension = suspension;
154
+ this.status = 'suspended';
155
+ this.emit('hook:workflow:suspended', {
156
+ runId: this.runId,
157
+ step: suspension.step,
158
+ reason: suspension.reason,
159
+ data: suspension.data,
160
+ });
161
+ return {
162
+ status: 'suspended',
163
+ step: suspension.step,
164
+ reason: suspension.reason,
165
+ data: suspension.data,
166
+ stepExecId: suspension.stepExecId,
167
+ suspendIndex: suspension.suspendIndex,
168
+ suspendKey: suspension.suspendKey,
169
+ awaitResults: suspension.awaitResults,
170
+ snapshot: this.getSnapshot(),
171
+ };
172
+ }
173
+ const output = await this.evaluateWorkflowOutputs();
174
+ this.status = 'finished';
175
+ this.suspension = undefined;
176
+ this.currentStep = undefined;
177
+ this.emit('hook:workflow:finish', { runId: this.runId, output });
178
+ if (!this.context) {
179
+ throw new Error('Workflow context is not attached.');
180
+ }
181
+ this.context.attachWorkflowRuntime(undefined);
182
+ return { status: 'finished', output, snapshot: this.getSnapshot() };
183
+ }
184
+ catch (error) {
185
+ this.status = 'failed';
186
+ this.currentStep = undefined;
187
+ this.emit('hook:workflow:failure', { runId: this.runId, error });
188
+ this.context?.attachWorkflowRuntime(undefined);
189
+ return { status: 'failed', error, snapshot: this.getSnapshot() };
190
+ }
191
+ }
192
+ /**
193
+ * Rebuild a runner from persisted state, allowing hosts to resume after restarts.
194
+ *
195
+ * @param compiled The compiled workflow definition.
196
+ * @param options Persisted state and metadata needed to rebuild the runner.
197
+ * @returns A runner positioned to continue from the prior suspension or status.
198
+ */
199
+ static async fromPersistedState(compiled, options) {
200
+ const runner = new WorkflowRunner(compiled, {
201
+ runId: options.runId,
202
+ });
203
+ runner.state = {
204
+ input: options.state.input ?? {},
205
+ output: options.state.output ?? {},
206
+ iteration: options.state.iteration,
207
+ accumulator: options.state.accumulator,
208
+ iterationStack: [...(options.state.iterationStack ?? [])],
209
+ };
210
+ runner.context = options.context;
211
+ runner.snapshots = options.snapshot.actions ?? {};
212
+ runner.stepLog = {};
213
+ runner.status = options.snapshot.status ?? 'idle';
214
+ runner.lastResumeData = options.lastResumeData;
215
+ runner.runtimeControl = new runner_runtime_control_1.RunnerRuntimeControl(runner);
216
+ options.context.attachWorkflowRuntime(runner.runtimeControl);
217
+ if (options.suspension) {
218
+ const suspension = (0, suspension_rebuilder_1.rebuildSuspension)(runner.createSuspensionRebuilderDeps(), {
219
+ state: runner.state,
220
+ stepId: options.suspension.stepId,
221
+ reason: options.suspension.reason ?? undefined,
222
+ data: options.suspension.data,
223
+ stepExecId: options.suspension.stepExecId,
224
+ suspendIndex: options.suspension.suspendIndex,
225
+ suspendKey: options.suspension.suspendKey,
226
+ awaitResults: options.suspension.awaitResults,
227
+ });
228
+ if (!suspension) {
229
+ throw new Error(`Unable to rebuild suspension for step ${options.suspension.stepId}`);
230
+ }
231
+ runner.suspension = suspension;
232
+ runner.status = 'suspended';
233
+ }
234
+ return runner;
235
+ }
236
+ /**
237
+ * Emit an event if an emitter is provided.
238
+ *
239
+ * @param event The event name to emit.
240
+ * @param payload The event payload.
241
+ */
242
+ emit(event, payload) {
243
+ this.context?.eventEmitter?.emit(event, payload);
244
+ }
245
+ /**
246
+ * Evaluate and map the workflow outputs after all steps have completed.
247
+ * Throws if the internal state or context were not initialized.
248
+ *
249
+ * @returns The evaluated workflow outputs.
250
+ * @throws When state or context are missing.
251
+ */
252
+ async evaluateWorkflowOutputs() {
253
+ if (!this.state || !this.context) {
254
+ throw new Error('Workflow state not initialized.');
255
+ }
256
+ return (0, workflow_values_1.evaluateMapping)(this.compiled.outputMapping, {
257
+ input: this.state.input,
258
+ context: this.context.state,
259
+ output: this.state.output,
260
+ });
261
+ }
262
+ /**
263
+ * Build a stable step id that reflects the current loop iteration stack.
264
+ *
265
+ * @param step The compiled step to annotate.
266
+ * @param iterationStack The loop stack representing nested iterations.
267
+ * @returns A step info object with an iteration-aware id.
268
+ */
269
+ buildInstanceStepInfo(step, iterationStack) {
270
+ const suffix = iterationStack.length > 0 ? `[${iterationStack.join('.')}]` : '';
271
+ return {
272
+ id: `${step.id}${suffix}`,
273
+ name: step.label,
274
+ type: step.type,
275
+ };
276
+ }
277
+ /**
278
+ * Record an action snapshot for the given step id.
279
+ *
280
+ * @param step The step being updated.
281
+ * @param status The new snapshot status.
282
+ * @param reason Optional reason to include when marking failure/suspension.
283
+ */
284
+ markSnapshot(step, status, reason) {
285
+ this.snapshots[step.id] = {
286
+ id: step.id,
287
+ name: step.name,
288
+ status,
289
+ reason,
290
+ };
291
+ }
292
+ /**
293
+ * Record a detailed execution entry for the given step.
294
+ *
295
+ * @param step The step being updated.
296
+ * @param update Partial record fields to merge into the log.
297
+ */
298
+ recordStepExecution(step, update) {
299
+ const existing = this.stepLog[step.id];
300
+ const base = existing ??
301
+ {
302
+ id: step.id,
303
+ name: step.name,
304
+ status: 'pending',
305
+ };
306
+ const mergedContext = existing?.context || update.context
307
+ ? { ...existing?.context, ...update.context }
308
+ : undefined;
309
+ this.stepLog[step.id] = {
310
+ ...base,
311
+ ...update,
312
+ id: step.id,
313
+ name: step.name,
314
+ status: update.status ?? base.status,
315
+ context: mergedContext,
316
+ };
317
+ }
318
+ /**
319
+ * Construct the environment object passed to step executors.
320
+ *
321
+ * @returns A step executor environment bound to this runner.
322
+ * @throws When the workflow context is missing.
323
+ */
324
+ createExecutorEnv() {
325
+ if (!this.context) {
326
+ throw new Error('Workflow context is not attached.');
327
+ }
328
+ return {
329
+ compiled: this.compiled,
330
+ context: this.context,
331
+ runId: this.runId,
332
+ buildInstanceStepInfo: (step, iterationStack) => this.buildInstanceStepInfo(step, iterationStack),
333
+ markSnapshot: (step, status, reason) => this.markSnapshot(step, status, reason),
334
+ recordStepExecution: (step, update) => this.recordStepExecution(step, update),
335
+ emit: (event, payload) => this.emit(event, payload),
336
+ setCurrentStep: (step) => {
337
+ this.currentStep = step;
338
+ },
339
+ beginStepExecution: (stepId) => {
340
+ if (!this.runtimeControl) {
341
+ return `${stepId}#1`;
342
+ }
343
+ return this.runtimeControl.beginStepExecution(stepId);
344
+ },
345
+ waitForStepSuspension: (stepId) => {
346
+ if (!this.runtimeControl) {
347
+ throw new Error('Workflow runtime control is not initialized.');
348
+ }
349
+ return this.runtimeControl.waitForStepSuspension(stepId);
350
+ },
351
+ clearStepSuspensions: (stepId, error) => {
352
+ this.runtimeControl?.clearStepSuspensions(stepId, error);
353
+ },
354
+ primeStepResumeData: (stepId, resumeData) => {
355
+ this.runtimeControl?.primeStepResumeData(stepId, resumeData);
356
+ },
357
+ prepareStepReplay: (seed) => {
358
+ this.runtimeControl?.prepareStepReplay(seed);
359
+ },
360
+ recordStepSuspendResult: (params) => {
361
+ this.runtimeControl?.recordStepSuspendResult(params);
362
+ },
363
+ captureTaskOutput: (task, state, result) => this.captureTaskOutput(task, state, result),
364
+ executeFlow: (steps, state, path, startIndex) => this.executeFlow(steps, state, path, startIndex),
365
+ executeStep: (step, state, path) => this.executeStep(step, state, path),
366
+ };
367
+ }
368
+ /**
369
+ * Build dependencies used to reconstruct a suspension from persisted state.
370
+ *
371
+ * @returns Dependency bag for suspension rebuilders.
372
+ */
373
+ createSuspensionRebuilderDeps() {
374
+ return {
375
+ compiled: this.compiled,
376
+ context: this.context,
377
+ runId: this.runId,
378
+ createExecutorEnv: () => this.createExecutorEnv(),
379
+ buildInstanceStepInfo: (step, iterationStack) => this.buildInstanceStepInfo(step, iterationStack),
380
+ captureTaskOutput: (task, state, result) => this.captureTaskOutput(task, state, result),
381
+ markSnapshot: (step, status, reason) => this.markSnapshot(step, status, reason),
382
+ emit: (event, payload) => this.emit(event, payload),
383
+ executeFlow: (steps, state, path, startIndex) => this.executeFlow(steps, state, path, startIndex),
384
+ };
385
+ }
386
+ /**
387
+ * Walk a list of compiled steps, threading state and returning a suspension when encountered.
388
+ *
389
+ * @param steps The steps to execute sequentially.
390
+ * @param state Mutable execution state shared across steps.
391
+ * @param path Path tokens leading to the current step for tracing.
392
+ * @param startIndex Index to resume from within the flow.
393
+ * @returns A suspension if execution pauses, otherwise void.
394
+ */
395
+ async executeFlow(steps, state, path, startIndex = 0) {
396
+ // Walk the flow sequentially; if a step suspends, wrap its continuation so we resume at the same index.
397
+ for (let index = startIndex; index < steps.length; index += 1) {
398
+ const step = steps[index];
399
+ const stepPath = [...path, index];
400
+ const suspension = await this.executeStep(step, state, stepPath);
401
+ if (suspension) {
402
+ return {
403
+ ...suspension,
404
+ continue: async (resumeData) => {
405
+ const next = await suspension.continue(resumeData);
406
+ if (next) {
407
+ return next;
408
+ }
409
+ return this.executeFlow(steps, state, path, index + 1);
410
+ },
411
+ };
412
+ }
413
+ }
414
+ return undefined;
415
+ }
416
+ /**
417
+ * Execute a single compiled step by delegating to its executor.
418
+ *
419
+ * @param step The step to run.
420
+ * @param state The shared execution state.
421
+ * @param path Tokens describing the location of the step in the workflow.
422
+ * @returns A suspension if the step pauses execution, otherwise void.
423
+ */
424
+ async executeStep(step, state, path) {
425
+ const env = this.createExecutorEnv();
426
+ switch (step.type) {
427
+ case workflow_event_emitter_1.StepType.Task:
428
+ return (0, task_executor_1.executeTaskStep)(env, step, state, path);
429
+ case workflow_event_emitter_1.StepType.Parallel:
430
+ return (0, parallel_executor_1.executeParallel)(env, step, state, path);
431
+ case workflow_event_emitter_1.StepType.Conditional:
432
+ return (0, conditional_executor_1.executeConditional)(env, step, state, path);
433
+ case workflow_event_emitter_1.StepType.Loop:
434
+ return (0, loop_executor_1.executeLoop)(env, step, state, path);
435
+ }
436
+ }
437
+ /**
438
+ * Store the raw task result under the task name in the workflow output state.
439
+ * @reviewed
440
+ * @param task The task whose output is being captured.
441
+ * @param state Current execution state to mutate.
442
+ * @param result Raw result returned by the task action.
443
+ */
444
+ async captureTaskOutput(task, state, result) {
445
+ state.output[task.name] = result;
446
+ }
447
+ }
448
+ exports.WorkflowRunner = WorkflowRunner;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /*
3
+ * Hexabot — Fair Core License (FCL-1.0-ALv2)
4
+ * Copyright (c) 2025 Hexastack.
5
+ * Full terms: see LICENSE.md.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /*
3
+ * Hexabot — Fair Core License (FCL-1.0-ALv2)
4
+ * Copyright (c) 2025 Hexastack.
5
+ * Full terms: see LICENSE.md.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.mergeSettings = exports.evaluateMapping = exports.evaluateValue = exports.compileValue = void 0;
12
+ const jsonata_1 = __importDefault(require("jsonata"));
13
+ /** Basic object guard that rejects arrays and null. */
14
+ const isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
15
+ const registerJsonataFunctions = (expression, registry) => {
16
+ if (!registry) {
17
+ return;
18
+ }
19
+ for (const [name, config] of Object.entries(registry)) {
20
+ if (typeof config === 'function') {
21
+ expression.registerFunction(name, config);
22
+ continue;
23
+ }
24
+ if (config && typeof config.implementation === 'function') {
25
+ expression.registerFunction(name, config.implementation, config.signature);
26
+ continue;
27
+ }
28
+ throw new Error(`Invalid JSONata function config for "${name}"`);
29
+ }
30
+ };
31
+ /**
32
+ * Prepares a workflow value for evaluation.
33
+ * Strings prefixed with `=` are treated as JSONata expressions; everything else is a literal.
34
+ */
35
+ const compileValue = (value, options) => {
36
+ if (typeof value === 'string' && value.startsWith('=')) {
37
+ const expression = (0, jsonata_1.default)(value.slice(1));
38
+ registerJsonataFunctions(expression, options?.jsonataFunctions);
39
+ return { kind: 'expression', source: value, expression };
40
+ }
41
+ return { kind: 'literal', value };
42
+ };
43
+ exports.compileValue = compileValue;
44
+ /**
45
+ * Evaluate a compiled value against the current workflow scope.
46
+ * Expressions are executed via JSONata with the scope exposed as variables; `context`
47
+ * represents the workflow context state, not the context instance itself.
48
+ */
49
+ const evaluateValue = async (compiled, scope) => {
50
+ if (compiled.kind === 'literal') {
51
+ return compiled.value;
52
+ }
53
+ return compiled.expression.evaluate({}, {
54
+ input: scope.input,
55
+ context: scope.context,
56
+ output: scope.output,
57
+ iteration: scope.iteration,
58
+ accumulator: scope.accumulator,
59
+ result: scope.result,
60
+ });
61
+ };
62
+ exports.evaluateValue = evaluateValue;
63
+ /**
64
+ * Evaluate all entries of a compiled mapping, returning a plain object.
65
+ * Missing mappings resolve to an empty object.
66
+ */
67
+ const evaluateMapping = async (mapping, scope) => {
68
+ if (!mapping) {
69
+ return {};
70
+ }
71
+ const result = {};
72
+ for (const [key, compiled] of Object.entries(mapping)) {
73
+ result[key] = await (0, exports.evaluateValue)(compiled, scope);
74
+ }
75
+ return result;
76
+ };
77
+ exports.evaluateMapping = evaluateMapping;
78
+ /**
79
+ * Deep-merge workflow settings, preferring non-undefined overrides.
80
+ * Nested objects are merged recursively to preserve defaults.
81
+ */
82
+ const mergeSettings = (base, override) => {
83
+ const merged = { ...(base ?? {}) };
84
+ if (!override) {
85
+ return merged;
86
+ }
87
+ for (const key of Object.keys(override)) {
88
+ const value = override[key];
89
+ const previous = merged[key];
90
+ if (isPlainObject(previous) && isPlainObject(value)) {
91
+ merged[key] = (0, exports.mergeSettings)(previous, value);
92
+ }
93
+ else if (value !== undefined) {
94
+ merged[key] = value;
95
+ }
96
+ }
97
+ return merged;
98
+ };
99
+ exports.mergeSettings = mergeSettings;