@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,52 @@
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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.withTimeout = exports.sleep = exports.toSnakeCase = exports.isSnakeCaseName = exports.assertSnakeCaseName = exports.createDeferred = exports.mergeSettings = exports.evaluateValue = exports.evaluateMapping = exports.compileValue = exports.NonDeterministicWorkflowError = exports.StepType = exports.WorkflowRunner = exports.WorkflowEventEmitter = exports.Workflow = exports.compileWorkflow = exports.BaseWorkflowContext = exports.defineAction = exports.AbstractAction = void 0;
23
+ var abstract_action_1 = require("./action/abstract-action");
24
+ Object.defineProperty(exports, "AbstractAction", { enumerable: true, get: function () { return abstract_action_1.AbstractAction; } });
25
+ var action_1 = require("./action/action");
26
+ Object.defineProperty(exports, "defineAction", { enumerable: true, get: function () { return action_1.defineAction; } });
27
+ var context_1 = require("./context");
28
+ Object.defineProperty(exports, "BaseWorkflowContext", { enumerable: true, get: function () { return context_1.BaseWorkflowContext; } });
29
+ __exportStar(require("./dsl.types"), exports);
30
+ var workflow_1 = require("./workflow");
31
+ Object.defineProperty(exports, "compileWorkflow", { enumerable: true, get: function () { return workflow_1.compileWorkflow; } });
32
+ Object.defineProperty(exports, "Workflow", { enumerable: true, get: function () { return workflow_1.Workflow; } });
33
+ Object.defineProperty(exports, "WorkflowEventEmitter", { enumerable: true, get: function () { return workflow_1.WorkflowEventEmitter; } });
34
+ Object.defineProperty(exports, "WorkflowRunner", { enumerable: true, get: function () { return workflow_1.WorkflowRunner; } });
35
+ var workflow_event_emitter_1 = require("./workflow-event-emitter");
36
+ Object.defineProperty(exports, "StepType", { enumerable: true, get: function () { return workflow_event_emitter_1.StepType; } });
37
+ var runner_runtime_control_1 = require("./runner-runtime-control");
38
+ Object.defineProperty(exports, "NonDeterministicWorkflowError", { enumerable: true, get: function () { return runner_runtime_control_1.NonDeterministicWorkflowError; } });
39
+ var workflow_values_1 = require("./workflow-values");
40
+ Object.defineProperty(exports, "compileValue", { enumerable: true, get: function () { return workflow_values_1.compileValue; } });
41
+ Object.defineProperty(exports, "evaluateMapping", { enumerable: true, get: function () { return workflow_values_1.evaluateMapping; } });
42
+ Object.defineProperty(exports, "evaluateValue", { enumerable: true, get: function () { return workflow_values_1.evaluateValue; } });
43
+ Object.defineProperty(exports, "mergeSettings", { enumerable: true, get: function () { return workflow_values_1.mergeSettings; } });
44
+ var deferred_1 = require("./utils/deferred");
45
+ Object.defineProperty(exports, "createDeferred", { enumerable: true, get: function () { return deferred_1.createDeferred; } });
46
+ var naming_1 = require("./utils/naming");
47
+ Object.defineProperty(exports, "assertSnakeCaseName", { enumerable: true, get: function () { return naming_1.assertSnakeCaseName; } });
48
+ Object.defineProperty(exports, "isSnakeCaseName", { enumerable: true, get: function () { return naming_1.isSnakeCaseName; } });
49
+ Object.defineProperty(exports, "toSnakeCase", { enumerable: true, get: function () { return naming_1.toSnakeCase; } });
50
+ var timeout_1 = require("./utils/timeout");
51
+ Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return timeout_1.sleep; } });
52
+ Object.defineProperty(exports, "withTimeout", { enumerable: true, get: function () { return timeout_1.withTimeout; } });
@@ -0,0 +1,343 @@
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.RunnerRuntimeControl = exports.NonDeterministicWorkflowError = void 0;
9
+ const deferred_1 = require("./utils/deferred");
10
+ const INDEX_KEY_PREFIX = 'index:';
11
+ const USER_KEY_PREFIX = 'key:';
12
+ class NonDeterministicWorkflowError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'NonDeterministicWorkflowError';
16
+ }
17
+ }
18
+ exports.NonDeterministicWorkflowError = NonDeterministicWorkflowError;
19
+ /** Minimal wrapper that exposes runner controls to actions via the context. */
20
+ class RunnerRuntimeControl {
21
+ constructor(runner) {
22
+ this.pendingSuspensions = new Map();
23
+ this.suspensionWaiters = new Map();
24
+ this.primedResumeData = new Map();
25
+ this.stepAttempts = new Map();
26
+ this.activeStepExecutions = new Map();
27
+ this.replaySeeds = new Map();
28
+ this.runner = runner;
29
+ }
30
+ get status() {
31
+ return this.runner.getStatus();
32
+ }
33
+ get resumeData() {
34
+ return this.runner.getLastResumeData();
35
+ }
36
+ suspend(options) {
37
+ const currentStep = this.runner.getCurrentStep();
38
+ if (!currentStep) {
39
+ throw new Error('workflow.suspend() can only be called while a workflow step is running.');
40
+ }
41
+ const execution = this.ensureStepExecution(currentStep.id);
42
+ execution.suspendCursor += 1;
43
+ const suspendIndex = execution.suspendCursor;
44
+ const suspendKey = buildSuspendKey(suspendIndex, options?.key);
45
+ this.assertReplayExpectation(execution, {
46
+ suspendIndex,
47
+ suspendKey,
48
+ reason: options?.reason,
49
+ });
50
+ if (execution.awaitResults.has(suspendKey)) {
51
+ return Promise.resolve(execution.awaitResults.get(suspendKey));
52
+ }
53
+ const primed = this.dequeuePrimedResumeData(currentStep.id);
54
+ if (primed.found) {
55
+ return Promise.resolve(primed.value);
56
+ }
57
+ const request = {
58
+ stepId: currentStep.id,
59
+ stepExecId: execution.stepExecId,
60
+ suspendIndex,
61
+ suspendKey,
62
+ reason: options?.reason,
63
+ data: options?.data,
64
+ awaitResults: this.serializeAwaitResults(execution.awaitResults),
65
+ resume: (0, deferred_1.createDeferred)(),
66
+ };
67
+ this.enqueueSuspension(request);
68
+ return request.resume.promise;
69
+ }
70
+ resume(data) {
71
+ void this.runner.resume({ resumeData: data });
72
+ }
73
+ waitForStepSuspension(stepId) {
74
+ const queued = this.dequeueStepSuspension(stepId);
75
+ if (queued) {
76
+ return Promise.resolve(queued);
77
+ }
78
+ return new Promise((resolve) => {
79
+ const waiters = this.suspensionWaiters.get(stepId) ?? [];
80
+ waiters.push(resolve);
81
+ this.suspensionWaiters.set(stepId, waiters);
82
+ });
83
+ }
84
+ beginStepExecution(stepId) {
85
+ const existing = this.activeStepExecutions.get(stepId);
86
+ if (existing) {
87
+ return existing.stepExecId;
88
+ }
89
+ const seeded = this.replaySeeds.get(stepId);
90
+ if (seeded) {
91
+ const execution = {
92
+ stepId,
93
+ stepExecId: seeded.stepExecId,
94
+ suspendCursor: 0,
95
+ awaitResults: this.normalizeAwaitResults(seeded.awaitResults),
96
+ replayExpectation: seeded.activeSuspension
97
+ ? {
98
+ suspendIndex: seeded.activeSuspension.suspendIndex,
99
+ suspendKey: normalizeSuspendKey(seeded.activeSuspension.suspendIndex, seeded.activeSuspension.suspendKey) ?? buildSuspendKey(seeded.activeSuspension.suspendIndex ?? 1),
100
+ reason: seeded.activeSuspension.reason,
101
+ matched: false,
102
+ }
103
+ : undefined,
104
+ };
105
+ this.activeStepExecutions.set(stepId, execution);
106
+ this.replaySeeds.delete(stepId);
107
+ this.bumpStepAttemptCounter(stepId, execution.stepExecId);
108
+ return execution.stepExecId;
109
+ }
110
+ const attempt = (this.stepAttempts.get(stepId) ?? 0) + 1;
111
+ const stepExecId = `${stepId}#${attempt}`;
112
+ this.stepAttempts.set(stepId, attempt);
113
+ this.activeStepExecutions.set(stepId, {
114
+ stepId,
115
+ stepExecId,
116
+ suspendCursor: 0,
117
+ awaitResults: new Map(),
118
+ });
119
+ return stepExecId;
120
+ }
121
+ prepareStepReplay(seed) {
122
+ const normalizedAwaitResults = this.serializeAwaitResults(this.normalizeAwaitResults(seed.awaitResults ?? {}));
123
+ this.replaySeeds.set(seed.stepId, {
124
+ stepExecId: seed.stepExecId,
125
+ awaitResults: normalizedAwaitResults,
126
+ activeSuspension: seed.activeSuspension
127
+ ? {
128
+ suspendIndex: seed.activeSuspension.suspendIndex,
129
+ suspendKey: normalizeSuspendKey(seed.activeSuspension.suspendIndex, seed.activeSuspension.suspendKey),
130
+ reason: seed.activeSuspension.reason,
131
+ }
132
+ : undefined,
133
+ });
134
+ this.bumpStepAttemptCounter(seed.stepId, seed.stepExecId);
135
+ }
136
+ recordStepSuspendResult(params) {
137
+ const suspendKey = normalizeSuspendKey(params.suspendIndex, params.suspendKey);
138
+ if (!suspendKey) {
139
+ this.primeStepResumeData(params.stepId, params.resumeData);
140
+ return;
141
+ }
142
+ const activeExecution = this.activeStepExecutions.get(params.stepId);
143
+ if (activeExecution &&
144
+ (!params.stepExecId || params.stepExecId === activeExecution.stepExecId)) {
145
+ if (!activeExecution.awaitResults.has(suspendKey)) {
146
+ activeExecution.awaitResults.set(suspendKey, params.resumeData);
147
+ }
148
+ return;
149
+ }
150
+ const replaySeed = this.replaySeeds.get(params.stepId);
151
+ if (replaySeed) {
152
+ if (params.stepExecId && replaySeed.stepExecId !== params.stepExecId) {
153
+ return;
154
+ }
155
+ replaySeed.awaitResults[suspendKey] =
156
+ replaySeed.awaitResults[suspendKey] ?? params.resumeData;
157
+ this.replaySeeds.set(params.stepId, replaySeed);
158
+ return;
159
+ }
160
+ const fallbackExecId = params.stepExecId ?? `${params.stepId}#1`;
161
+ this.replaySeeds.set(params.stepId, {
162
+ stepExecId: fallbackExecId,
163
+ awaitResults: { [suspendKey]: params.resumeData },
164
+ });
165
+ this.bumpStepAttemptCounter(params.stepId, fallbackExecId);
166
+ }
167
+ clearStepSuspensions(stepId, error) {
168
+ const queued = this.pendingSuspensions.get(stepId);
169
+ if (queued) {
170
+ for (const request of queued) {
171
+ request.resume.reject(error ?? new Error(`Suspension for step "${stepId}" was cancelled.`));
172
+ }
173
+ }
174
+ const execution = this.activeStepExecutions.get(stepId);
175
+ this.pendingSuspensions.delete(stepId);
176
+ this.suspensionWaiters.delete(stepId);
177
+ this.primedResumeData.delete(stepId);
178
+ this.activeStepExecutions.delete(stepId);
179
+ if (!error &&
180
+ execution?.replayExpectation &&
181
+ !execution.replayExpectation.matched) {
182
+ throw new NonDeterministicWorkflowError(`Replay for step "${stepId}" did not reach expected suspension ` +
183
+ `"${execution.replayExpectation.suspendKey}".`);
184
+ }
185
+ }
186
+ primeStepResumeData(stepId, resumeData) {
187
+ const queued = this.primedResumeData.get(stepId) ?? [];
188
+ queued.push(resumeData);
189
+ this.primedResumeData.set(stepId, queued);
190
+ }
191
+ getSnapshot() {
192
+ return this.runner.getSnapshot();
193
+ }
194
+ enqueueSuspension(request) {
195
+ const waiters = this.suspensionWaiters.get(request.stepId);
196
+ if (waiters && waiters.length > 0) {
197
+ const resolve = waiters.shift();
198
+ if (!resolve) {
199
+ return;
200
+ }
201
+ if (waiters.length === 0) {
202
+ this.suspensionWaiters.delete(request.stepId);
203
+ }
204
+ else {
205
+ this.suspensionWaiters.set(request.stepId, waiters);
206
+ }
207
+ resolve(request);
208
+ return;
209
+ }
210
+ const queued = this.pendingSuspensions.get(request.stepId) ?? [];
211
+ queued.push(request);
212
+ this.pendingSuspensions.set(request.stepId, queued);
213
+ }
214
+ dequeueStepSuspension(stepId) {
215
+ const queued = this.pendingSuspensions.get(stepId);
216
+ if (!queued || queued.length === 0) {
217
+ return undefined;
218
+ }
219
+ const request = queued.shift();
220
+ if (queued.length === 0) {
221
+ this.pendingSuspensions.delete(stepId);
222
+ }
223
+ else {
224
+ this.pendingSuspensions.set(stepId, queued);
225
+ }
226
+ return request;
227
+ }
228
+ dequeuePrimedResumeData(stepId) {
229
+ const queued = this.primedResumeData.get(stepId);
230
+ if (!queued || queued.length === 0) {
231
+ return { found: false };
232
+ }
233
+ const value = queued.shift();
234
+ if (queued.length === 0) {
235
+ this.primedResumeData.delete(stepId);
236
+ }
237
+ else {
238
+ this.primedResumeData.set(stepId, queued);
239
+ }
240
+ return { found: true, value };
241
+ }
242
+ ensureStepExecution(stepId) {
243
+ const existing = this.activeStepExecutions.get(stepId);
244
+ if (existing) {
245
+ return existing;
246
+ }
247
+ this.beginStepExecution(stepId);
248
+ const created = this.activeStepExecutions.get(stepId);
249
+ if (!created) {
250
+ throw new Error(`Unable to create runtime state for step "${stepId}".`);
251
+ }
252
+ return created;
253
+ }
254
+ assertReplayExpectation(execution, encountered) {
255
+ const expectation = execution.replayExpectation;
256
+ if (!expectation || expectation.matched) {
257
+ return;
258
+ }
259
+ if (encountered.suspendKey === expectation.suspendKey) {
260
+ if (expectation.reason !== undefined &&
261
+ encountered.reason !== expectation.reason) {
262
+ throw new NonDeterministicWorkflowError(`Replay for step "${execution.stepId}" reached suspend ` +
263
+ `"${encountered.suspendKey}" with reason "${encountered.reason ?? ''}", ` +
264
+ `expected reason "${expectation.reason}".`);
265
+ }
266
+ expectation.matched = true;
267
+ return;
268
+ }
269
+ if (execution.awaitResults.has(encountered.suspendKey)) {
270
+ return;
271
+ }
272
+ throw new NonDeterministicWorkflowError(`Replay for step "${execution.stepId}" reached unexpected suspend ` +
273
+ `"${encountered.suspendKey}" before expected ` +
274
+ `"${expectation.suspendKey}".`);
275
+ }
276
+ normalizeAwaitResults(awaitResults) {
277
+ const normalized = new Map();
278
+ for (const [rawKey, value] of Object.entries(awaitResults)) {
279
+ const normalizedKey = normalizeStoredSuspendKey(rawKey);
280
+ normalized.set(normalizedKey, value);
281
+ }
282
+ return normalized;
283
+ }
284
+ serializeAwaitResults(awaitResults) {
285
+ return Object.fromEntries(awaitResults.entries());
286
+ }
287
+ bumpStepAttemptCounter(stepId, stepExecId) {
288
+ const parsedAttempt = parseStepExecAttempt(stepId, stepExecId);
289
+ if (parsedAttempt === null) {
290
+ return;
291
+ }
292
+ const previous = this.stepAttempts.get(stepId) ?? 0;
293
+ if (parsedAttempt > previous) {
294
+ this.stepAttempts.set(stepId, parsedAttempt);
295
+ }
296
+ }
297
+ }
298
+ exports.RunnerRuntimeControl = RunnerRuntimeControl;
299
+ const buildSuspendKey = (suspendIndex, key) => {
300
+ if (key) {
301
+ return `${USER_KEY_PREFIX}${key}`;
302
+ }
303
+ return `${INDEX_KEY_PREFIX}${suspendIndex}`;
304
+ };
305
+ const normalizeSuspendKey = (suspendIndex, suspendKey) => {
306
+ if (typeof suspendKey === 'string' && suspendKey.length > 0) {
307
+ if (suspendKey.startsWith(INDEX_KEY_PREFIX) ||
308
+ suspendKey.startsWith(USER_KEY_PREFIX)) {
309
+ return suspendKey;
310
+ }
311
+ if (/^\d+$/.test(suspendKey)) {
312
+ return `${INDEX_KEY_PREFIX}${suspendKey}`;
313
+ }
314
+ return `${USER_KEY_PREFIX}${suspendKey}`;
315
+ }
316
+ if (typeof suspendIndex === 'number' &&
317
+ Number.isInteger(suspendIndex) &&
318
+ suspendIndex > 0) {
319
+ return `${INDEX_KEY_PREFIX}${suspendIndex}`;
320
+ }
321
+ return undefined;
322
+ };
323
+ const normalizeStoredSuspendKey = (rawKey) => {
324
+ if (rawKey.startsWith(INDEX_KEY_PREFIX) ||
325
+ rawKey.startsWith(USER_KEY_PREFIX)) {
326
+ return rawKey;
327
+ }
328
+ if (/^\d+$/.test(rawKey)) {
329
+ return `${INDEX_KEY_PREFIX}${rawKey}`;
330
+ }
331
+ return `${USER_KEY_PREFIX}${rawKey}`;
332
+ };
333
+ const parseStepExecAttempt = (stepId, stepExecId) => {
334
+ const prefix = `${stepId}#`;
335
+ if (!stepExecId.startsWith(prefix)) {
336
+ return null;
337
+ }
338
+ const numeric = Number.parseInt(stepExecId.slice(prefix.length), 10);
339
+ if (Number.isNaN(numeric) || numeric < 1) {
340
+ return null;
341
+ }
342
+ return numeric;
343
+ };
@@ -0,0 +1,63 @@
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.executeConditional = executeConditional;
9
+ const workflow_values_1 = require("../workflow-values");
10
+ const skip_helpers_1 = require("./skip-helpers");
11
+ /**
12
+ * Evaluate a conditional step by checking branches in order and executing the first match.
13
+ *
14
+ * @param env Executor environment providing helpers and workflow context.
15
+ * @param step The conditional step definition.
16
+ * @param state Mutable workflow execution state.
17
+ * @param path Path tokens locating this step within the workflow tree.
18
+ * @returns A suspension if a branch pauses execution, otherwise void.
19
+ */
20
+ async function executeConditional(env, step, state, path) {
21
+ for (let index = 0; index < step.branches.length; index += 1) {
22
+ const branch = step.branches[index];
23
+ const scope = {
24
+ input: state.input,
25
+ context: env.context.state,
26
+ output: state.output,
27
+ iteration: state.iteration,
28
+ accumulator: state.accumulator,
29
+ };
30
+ const conditionResult = branch.condition !== undefined
31
+ ? await (0, workflow_values_1.evaluateValue)(branch.condition, scope)
32
+ : true;
33
+ if (conditionResult) {
34
+ step.branches.forEach((candidate, candidateIndex) => {
35
+ if (candidateIndex !== index) {
36
+ (0, skip_helpers_1.markStepsSkipped)(env, candidate.steps, state.iterationStack);
37
+ }
38
+ });
39
+ const suspension = await env.executeFlow(branch.steps, state, [
40
+ ...path,
41
+ 'branch',
42
+ index,
43
+ ]);
44
+ if (suspension) {
45
+ return {
46
+ ...suspension,
47
+ continue: async (resumeData) => {
48
+ const next = await suspension.continue(resumeData);
49
+ if (next) {
50
+ return next;
51
+ }
52
+ return undefined;
53
+ },
54
+ };
55
+ }
56
+ return undefined;
57
+ }
58
+ }
59
+ if (step.branches.length > 0) {
60
+ step.branches.forEach((branch) => (0, skip_helpers_1.markStepsSkipped)(env, branch.steps, state.iterationStack));
61
+ }
62
+ return undefined;
63
+ }
@@ -0,0 +1,187 @@
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.executeLoop = executeLoop;
9
+ exports.updateAccumulator = updateAccumulator;
10
+ exports.shouldStopLoop = shouldStopLoop;
11
+ const workflow_values_1 = require("../workflow-values");
12
+ /**
13
+ * Execute a loop step by iterating over input items and executing child steps.
14
+ *
15
+ * @param env Executor environment with helpers and workflow context.
16
+ * @param step The loop step configuration.
17
+ * @param state Mutable workflow execution state.
18
+ * @param path Path tokens locating this loop within the workflow.
19
+ * @param startIndex Index to resume iteration from when continuing.
20
+ * @returns A suspension if a child step pauses execution, otherwise void.
21
+ */
22
+ async function executeLoop(env, step, state, path, startIndex = 0) {
23
+ if (step.loopType === 'for_each') {
24
+ return executeForEachLoop(env, step, state, path, startIndex);
25
+ }
26
+ return executeWhileLoop(env, step, state, path, startIndex);
27
+ }
28
+ async function executeForEachLoop(env, step, state, path, startIndex) {
29
+ const scope = {
30
+ input: state.input,
31
+ context: env.context.state,
32
+ output: state.output,
33
+ iteration: state.iteration,
34
+ accumulator: state.accumulator,
35
+ };
36
+ const items = await (0, workflow_values_1.evaluateValue)(step.forEach.in, scope);
37
+ const iterable = Array.isArray(items) ? items : [];
38
+ let accumulator = step.accumulate?.initial ?? state.accumulator;
39
+ for (let index = startIndex; index < iterable.length; index += 1) {
40
+ const item = iterable[index];
41
+ const iterationState = {
42
+ ...state,
43
+ iteration: { item, index },
44
+ accumulator,
45
+ iterationStack: [...state.iterationStack, index],
46
+ };
47
+ const suspension = await env.executeFlow(step.steps, iterationState, [
48
+ ...path,
49
+ index,
50
+ ]);
51
+ if (suspension) {
52
+ return {
53
+ ...suspension,
54
+ continue: async (resumeData) => {
55
+ const next = await suspension.continue(resumeData);
56
+ if (next) {
57
+ return next;
58
+ }
59
+ const postScope = buildScope(env, iterationState, { item, index }, accumulator);
60
+ accumulator = await updateAccumulator(step, postScope, accumulator);
61
+ const shouldStop = await shouldStopLoop(step, postScope);
62
+ if (shouldStop) {
63
+ state.accumulator = accumulator;
64
+ if (step.accumulate && step.name) {
65
+ state.output[step.name] = { [step.accumulate.as]: accumulator };
66
+ }
67
+ return undefined;
68
+ }
69
+ return executeForEachLoop(env, step, {
70
+ ...iterationState,
71
+ accumulator,
72
+ iterationStack: state.iterationStack,
73
+ }, path, index + 1);
74
+ },
75
+ };
76
+ }
77
+ const postScope = buildScope(env, iterationState, { item, index }, accumulator);
78
+ accumulator = await updateAccumulator(step, postScope, accumulator);
79
+ const shouldStop = await shouldStopLoop(step, postScope);
80
+ if (shouldStop) {
81
+ break;
82
+ }
83
+ state.output = iterationState.output;
84
+ }
85
+ finalizeAccumulatorState(step, state, accumulator);
86
+ return undefined;
87
+ }
88
+ async function executeWhileLoop(env, step, state, path, startIndex) {
89
+ let accumulator = step.accumulate?.initial ?? state.accumulator;
90
+ for (let index = startIndex;; index += 1) {
91
+ const conditionScope = buildScope(env, state, { item: undefined, index }, accumulator);
92
+ const shouldContinue = await (0, workflow_values_1.evaluateValue)(step.while, conditionScope);
93
+ if (!Boolean(shouldContinue)) {
94
+ break;
95
+ }
96
+ const iterationState = {
97
+ ...state,
98
+ iteration: { item: undefined, index },
99
+ accumulator,
100
+ iterationStack: [...state.iterationStack, index],
101
+ };
102
+ const suspension = await env.executeFlow(step.steps, iterationState, [
103
+ ...path,
104
+ index,
105
+ ]);
106
+ if (suspension) {
107
+ return {
108
+ ...suspension,
109
+ continue: async (resumeData) => {
110
+ const next = await suspension.continue(resumeData);
111
+ if (next) {
112
+ return next;
113
+ }
114
+ const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
115
+ accumulator = await updateAccumulator(step, postScope, accumulator);
116
+ return executeWhileLoop(env, step, {
117
+ ...iterationState,
118
+ accumulator,
119
+ iterationStack: state.iterationStack,
120
+ }, path, index + 1);
121
+ },
122
+ };
123
+ }
124
+ const postScope = buildScope(env, iterationState, { item: undefined, index }, accumulator);
125
+ accumulator = await updateAccumulator(step, postScope, accumulator);
126
+ state.output = iterationState.output;
127
+ }
128
+ finalizeAccumulatorState(step, state, accumulator);
129
+ return undefined;
130
+ }
131
+ function finalizeAccumulatorState(step, state, accumulator) {
132
+ if (step.accumulate && step.name) {
133
+ state.output[step.name] = { [step.accumulate.as]: accumulator };
134
+ }
135
+ if (step.accumulate) {
136
+ state.accumulator = accumulator;
137
+ }
138
+ }
139
+ /**
140
+ * Update the loop accumulator using the configured merge expression.
141
+ *
142
+ * @param step The loop step containing accumulation settings.
143
+ * @param scope Current evaluation scope for expressions.
144
+ * @param previous Previous accumulator value to merge with.
145
+ * @returns The updated accumulator value.
146
+ */
147
+ async function updateAccumulator(step, scope, previous) {
148
+ if (!step.accumulate) {
149
+ return previous;
150
+ }
151
+ return (0, workflow_values_1.evaluateValue)(step.accumulate.merge, {
152
+ ...scope,
153
+ accumulator: previous,
154
+ });
155
+ }
156
+ /**
157
+ * Determine whether loop execution should stop based on the `until` condition.
158
+ *
159
+ * @param step The loop step configuration.
160
+ * @param scope Current evaluation scope for expressions.
161
+ * @returns True if the loop should stop, otherwise false.
162
+ */
163
+ async function shouldStopLoop(step, scope) {
164
+ if (step.loopType !== 'for_each' || !step.until) {
165
+ return false;
166
+ }
167
+ const result = await (0, workflow_values_1.evaluateValue)(step.until, scope);
168
+ return Boolean(result);
169
+ }
170
+ /**
171
+ * Build the evaluation scope for loop iteration and accumulator updates.
172
+ *
173
+ * @param env Executor environment with workflow context.
174
+ * @param state Execution state for the current iteration.
175
+ * @param iteration The current iteration item and index.
176
+ * @param accumulator Accumulator value to expose to expressions.
177
+ * @returns An evaluation scope object.
178
+ */
179
+ function buildScope(env, state, iteration, accumulator) {
180
+ return {
181
+ input: state.input,
182
+ context: env.context.state,
183
+ output: state.output,
184
+ iteration,
185
+ accumulator,
186
+ };
187
+ }