@emmvish/stable-request 2.8.4 → 3.0.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 (138) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1153 -2319
  3. package/dist/constants/index.d.ts +0 -10
  4. package/dist/constants/index.d.ts.map +1 -1
  5. package/dist/constants/index.js +0 -113
  6. package/dist/constants/index.js.map +1 -1
  7. package/dist/core/index.d.ts +0 -5
  8. package/dist/core/index.d.ts.map +1 -1
  9. package/dist/core/index.js +0 -5
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/core/stable-request.d.ts.map +1 -1
  12. package/dist/core/stable-request.js +22 -7
  13. package/dist/core/stable-request.js.map +1 -1
  14. package/dist/enums/index.d.ts +0 -37
  15. package/dist/enums/index.d.ts.map +1 -1
  16. package/dist/enums/index.js +0 -43
  17. package/dist/enums/index.js.map +1 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +18 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/types/index.d.ts +100 -1135
  23. package/dist/types/index.d.ts.map +1 -1
  24. package/dist/utilities/index.d.ts +0 -18
  25. package/dist/utilities/index.d.ts.map +1 -1
  26. package/dist/utilities/index.js +0 -18
  27. package/dist/utilities/index.js.map +1 -1
  28. package/dist/utilities/infrastructure-persistence.d.ts +0 -1
  29. package/dist/utilities/infrastructure-persistence.d.ts.map +1 -1
  30. package/dist/utilities/infrastructure-persistence.js +12 -15
  31. package/dist/utilities/infrastructure-persistence.js.map +1 -1
  32. package/dist/utilities/metrics-aggregator.d.ts +2 -13
  33. package/dist/utilities/metrics-aggregator.d.ts.map +1 -1
  34. package/dist/utilities/metrics-aggregator.js +9 -251
  35. package/dist/utilities/metrics-aggregator.js.map +1 -1
  36. package/dist/utilities/metrics-validator.d.ts +6 -76
  37. package/dist/utilities/metrics-validator.d.ts.map +1 -1
  38. package/dist/utilities/metrics-validator.js +12 -181
  39. package/dist/utilities/metrics-validator.js.map +1 -1
  40. package/dist/utilities/validate-trial-mode-probabilities.js +2 -2
  41. package/dist/utilities/validate-trial-mode-probabilities.js.map +1 -1
  42. package/package.json +20 -24
  43. package/dist/core/stable-api-gateway.d.ts +0 -4
  44. package/dist/core/stable-api-gateway.d.ts.map +0 -1
  45. package/dist/core/stable-api-gateway.js +0 -136
  46. package/dist/core/stable-api-gateway.js.map +0 -1
  47. package/dist/core/stable-function.d.ts +0 -11
  48. package/dist/core/stable-function.d.ts.map +0 -1
  49. package/dist/core/stable-function.js +0 -340
  50. package/dist/core/stable-function.js.map +0 -1
  51. package/dist/core/stable-scheduler.d.ts +0 -71
  52. package/dist/core/stable-scheduler.d.ts.map +0 -1
  53. package/dist/core/stable-scheduler.js +0 -768
  54. package/dist/core/stable-scheduler.js.map +0 -1
  55. package/dist/core/stable-workflow-graph.d.ts +0 -3
  56. package/dist/core/stable-workflow-graph.d.ts.map +0 -1
  57. package/dist/core/stable-workflow-graph.js +0 -5
  58. package/dist/core/stable-workflow-graph.js.map +0 -1
  59. package/dist/core/stable-workflow.d.ts +0 -3
  60. package/dist/core/stable-workflow.d.ts.map +0 -1
  61. package/dist/core/stable-workflow.js +0 -362
  62. package/dist/core/stable-workflow.js.map +0 -1
  63. package/dist/stable-runner/index.d.ts +0 -2
  64. package/dist/stable-runner/index.d.ts.map +0 -1
  65. package/dist/stable-runner/index.js +0 -324
  66. package/dist/stable-runner/index.js.map +0 -1
  67. package/dist/utilities/concurrency-limiter.d.ts +0 -46
  68. package/dist/utilities/concurrency-limiter.d.ts.map +0 -1
  69. package/dist/utilities/concurrency-limiter.js +0 -172
  70. package/dist/utilities/concurrency-limiter.js.map +0 -1
  71. package/dist/utilities/execute-branch-workflow.d.ts +0 -3
  72. package/dist/utilities/execute-branch-workflow.d.ts.map +0 -1
  73. package/dist/utilities/execute-branch-workflow.js +0 -730
  74. package/dist/utilities/execute-branch-workflow.js.map +0 -1
  75. package/dist/utilities/execute-concurrently.d.ts +0 -3
  76. package/dist/utilities/execute-concurrently.d.ts.map +0 -1
  77. package/dist/utilities/execute-concurrently.js +0 -258
  78. package/dist/utilities/execute-concurrently.js.map +0 -1
  79. package/dist/utilities/execute-gateway-item.d.ts +0 -6
  80. package/dist/utilities/execute-gateway-item.d.ts.map +0 -1
  81. package/dist/utilities/execute-gateway-item.js +0 -127
  82. package/dist/utilities/execute-gateway-item.js.map +0 -1
  83. package/dist/utilities/execute-non-linear-workflow.d.ts +0 -3
  84. package/dist/utilities/execute-non-linear-workflow.d.ts.map +0 -1
  85. package/dist/utilities/execute-non-linear-workflow.js +0 -483
  86. package/dist/utilities/execute-non-linear-workflow.js.map +0 -1
  87. package/dist/utilities/execute-phase.d.ts +0 -3
  88. package/dist/utilities/execute-phase.d.ts.map +0 -1
  89. package/dist/utilities/execute-phase.js +0 -129
  90. package/dist/utilities/execute-phase.js.map +0 -1
  91. package/dist/utilities/execute-sequentially.d.ts +0 -3
  92. package/dist/utilities/execute-sequentially.d.ts.map +0 -1
  93. package/dist/utilities/execute-sequentially.js +0 -49
  94. package/dist/utilities/execute-sequentially.js.map +0 -1
  95. package/dist/utilities/execute-with-timeout.d.ts +0 -6
  96. package/dist/utilities/execute-with-timeout.d.ts.map +0 -1
  97. package/dist/utilities/execute-with-timeout.js +0 -28
  98. package/dist/utilities/execute-with-timeout.js.map +0 -1
  99. package/dist/utilities/execute-workflow-graph.d.ts +0 -3
  100. package/dist/utilities/execute-workflow-graph.d.ts.map +0 -1
  101. package/dist/utilities/execute-workflow-graph.js +0 -429
  102. package/dist/utilities/execute-workflow-graph.js.map +0 -1
  103. package/dist/utilities/extract-common-request-config-options.d.ts +0 -3
  104. package/dist/utilities/extract-common-request-config-options.d.ts.map +0 -1
  105. package/dist/utilities/extract-common-request-config-options.js +0 -12
  106. package/dist/utilities/extract-common-request-config-options.js.map +0 -1
  107. package/dist/utilities/fn-exec.d.ts +0 -3
  108. package/dist/utilities/fn-exec.d.ts.map +0 -1
  109. package/dist/utilities/fn-exec.js +0 -66
  110. package/dist/utilities/fn-exec.js.map +0 -1
  111. package/dist/utilities/function-cache-manager.d.ts +0 -32
  112. package/dist/utilities/function-cache-manager.d.ts.map +0 -1
  113. package/dist/utilities/function-cache-manager.js +0 -172
  114. package/dist/utilities/function-cache-manager.js.map +0 -1
  115. package/dist/utilities/prepare-api-function-options.d.ts +0 -3
  116. package/dist/utilities/prepare-api-function-options.d.ts.map +0 -1
  117. package/dist/utilities/prepare-api-function-options.js +0 -51
  118. package/dist/utilities/prepare-api-function-options.js.map +0 -1
  119. package/dist/utilities/prepare-api-request-data.d.ts +0 -3
  120. package/dist/utilities/prepare-api-request-data.d.ts.map +0 -1
  121. package/dist/utilities/prepare-api-request-data.js +0 -15
  122. package/dist/utilities/prepare-api-request-data.js.map +0 -1
  123. package/dist/utilities/prepare-api-request-options.d.ts +0 -3
  124. package/dist/utilities/prepare-api-request-options.d.ts.map +0 -1
  125. package/dist/utilities/prepare-api-request-options.js +0 -22
  126. package/dist/utilities/prepare-api-request-options.js.map +0 -1
  127. package/dist/utilities/rate-limiter.d.ts +0 -49
  128. package/dist/utilities/rate-limiter.d.ts.map +0 -1
  129. package/dist/utilities/rate-limiter.js +0 -197
  130. package/dist/utilities/rate-limiter.js.map +0 -1
  131. package/dist/utilities/validate-workflow-graph.d.ts +0 -7
  132. package/dist/utilities/validate-workflow-graph.d.ts.map +0 -1
  133. package/dist/utilities/validate-workflow-graph.js +0 -235
  134. package/dist/utilities/validate-workflow-graph.js.map +0 -1
  135. package/dist/utilities/workflow-graph-builder.d.ts +0 -37
  136. package/dist/utilities/workflow-graph-builder.d.ts.map +0 -1
  137. package/dist/utilities/workflow-graph-builder.js +0 -225
  138. package/dist/utilities/workflow-graph-builder.js.map +0 -1
@@ -1,730 +0,0 @@
1
- import { executeNonLinearWorkflow } from './execute-non-linear-workflow.js';
2
- import { executeWithPersistence } from './execute-with-persistence.js';
3
- import { formatLogContext } from './format-log-context.js';
4
- import { MetricsAggregator } from './metrics-aggregator.js';
5
- import { MetricsValidator } from './metrics-validator.js';
6
- import { PHASE_DECISION_ACTIONS } from '../enums/index.js';
7
- export async function executeBranchWorkflow(context) {
8
- const { branches, workflowId, commonGatewayOptions, requestGroups, logPhaseResults, handlePhaseCompletion, handlePhaseError = () => { }, handleBranchCompletion, handleBranchDecision, preBranchExecutionHook, prePhaseExecutionHook, maxSerializableChars, workflowHookParams, sharedBuffer, stopOnFirstPhaseError, maxWorkflowIterations, enableBranchRacing = false } = context;
9
- const branchResults = [];
10
- const allPhaseResults = [];
11
- const executionHistory = [];
12
- const branchExecutionHistory = [];
13
- const branchExecutionCounts = new Map();
14
- let totalRequests = 0;
15
- let successfulRequests = 0;
16
- let failedRequests = 0;
17
- let terminatedEarly = false;
18
- let terminationReason;
19
- let iterationCount = 0;
20
- const mergeBranchConfig = (branch) => {
21
- if (!branch.commonConfig) {
22
- return commonGatewayOptions;
23
- }
24
- const merged = {
25
- ...commonGatewayOptions,
26
- ...branch.commonConfig
27
- };
28
- if (!branch.commonConfig.commonExecutionTimeout && commonGatewayOptions.commonExecutionTimeout) {
29
- merged.commonExecutionTimeout = commonGatewayOptions.commonExecutionTimeout;
30
- }
31
- return merged;
32
- };
33
- const executeSingleBranch = async (branch, branchIndex, executionNumber) => {
34
- const branchStartTime = Date.now();
35
- const branchId = branch.id || `branch-${branchIndex + 1}`;
36
- if (branch.maxTimeout) {
37
- const timeoutPromise = new Promise((_, reject) => {
38
- setTimeout(() => {
39
- const contextStr = `workflowId=${workflowId}, branchId=${branchId}`;
40
- reject(new Error(`stable-request: Branch execution exceeded maxTimeout of ${branch.maxTimeout}ms [${contextStr}]`));
41
- }, branch.maxTimeout);
42
- });
43
- const executionPromise = executeSingleBranchInternal(branch, branchIndex, executionNumber);
44
- return Promise.race([executionPromise, timeoutPromise]);
45
- }
46
- return executeSingleBranchInternal(branch, branchIndex, executionNumber);
47
- };
48
- const executeSingleBranchInternal = async (branch, branchIndex, executionNumber) => {
49
- const branchStartTime = Date.now();
50
- const branchId = branch.id || `branch-${branchIndex + 1}`;
51
- let modifiedBranch = branch;
52
- if (preBranchExecutionHook) {
53
- try {
54
- const hookOptions = {
55
- workflowId,
56
- branchId,
57
- branchIndex,
58
- branch: { ...branch },
59
- sharedBuffer,
60
- params: workflowHookParams?.preBranchExecutionHookParams
61
- };
62
- const result = await executeWithPersistence(preBranchExecutionHook, hookOptions, branch.statePersistence, { workflowId, branchId }, sharedBuffer);
63
- if (result) {
64
- modifiedBranch = result;
65
- console.info(`${formatLogContext({ workflowId, branchId })}stable-request: Branch configuration modified by preBranchExecutionHook`);
66
- }
67
- }
68
- catch (error) {
69
- console.error(`${formatLogContext({ workflowId, branchId })}stable-request: Error in preBranchExecutionHook:`, error);
70
- }
71
- }
72
- try {
73
- const branchConfig = mergeBranchConfig(modifiedBranch);
74
- const result = await executeNonLinearWorkflow({
75
- phases: modifiedBranch.phases,
76
- workflowId: `${workflowId}-branch-${branchId}`,
77
- branchId,
78
- commonGatewayOptions: branchConfig,
79
- requestGroups,
80
- logPhaseResults,
81
- handlePhaseCompletion,
82
- handlePhaseError,
83
- handlePhaseDecision: workflowHookParams?.handlePhaseDecision,
84
- prePhaseExecutionHook,
85
- maxSerializableChars,
86
- workflowHookParams,
87
- sharedBuffer,
88
- stopOnFirstPhaseError,
89
- maxWorkflowIterations
90
- });
91
- const branchExecutionTime = Date.now() - branchStartTime;
92
- const branchResult = {
93
- workflowId,
94
- branchId,
95
- branchIndex,
96
- success: result.failedRequests === 0,
97
- executionTime: branchExecutionTime,
98
- completedPhases: result.phaseResults.length,
99
- phaseResults: result.phaseResults,
100
- executionNumber
101
- };
102
- branchResult.metrics = MetricsAggregator.extractBranchMetrics(branchResult);
103
- if (modifiedBranch.metricsGuardrails && branchResult.metrics) {
104
- branchResult.validation = MetricsValidator.validateBranchMetrics(branchResult.metrics, modifiedBranch.metricsGuardrails);
105
- }
106
- return {
107
- branchResult,
108
- phaseResults: result.phaseResults,
109
- executionHistory: result.executionHistory,
110
- totalRequests: result.totalRequests,
111
- successfulRequests: result.successfulRequests,
112
- failedRequests: result.failedRequests
113
- };
114
- }
115
- catch (error) {
116
- console.error(`${formatLogContext({ workflowId })}stable-request: Branch ${branchId} failed:`, error);
117
- const errorBranchResult = {
118
- workflowId,
119
- branchId,
120
- branchIndex,
121
- success: false,
122
- executionTime: Date.now() - branchStartTime,
123
- completedPhases: 0,
124
- phaseResults: [],
125
- executionNumber,
126
- error: error?.message || 'Branch execution failed',
127
- decision: undefined
128
- };
129
- errorBranchResult.metrics = MetricsAggregator.extractBranchMetrics(errorBranchResult);
130
- if (branch.metricsGuardrails && errorBranchResult.metrics) {
131
- errorBranchResult.validation = MetricsValidator.validateBranchMetrics(errorBranchResult.metrics, branch.metricsGuardrails);
132
- }
133
- return {
134
- branchResult: errorBranchResult,
135
- phaseResults: [],
136
- executionHistory: [],
137
- totalRequests: 0,
138
- successfulRequests: 0,
139
- failedRequests: 1
140
- };
141
- }
142
- };
143
- if (enableBranchRacing) {
144
- if (logPhaseResults) {
145
- console.info(`${formatLogContext({ workflowId })}stable-request: Starting branch racing with ${branches.length} branches`);
146
- }
147
- const branchPromises = branches.map((branch, index) => executeSingleBranch(branch, index, 1)
148
- .then(result => ({ success: true, result, index, branchId: branch.id }))
149
- .catch(error => ({ success: false, error, index, branchId: branch.id })));
150
- try {
151
- const winner = await Promise.race(branchPromises);
152
- if (logPhaseResults) {
153
- console.info(`${formatLogContext({ workflowId })}stable-request: Branch '${winner.branchId}' won the race`);
154
- }
155
- if (winner.success && 'result' in winner) {
156
- const result = winner.result;
157
- branchResults.push(result.branchResult);
158
- allPhaseResults.push(...result.phaseResults);
159
- executionHistory.push(...result.executionHistory);
160
- totalRequests = result.totalRequests;
161
- successfulRequests = result.successfulRequests;
162
- failedRequests = result.failedRequests;
163
- branchExecutionHistory.push({
164
- branchId: winner.branchId,
165
- branchIndex: winner.index,
166
- executionNumber: 1,
167
- timestamp: new Date().toISOString(),
168
- success: result.branchResult.success,
169
- executionTime: result.branchResult.executionTime
170
- });
171
- if (handleBranchCompletion) {
172
- try {
173
- await executeWithPersistence(handleBranchCompletion, {
174
- workflowId,
175
- branchId: result.branchResult.branchId,
176
- branchResults: result.branchResult.phaseResults,
177
- success: result.branchResult.success,
178
- maxSerializableChars
179
- }, branches[winner.index].statePersistence, { workflowId, branchId: result.branchResult.branchId }, sharedBuffer);
180
- }
181
- catch (hookError) {
182
- console.error(`${formatLogContext({ workflowId, branchId: result.branchResult.branchId })}stable-request: Error in handleBranchCompletion hook:`, hookError);
183
- }
184
- }
185
- }
186
- else {
187
- const error = 'error' in winner ? winner.error : undefined;
188
- console.error(`${formatLogContext({ workflowId })}stable-request: Winning branch '${winner.branchId}' failed:`, error);
189
- const errorResult = {
190
- workflowId,
191
- branchId: winner.branchId,
192
- branchIndex: winner.index,
193
- success: false,
194
- executionTime: 0,
195
- completedPhases: 0,
196
- phaseResults: [],
197
- executionNumber: 1,
198
- error: error?.message || 'stable-request: Branch execution failed'
199
- };
200
- errorResult.metrics = MetricsAggregator.extractBranchMetrics(errorResult);
201
- branchResults.push(errorResult);
202
- failedRequests = 1;
203
- }
204
- for (let i = 0; i < branches.length; i++) {
205
- if (i === winner.index) {
206
- continue;
207
- }
208
- const cancelledResult = {
209
- workflowId,
210
- branchId: branches[i].id,
211
- branchIndex: i,
212
- success: false,
213
- executionTime: 0,
214
- completedPhases: 0,
215
- phaseResults: [],
216
- executionNumber: 1,
217
- skipped: true,
218
- error: 'stable-request: Cancelled - another branch won the race'
219
- };
220
- cancelledResult.metrics = MetricsAggregator.extractBranchMetrics(cancelledResult);
221
- branchResults.push(cancelledResult);
222
- branchExecutionHistory.push({
223
- branchId: branches[i].id,
224
- branchIndex: i,
225
- executionNumber: 1,
226
- timestamp: new Date().toISOString(),
227
- success: false,
228
- executionTime: 0
229
- });
230
- }
231
- return {
232
- branchResults,
233
- allPhaseResults,
234
- executionHistory,
235
- branchExecutionHistory,
236
- totalRequests,
237
- successfulRequests,
238
- failedRequests,
239
- terminatedEarly: false,
240
- terminationReason: `Branch racing completed - ${winner.branchId} won`
241
- };
242
- }
243
- catch (error) {
244
- console.error(`${formatLogContext({ workflowId })}stable-request: Branch racing failed:`, error);
245
- return {
246
- branchResults: [],
247
- allPhaseResults: [],
248
- executionHistory: [],
249
- branchExecutionHistory: [],
250
- totalRequests: 0,
251
- successfulRequests: 0,
252
- failedRequests: branches.length,
253
- terminatedEarly: true,
254
- terminationReason: 'Branch racing failed'
255
- };
256
- }
257
- }
258
- sharedBuffer;
259
- let currentBranchId = branches[0]?.id || null;
260
- while (currentBranchId !== null && iterationCount < maxWorkflowIterations) {
261
- iterationCount++;
262
- const branchIndex = branches.findIndex(b => b.id === currentBranchId);
263
- if (branchIndex === -1) {
264
- console.error(`${formatLogContext({ workflowId })}stable-request: Branch '${currentBranchId}' not found`);
265
- terminatedEarly = true;
266
- terminationReason = `Branch '${currentBranchId}' not found`;
267
- break;
268
- }
269
- const currentBranch = branches[branchIndex];
270
- const executionNumber = (branchExecutionCounts.get(currentBranchId) || 0) + 1;
271
- branchExecutionCounts.set(currentBranchId, executionNumber);
272
- const maxReplayCount = currentBranch.maxReplayCount ?? Infinity;
273
- if (executionNumber > maxReplayCount + 1) {
274
- if (logPhaseResults) {
275
- console.warn(`${formatLogContext({ workflowId })}stable-request: Branch '${currentBranchId}' exceeded max replay count (${maxReplayCount}). Skipping.`);
276
- }
277
- const skippedResult = {
278
- workflowId,
279
- branchId: currentBranchId,
280
- branchIndex,
281
- success: false,
282
- executionTime: 0,
283
- completedPhases: 0,
284
- phaseResults: [],
285
- executionNumber,
286
- skipped: true,
287
- error: `Exceeded max replay count of ${maxReplayCount}`
288
- };
289
- skippedResult.metrics = MetricsAggregator.extractBranchMetrics(skippedResult);
290
- if (currentBranch.metricsGuardrails && skippedResult.metrics) {
291
- skippedResult.validation = MetricsValidator.validateBranchMetrics(skippedResult.metrics, currentBranch.metricsGuardrails);
292
- }
293
- branchResults.push(skippedResult);
294
- branchExecutionHistory.push({
295
- branchId: currentBranchId,
296
- branchIndex,
297
- executionNumber,
298
- timestamp: new Date().toISOString(),
299
- success: false,
300
- executionTime: 0
301
- });
302
- currentBranchId = branches[branchIndex + 1]?.id || null;
303
- continue;
304
- }
305
- const isConcurrent = currentBranch.markConcurrentBranch;
306
- if (isConcurrent) {
307
- const concurrentGroup = [
308
- { branch: currentBranch, index: branchIndex, executionNumber }
309
- ];
310
- let j = branchIndex + 1;
311
- while (j < branches.length && branches[j].markConcurrentBranch) {
312
- const concurrentBranch = branches[j];
313
- const concurrentExecNum = (branchExecutionCounts.get(concurrentBranch.id) || 0) + 1;
314
- branchExecutionCounts.set(concurrentBranch.id, concurrentExecNum);
315
- concurrentGroup.push({ branch: concurrentBranch, index: j, executionNumber: concurrentExecNum });
316
- j++;
317
- }
318
- if (logPhaseResults) {
319
- const branchIds = concurrentGroup.map(({ branch }) => branch.id).join(', ');
320
- console.info(`${formatLogContext({ workflowId })}\nstable-request: Executing ${concurrentGroup.length} branches in parallel: [${branchIds}]`);
321
- }
322
- const groupPromises = concurrentGroup.map(({ branch, index, executionNumber }) => executeSingleBranch(branch, index, executionNumber));
323
- const groupResults = await Promise.all(groupPromises);
324
- const concurrentBranchResults = [];
325
- let concurrentGroupJumpTarget = null;
326
- let concurrentGroupSkipTarget = null;
327
- let shouldTerminate = false;
328
- for (let k = 0; k < groupResults.length; k++) {
329
- const result = groupResults[k];
330
- const { branch, index, executionNumber } = concurrentGroup[k];
331
- concurrentBranchResults.push(result.branchResult);
332
- branchResults.push(result.branchResult);
333
- allPhaseResults.push(...result.phaseResults);
334
- executionHistory.push(...result.executionHistory);
335
- totalRequests += result.totalRequests;
336
- successfulRequests += result.successfulRequests;
337
- failedRequests += result.failedRequests;
338
- branchExecutionHistory.push({
339
- branchId: branch.id,
340
- branchIndex: index,
341
- executionNumber,
342
- timestamp: new Date().toISOString(),
343
- success: result.branchResult.success,
344
- executionTime: result.branchResult.executionTime
345
- });
346
- if (handleBranchCompletion) {
347
- try {
348
- await executeWithPersistence(handleBranchCompletion, {
349
- workflowId,
350
- branchId: result.branchResult.branchId,
351
- branchResults: result.branchResult.phaseResults,
352
- success: result.branchResult.success,
353
- maxSerializableChars
354
- }, branch.statePersistence, { workflowId, branchId: result.branchResult.branchId }, sharedBuffer);
355
- }
356
- catch (hookError) {
357
- console.error(`${formatLogContext({ workflowId, branchId: result.branchResult.branchId })}stable-request: Error in handleBranchCompletion hook:`, hookError);
358
- }
359
- }
360
- if (branch.branchDecisionHook) {
361
- try {
362
- const decision = await executeWithPersistence(branch.branchDecisionHook, {
363
- workflowId,
364
- branchResults: result.phaseResults,
365
- branchId: branch.id,
366
- branchIndex: index,
367
- executionNumber,
368
- executionHistory: result.executionHistory,
369
- branchExecutionHistory,
370
- sharedBuffer,
371
- params: workflowHookParams?.handleBranchDecisionParams,
372
- concurrentBranchResults: concurrentBranchResults
373
- }, branch.statePersistence, { workflowId, branchId: branch.id }, sharedBuffer);
374
- result.branchResult.decision = decision;
375
- const historyRecord = branchExecutionHistory.find(h => h.branchId === branch.id && h.executionNumber === executionNumber);
376
- if (historyRecord) {
377
- historyRecord.decision = decision;
378
- }
379
- if (handleBranchDecision) {
380
- try {
381
- const wrappedHook = (options) => handleBranchDecision(options.decision, options.branchResult, options.maxSerializableChars);
382
- await executeWithPersistence(wrappedHook, { decision, branchResult: result.branchResult, maxSerializableChars }, workflowHookParams?.statePersistence, { workflowId, branchId: branch.id }, sharedBuffer);
383
- }
384
- catch (hookError) {
385
- console.error(`${formatLogContext({ workflowId, branchId: branch.id })}stable-request: Error in handleBranchDecision hook:`, hookError);
386
- }
387
- }
388
- if (k === groupResults.length - 1) {
389
- if (decision.action === PHASE_DECISION_ACTIONS.TERMINATE) {
390
- shouldTerminate = true;
391
- terminationReason = decision.metadata?.reason || `Branch ${branch.id} terminated workflow`;
392
- }
393
- else if (decision.action === PHASE_DECISION_ACTIONS.JUMP) {
394
- concurrentGroupJumpTarget = decision.targetBranchId || null;
395
- }
396
- else if (decision.action === PHASE_DECISION_ACTIONS.SKIP) {
397
- concurrentGroupSkipTarget = decision.targetBranchId || null;
398
- }
399
- if (logPhaseResults && decision.action !== PHASE_DECISION_ACTIONS.CONTINUE) {
400
- console.info(`${formatLogContext({ workflowId })}stable-request: Concurrent group decision: ${decision.action}`, decision.targetBranchId ? `-> ${decision.targetBranchId}` : '');
401
- }
402
- }
403
- }
404
- catch (decisionError) {
405
- console.error(`${formatLogContext({ workflowId, branchId: branch.id })}stable-request: Error in branch decision hook for ${branch.id}:`, decisionError);
406
- }
407
- }
408
- if (stopOnFirstPhaseError && result.failedRequests > 0) {
409
- shouldTerminate = true;
410
- terminationReason = `Branch ${branch.id} in concurrent group failed`;
411
- break;
412
- }
413
- }
414
- const lastBranchDecision = concurrentGroup[concurrentGroup.length - 1]?.branch.branchDecisionHook ?
415
- groupResults[groupResults.length - 1].branchResult.decision : undefined;
416
- if (lastBranchDecision) {
417
- if (lastBranchDecision.addBranches && Array.isArray(lastBranchDecision.addBranches) && lastBranchDecision.addBranches.length > 0) {
418
- if (logPhaseResults) {
419
- console.info(`${formatLogContext({ workflowId })}stable-request: Adding ${lastBranchDecision.addBranches.length} dynamic branch(es) after concurrent group`);
420
- }
421
- lastBranchDecision.addBranches.forEach((newBranch, idx) => {
422
- const newBranchId = newBranch.id || `dynamic-branch-${Date.now()}-${idx}`;
423
- const newBranchWithId = { ...newBranch, id: newBranchId };
424
- branches.splice(j + idx, 0, newBranchWithId);
425
- if (logPhaseResults) {
426
- console.info(`${formatLogContext({ workflowId })}stable-request: Added dynamic branch '${newBranchId}' at index ${j + idx}`);
427
- }
428
- });
429
- }
430
- if (lastBranchDecision.addPhases && Array.isArray(lastBranchDecision.addPhases) && lastBranchDecision.addPhases.length > 0) {
431
- const lastBranch = concurrentGroup[concurrentGroup.length - 1].branch;
432
- if (logPhaseResults) {
433
- console.info(`${formatLogContext({ workflowId, branchId: lastBranch.id })}stable-request: Adding ${lastBranchDecision.addPhases.length} dynamic phase(s) to branch '${lastBranch.id}'`);
434
- }
435
- lastBranchDecision.addPhases.forEach((newPhase) => {
436
- lastBranch.phases.push(newPhase);
437
- });
438
- }
439
- }
440
- if (shouldTerminate) {
441
- terminatedEarly = true;
442
- break;
443
- }
444
- if (concurrentGroupJumpTarget) {
445
- currentBranchId = concurrentGroupJumpTarget;
446
- }
447
- else if (concurrentGroupSkipTarget) {
448
- const skipTargetIndex = branches.findIndex(b => b.id === concurrentGroupSkipTarget);
449
- if (skipTargetIndex !== -1) {
450
- for (let skipIdx = j; skipIdx < skipTargetIndex; skipIdx++) {
451
- const skippedBranch = branches[skipIdx];
452
- const skippedResult = {
453
- workflowId,
454
- branchId: skippedBranch.id,
455
- branchIndex: skipIdx,
456
- success: true,
457
- executionTime: 0,
458
- completedPhases: 0,
459
- phaseResults: [],
460
- executionNumber: 1,
461
- skipped: true
462
- };
463
- skippedResult.metrics = MetricsAggregator.extractBranchMetrics(skippedResult);
464
- if (skippedBranch.metricsGuardrails && skippedResult.metrics) {
465
- skippedResult.validation = MetricsValidator.validateBranchMetrics(skippedResult.metrics, skippedBranch.metricsGuardrails);
466
- }
467
- branchResults.push(skippedResult);
468
- branchExecutionHistory.push({
469
- branchId: skippedBranch.id,
470
- branchIndex: skipIdx,
471
- executionNumber: 1,
472
- timestamp: new Date().toISOString(),
473
- success: true,
474
- executionTime: 0,
475
- decision: { action: PHASE_DECISION_ACTIONS.SKIP }
476
- });
477
- }
478
- currentBranchId = concurrentGroupSkipTarget;
479
- }
480
- else {
481
- currentBranchId = branches[j]?.id || null;
482
- }
483
- }
484
- else {
485
- currentBranchId = branches[j]?.id || null;
486
- }
487
- }
488
- else {
489
- if (logPhaseResults) {
490
- console.info(`${formatLogContext({ workflowId })}\nstable-request: Executing branch: ${currentBranch.id} (execution #${executionNumber})`);
491
- }
492
- const result = await executeSingleBranch(currentBranch, branchIndex, executionNumber);
493
- branchResults.push(result.branchResult);
494
- allPhaseResults.push(...result.phaseResults);
495
- executionHistory.push(...result.executionHistory);
496
- totalRequests += result.totalRequests;
497
- successfulRequests += result.successfulRequests;
498
- failedRequests += result.failedRequests;
499
- branchExecutionHistory.push({
500
- branchId: currentBranchId,
501
- branchIndex,
502
- executionNumber,
503
- timestamp: new Date().toISOString(),
504
- success: result.branchResult.success,
505
- executionTime: result.branchResult.executionTime
506
- });
507
- if (handleBranchCompletion) {
508
- try {
509
- await executeWithPersistence(handleBranchCompletion, {
510
- workflowId,
511
- branchId: result.branchResult.branchId,
512
- branchResults: result.branchResult.phaseResults,
513
- success: result.branchResult.success,
514
- maxSerializableChars
515
- }, currentBranch.statePersistence, { workflowId, branchId: currentBranchId }, sharedBuffer);
516
- }
517
- catch (hookError) {
518
- console.error(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Error in handleBranchCompletion hook:`, hookError);
519
- }
520
- }
521
- let decision = {
522
- action: PHASE_DECISION_ACTIONS.CONTINUE
523
- };
524
- if (currentBranch.branchDecisionHook) {
525
- try {
526
- decision = await executeWithPersistence(currentBranch.branchDecisionHook, {
527
- workflowId,
528
- branchResults: result.phaseResults,
529
- branchId: currentBranch.id,
530
- branchIndex,
531
- executionNumber,
532
- executionHistory: result.executionHistory,
533
- branchExecutionHistory,
534
- sharedBuffer,
535
- params: workflowHookParams?.handleBranchDecisionParams
536
- }, currentBranch.statePersistence, { workflowId, branchId: currentBranchId }, sharedBuffer);
537
- result.branchResult.decision = decision;
538
- const historyRecord = branchExecutionHistory.find(h => h.branchId === currentBranchId && h.executionNumber === executionNumber);
539
- if (historyRecord) {
540
- historyRecord.decision = decision;
541
- }
542
- if (logPhaseResults) {
543
- console.info(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Branch '${currentBranchId}' decision: ${decision.action}`, decision.targetBranchId ? `-> ${decision.targetBranchId}` : '');
544
- }
545
- if (handleBranchDecision) {
546
- try {
547
- const wrappedHook = (options) => handleBranchDecision(options.decision, options.branchResult, options.maxSerializableChars);
548
- await executeWithPersistence(wrappedHook, { decision, branchResult: result.branchResult, maxSerializableChars }, workflowHookParams?.statePersistence, { workflowId, branchId: currentBranchId }, sharedBuffer);
549
- }
550
- catch (hookError) {
551
- console.error(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Error in handleBranchDecision hook:`, hookError);
552
- }
553
- }
554
- }
555
- catch (decisionError) {
556
- console.error(`${formatLogContext({ workflowId, branchId: currentBranch.id })}stable-request: Error in branch decision hook for ${currentBranch.id}:`, decisionError);
557
- decision = { action: PHASE_DECISION_ACTIONS.CONTINUE };
558
- }
559
- }
560
- if (decision.addBranches && Array.isArray(decision.addBranches) && decision.addBranches.length > 0) {
561
- if (logPhaseResults) {
562
- console.info(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Adding ${decision.addBranches.length} dynamic branch(es) after '${currentBranchId}'`);
563
- }
564
- decision.addBranches.forEach((newBranch, idx) => {
565
- const newBranchId = newBranch.id || `dynamic-branch-${Date.now()}-${idx}`;
566
- const newBranchWithId = { ...newBranch, id: newBranchId };
567
- branches.splice(branchIndex + 1 + idx, 0, newBranchWithId);
568
- if (logPhaseResults) {
569
- console.info(`${formatLogContext({ workflowId })}stable-request: Added dynamic branch '${newBranchId}' at index ${branchIndex + 1 + idx}`);
570
- }
571
- });
572
- }
573
- if (decision.addPhases && Array.isArray(decision.addPhases) && decision.addPhases.length > 0) {
574
- if (logPhaseResults) {
575
- console.info(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Adding ${decision.addPhases.length} dynamic phase(s) to branch '${currentBranchId}' and re-executing`);
576
- }
577
- decision.addPhases.forEach((newPhase) => {
578
- currentBranch.phases.push(newPhase);
579
- });
580
- if (logPhaseResults) {
581
- console.info(`${formatLogContext({ workflowId })}\nstable-request: Re-executing branch: ${currentBranch.id} with ${decision.addPhases.length} additional phase(s) (execution #${executionNumber + 1})`);
582
- }
583
- const reExecutionResult = await executeSingleBranch(currentBranch, branchIndex, executionNumber + 1);
584
- branchResults[branchResults.length - 1] = reExecutionResult.branchResult;
585
- allPhaseResults.push(...reExecutionResult.phaseResults);
586
- executionHistory.push(...reExecutionResult.executionHistory);
587
- totalRequests += reExecutionResult.totalRequests;
588
- successfulRequests += reExecutionResult.successfulRequests;
589
- failedRequests += reExecutionResult.failedRequests;
590
- branchExecutionHistory.push({
591
- branchId: currentBranchId,
592
- branchIndex,
593
- executionNumber: executionNumber + 1,
594
- timestamp: new Date().toISOString(),
595
- success: reExecutionResult.branchResult.success,
596
- executionTime: reExecutionResult.branchResult.executionTime
597
- });
598
- result.branchResult = reExecutionResult.branchResult;
599
- result.phaseResults = reExecutionResult.phaseResults;
600
- }
601
- switch (decision.action) {
602
- case PHASE_DECISION_ACTIONS.TERMINATE:
603
- terminatedEarly = true;
604
- terminationReason = decision.metadata?.reason || `Branch ${currentBranchId} terminated workflow`;
605
- currentBranchId = null;
606
- break;
607
- case PHASE_DECISION_ACTIONS.JUMP:
608
- if (decision.targetBranchId) {
609
- const targetIndex = branches.findIndex(b => b.id === decision.targetBranchId);
610
- if (targetIndex === -1) {
611
- console.error(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Jump target branch '${decision.targetBranchId}' not found`);
612
- terminatedEarly = true;
613
- terminationReason = `Jump target branch '${decision.targetBranchId}' not found`;
614
- currentBranchId = null;
615
- }
616
- else {
617
- currentBranchId = decision.targetBranchId;
618
- }
619
- }
620
- else {
621
- currentBranchId = branches[branchIndex + 1]?.id || null;
622
- }
623
- break;
624
- case PHASE_DECISION_ACTIONS.SKIP:
625
- if (!currentBranch.allowSkip && currentBranch.allowSkip !== undefined) {
626
- console.warn(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Branch '${currentBranchId}' attempted to skip but allowSkip is false. Continuing normally.`);
627
- currentBranchId = branches[branchIndex + 1]?.id || null;
628
- break;
629
- }
630
- if (decision.targetBranchId) {
631
- const skipTargetIndex = branches.findIndex(b => b.id === decision.targetBranchId);
632
- if (skipTargetIndex !== -1) {
633
- for (let skipIdx = branchIndex + 1; skipIdx < skipTargetIndex; skipIdx++) {
634
- const skippedBranch = branches[skipIdx];
635
- const skippedResult = {
636
- workflowId,
637
- branchId: skippedBranch.id,
638
- branchIndex: skipIdx,
639
- success: true,
640
- executionTime: 0,
641
- completedPhases: 0,
642
- phaseResults: [],
643
- executionNumber: 1,
644
- skipped: true
645
- };
646
- branchResults.push(skippedResult);
647
- branchExecutionHistory.push({
648
- branchId: skippedBranch.id,
649
- branchIndex: skipIdx,
650
- executionNumber: 1,
651
- timestamp: new Date().toISOString(),
652
- success: true,
653
- executionTime: 0,
654
- decision: { action: PHASE_DECISION_ACTIONS.SKIP }
655
- });
656
- }
657
- currentBranchId = decision.targetBranchId;
658
- }
659
- else {
660
- currentBranchId = branches[branchIndex + 1]?.id || null;
661
- }
662
- }
663
- else {
664
- const nextBranch = branches[branchIndex + 1];
665
- if (nextBranch) {
666
- const skippedResult = {
667
- workflowId,
668
- branchId: nextBranch.id,
669
- branchIndex: branchIndex + 1,
670
- success: true,
671
- executionTime: 0,
672
- completedPhases: 0,
673
- phaseResults: [],
674
- executionNumber: 1,
675
- skipped: true
676
- };
677
- branchResults.push(skippedResult);
678
- branchExecutionHistory.push({
679
- branchId: nextBranch.id,
680
- branchIndex: branchIndex + 1,
681
- executionNumber: 1,
682
- timestamp: new Date().toISOString(),
683
- success: true,
684
- executionTime: 0,
685
- decision: { action: PHASE_DECISION_ACTIONS.SKIP }
686
- });
687
- }
688
- currentBranchId = branches[branchIndex + 2]?.id || null;
689
- }
690
- break;
691
- case PHASE_DECISION_ACTIONS.REPLAY:
692
- if (!currentBranch.allowReplay && currentBranch.allowReplay !== undefined) {
693
- console.warn(`${formatLogContext({ workflowId, branchId: currentBranchId })}stable-request: Branch '${currentBranchId}' attempted to replay but allowReplay is false. Continuing normally.`);
694
- currentBranchId = branches[branchIndex + 1]?.id || null;
695
- break;
696
- }
697
- currentBranchId = currentBranch.id;
698
- break;
699
- case PHASE_DECISION_ACTIONS.CONTINUE:
700
- default:
701
- currentBranchId = branches[branchIndex + 1]?.id || null;
702
- break;
703
- }
704
- if (stopOnFirstPhaseError && result.failedRequests > 0) {
705
- terminatedEarly = true;
706
- terminationReason = `Branch ${currentBranch.id} failed`;
707
- break;
708
- }
709
- }
710
- }
711
- if (iterationCount >= maxWorkflowIterations) {
712
- terminatedEarly = true;
713
- terminationReason = `Exceeded maximum workflow iterations (${maxWorkflowIterations})`;
714
- if (logPhaseResults) {
715
- console.warn(`${formatLogContext({ workflowId })}stable-request: ${terminationReason}`);
716
- }
717
- }
718
- return {
719
- branchResults,
720
- allPhaseResults,
721
- executionHistory,
722
- branchExecutionHistory,
723
- totalRequests,
724
- successfulRequests,
725
- failedRequests,
726
- terminatedEarly,
727
- terminationReason
728
- };
729
- }
730
- //# sourceMappingURL=execute-branch-workflow.js.map