@holoscript/engine 6.0.3 → 6.0.4
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.
- package/dist/AutoMesher-CK47F6AV.js +17 -0
- package/dist/GPUBuffers-2LHBCD7X.js +9 -0
- package/dist/WebGPUContext-TNEUYU2Y.js +11 -0
- package/dist/animation/index.cjs +38 -38
- package/dist/animation/index.d.cts +1 -1
- package/dist/animation/index.d.ts +1 -1
- package/dist/animation/index.js +1 -1
- package/dist/audio/index.cjs +16 -6
- package/dist/audio/index.d.cts +1 -1
- package/dist/audio/index.d.ts +1 -1
- package/dist/audio/index.js +1 -1
- package/dist/camera/index.cjs +23 -23
- package/dist/camera/index.d.cts +1 -1
- package/dist/camera/index.d.ts +1 -1
- package/dist/camera/index.js +1 -1
- package/dist/character/index.cjs +6 -4
- package/dist/character/index.js +1 -1
- package/dist/choreography/index.cjs +1194 -0
- package/dist/choreography/index.d.cts +687 -0
- package/dist/choreography/index.d.ts +687 -0
- package/dist/choreography/index.js +1156 -0
- package/dist/chunk-2CSNRI2N.js +217 -0
- package/dist/chunk-33T2WINR.js +266 -0
- package/dist/chunk-35R73OFM.js +1257 -0
- package/dist/chunk-4MMDSUNP.js +1256 -0
- package/dist/chunk-5V6HOU72.js +319 -0
- package/dist/chunk-6QOP6PYF.js +1038 -0
- package/dist/chunk-7KMJVHIL.js +8944 -0
- package/dist/chunk-7VPUC62U.js +1106 -0
- package/dist/chunk-A2Y6RCAT.js +1878 -0
- package/dist/chunk-AHM42MK6.js +8944 -0
- package/dist/chunk-BL7IDTHE.js +218 -0
- package/dist/chunk-CITOMSWL.js +10462 -0
- package/dist/chunk-CXDPKW2K.js +8944 -0
- package/dist/chunk-CXZPLD4S.js +223 -0
- package/dist/chunk-CZYJE7IH.js +5169 -0
- package/dist/chunk-D2OP7YC7.js +6325 -0
- package/dist/chunk-EDRVQHUU.js +1544 -0
- package/dist/chunk-EJSLOOW2.js +3589 -0
- package/dist/chunk-F53SFGW5.js +1878 -0
- package/dist/chunk-HCFPELPY.js +919 -0
- package/dist/chunk-HNEE36PY.js +93 -0
- package/dist/chunk-HYXNV36F.js +1256 -0
- package/dist/chunk-IB7KHVFY.js +821 -0
- package/dist/chunk-IBBO7YYG.js +690 -0
- package/dist/chunk-ILIBGINU.js +5470 -0
- package/dist/chunk-IS4MHLKN.js +5479 -0
- package/dist/chunk-JT2PFKWD.js +5479 -0
- package/dist/chunk-K4CUB4NY.js +1038 -0
- package/dist/chunk-KATDQXRJ.js +10462 -0
- package/dist/chunk-KBQE6ZFJ.js +8944 -0
- package/dist/chunk-KBVD5K7E.js +560 -0
- package/dist/chunk-KCDPVQRY.js +4088 -0
- package/dist/chunk-KN4QJPKN.js +8944 -0
- package/dist/chunk-KWJ3ROSI.js +8944 -0
- package/dist/chunk-L45VF6DD.js +919 -0
- package/dist/chunk-LY4T37YK.js +307 -0
- package/dist/chunk-MDN5WZXA.js +1544 -0
- package/dist/chunk-MGCDP6VU.js +928 -0
- package/dist/chunk-NCX7X6G2.js +8681 -0
- package/dist/chunk-OF54BPVD.js +913 -0
- package/dist/chunk-OWSN2Q3Q.js +690 -0
- package/dist/chunk-PRRB5TTA.js +406 -0
- package/dist/chunk-PXWVQF76.js +4086 -0
- package/dist/chunk-PYCOIDT2.js +812 -0
- package/dist/chunk-PZCSADOV.js +928 -0
- package/dist/chunk-Q2XBVS2K.js +1038 -0
- package/dist/chunk-QDZRXWN5.js +1776 -0
- package/dist/chunk-RNWOZ6WQ.js +913 -0
- package/dist/chunk-ROLFT4CJ.js +1693 -0
- package/dist/chunk-SLTJRZ2N.js +266 -0
- package/dist/chunk-SRUS5XSU.js +4088 -0
- package/dist/chunk-TKCA3WZ5.js +5409 -0
- package/dist/chunk-TNRMXYI2.js +1650 -0
- package/dist/chunk-TQB3GJGM.js +9763 -0
- package/dist/chunk-TUFGXG6K.js +510 -0
- package/dist/chunk-U6KMTGQJ.js +632 -0
- package/dist/chunk-VMGJQST6.js +8681 -0
- package/dist/chunk-X4F4TCG4.js +5470 -0
- package/dist/chunk-ZIFROE75.js +1544 -0
- package/dist/chunk-ZIJQYHSQ.js +1204 -0
- package/dist/combat/index.cjs +4 -4
- package/dist/combat/index.d.cts +1 -1
- package/dist/combat/index.d.ts +1 -1
- package/dist/combat/index.js +1 -1
- package/dist/ecs/index.cjs +1 -1
- package/dist/ecs/index.js +1 -1
- package/dist/environment/index.cjs +14 -14
- package/dist/environment/index.d.cts +1 -1
- package/dist/environment/index.d.ts +1 -1
- package/dist/environment/index.js +1 -1
- package/dist/gpu/index.cjs +4810 -0
- package/dist/gpu/index.js +3714 -0
- package/dist/hologram/index.cjs +27 -1
- package/dist/hologram/index.js +1 -1
- package/dist/index-B2PIsAmR.d.cts +2180 -0
- package/dist/index-B2PIsAmR.d.ts +2180 -0
- package/dist/index-BHySEPX7.d.cts +2921 -0
- package/dist/index-BJV21zuy.d.cts +341 -0
- package/dist/index-BJV21zuy.d.ts +341 -0
- package/dist/index-BQutTphC.d.cts +790 -0
- package/dist/index-ByIq2XrS.d.cts +3910 -0
- package/dist/index-BysHjDSO.d.cts +224 -0
- package/dist/index-BysHjDSO.d.ts +224 -0
- package/dist/index-CKwAJGck.d.ts +455 -0
- package/dist/index-CUl3QstQ.d.cts +3006 -0
- package/dist/index-CUl3QstQ.d.ts +3006 -0
- package/dist/index-CmYtNiI-.d.cts +953 -0
- package/dist/index-CmYtNiI-.d.ts +953 -0
- package/dist/index-CnRzWxi_.d.cts +522 -0
- package/dist/index-CnRzWxi_.d.ts +522 -0
- package/dist/index-CwRWbSC7.d.ts +2921 -0
- package/dist/index-CxKIBstO.d.ts +790 -0
- package/dist/index-DJ6-R8vh.d.cts +455 -0
- package/dist/index-DQKisbcI.d.cts +4968 -0
- package/dist/index-DQKisbcI.d.ts +4968 -0
- package/dist/index-DRT2zJez.d.ts +3910 -0
- package/dist/index-DfNLiAka.d.cts +192 -0
- package/dist/index-DfNLiAka.d.ts +192 -0
- package/dist/index-nMvkoRm8.d.cts +405 -0
- package/dist/index-nMvkoRm8.d.ts +405 -0
- package/dist/index-s9yOFU37.d.cts +604 -0
- package/dist/index-s9yOFU37.d.ts +604 -0
- package/dist/index.cjs +22966 -6960
- package/dist/index.d.cts +864 -20
- package/dist/index.d.ts +864 -20
- package/dist/index.js +3062 -48
- package/dist/input/index.cjs +1 -1
- package/dist/input/index.js +1 -1
- package/dist/orbital/index.cjs +3 -3
- package/dist/orbital/index.d.cts +1 -1
- package/dist/orbital/index.d.ts +1 -1
- package/dist/orbital/index.js +1 -1
- package/dist/particles/index.cjs +16 -16
- package/dist/particles/index.d.cts +1 -1
- package/dist/particles/index.d.ts +1 -1
- package/dist/particles/index.js +1 -1
- package/dist/physics/index.cjs +2377 -21
- package/dist/physics/index.d.cts +1 -1
- package/dist/physics/index.d.ts +1 -1
- package/dist/physics/index.js +35 -1
- package/dist/postfx/index.cjs +3491 -0
- package/dist/postfx/index.js +93 -0
- package/dist/procedural/index.cjs +1 -1
- package/dist/procedural/index.js +1 -1
- package/dist/puppeteer-5VF6KDVO.js +52197 -0
- package/dist/puppeteer-IZVZ3SG4.js +52197 -0
- package/dist/rendering/index.cjs +33 -32
- package/dist/rendering/index.d.cts +1 -1
- package/dist/rendering/index.d.ts +1 -1
- package/dist/rendering/index.js +8 -6
- package/dist/runtime/index.cjs +23 -13
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +8 -6
- package/dist/runtime/protocols/index.cjs +349 -0
- package/dist/runtime/protocols/index.js +15 -0
- package/dist/scene/index.cjs +8 -8
- package/dist/scene/index.d.cts +1 -1
- package/dist/scene/index.d.ts +1 -1
- package/dist/scene/index.js +1 -1
- package/dist/shader/index.cjs +3087 -0
- package/dist/shader/index.js +3044 -0
- package/dist/simulation/index.cjs +10680 -0
- package/dist/simulation/index.d.cts +3 -0
- package/dist/simulation/index.d.ts +3 -0
- package/dist/simulation/index.js +307 -0
- package/dist/spatial/index.cjs +2443 -0
- package/dist/spatial/index.d.cts +1545 -0
- package/dist/spatial/index.d.ts +1545 -0
- package/dist/spatial/index.js +2400 -0
- package/dist/terrain/index.cjs +1 -1
- package/dist/terrain/index.d.cts +1 -1
- package/dist/terrain/index.d.ts +1 -1
- package/dist/terrain/index.js +1 -1
- package/dist/transformers.node-4NKAPD5U.js +45620 -0
- package/dist/vm/index.cjs +7 -8
- package/dist/vm/index.d.cts +1 -1
- package/dist/vm/index.d.ts +1 -1
- package/dist/vm/index.js +1 -1
- package/dist/vm-bridge/index.cjs +2 -2
- package/dist/vm-bridge/index.d.cts +2 -2
- package/dist/vm-bridge/index.d.ts +2 -2
- package/dist/vm-bridge/index.js +1 -1
- package/dist/vr/index.cjs +6 -6
- package/dist/vr/index.js +1 -1
- package/dist/world/index.cjs +3 -3
- package/dist/world/index.d.cts +1 -1
- package/dist/world/index.d.ts +1 -1
- package/dist/world/index.js +1 -1
- package/package.json +53 -21
- package/LICENSE +0 -21
|
@@ -0,0 +1,1156 @@
|
|
|
1
|
+
import "../chunk-AKLW2MUS.js";
|
|
2
|
+
|
|
3
|
+
// src/choreography/ChoreographyTypes.ts
|
|
4
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
5
|
+
strategy: "exponential",
|
|
6
|
+
maxRetries: 3,
|
|
7
|
+
delay: 1e3,
|
|
8
|
+
backoffMultiplier: 2,
|
|
9
|
+
maxDelay: 3e4
|
|
10
|
+
};
|
|
11
|
+
var Constraints = {
|
|
12
|
+
timeout(ms, hard = true) {
|
|
13
|
+
return { type: "timeout", value: ms, hard, description: `Max ${ms}ms execution time` };
|
|
14
|
+
},
|
|
15
|
+
concurrency(max) {
|
|
16
|
+
return { type: "concurrency", value: max, description: `Max ${max} concurrent steps` };
|
|
17
|
+
},
|
|
18
|
+
trust(minLevel) {
|
|
19
|
+
return { type: "trust", value: minLevel, hard: true, description: `Min trust: ${minLevel}` };
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/choreography/StepExecutor.ts
|
|
24
|
+
import { EventEmitter } from "events";
|
|
25
|
+
var DEFAULT_EXECUTOR_CONFIG = {
|
|
26
|
+
defaultTimeout: 3e4,
|
|
27
|
+
defaultRetry: DEFAULT_RETRY_CONFIG,
|
|
28
|
+
verbose: false
|
|
29
|
+
};
|
|
30
|
+
var StepExecutor = class extends EventEmitter {
|
|
31
|
+
config;
|
|
32
|
+
actionHandler = null;
|
|
33
|
+
runningSteps = /* @__PURE__ */ new Map();
|
|
34
|
+
constructor(config = {}) {
|
|
35
|
+
super();
|
|
36
|
+
this.config = { ...DEFAULT_EXECUTOR_CONFIG, ...config };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Register the action handler for executing agent actions
|
|
40
|
+
*/
|
|
41
|
+
setActionHandler(handler) {
|
|
42
|
+
this.actionHandler = handler;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Execute a single step
|
|
46
|
+
*/
|
|
47
|
+
async execute(step, context) {
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
if (this.shouldSkip(step, context)) {
|
|
50
|
+
const reason = "Condition not met";
|
|
51
|
+
this.emit("step:skipped", step, reason);
|
|
52
|
+
return {
|
|
53
|
+
stepId: step.id,
|
|
54
|
+
success: true,
|
|
55
|
+
outputs: {},
|
|
56
|
+
duration: 0,
|
|
57
|
+
error: void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const agent = context.agents.get(step.agentId);
|
|
61
|
+
if (!agent) {
|
|
62
|
+
throw new Error(`Agent not found: ${step.agentId}`);
|
|
63
|
+
}
|
|
64
|
+
const resolvedInputs = this.resolveInputs(step.inputs, context);
|
|
65
|
+
this.emit("step:inputs:resolved", step, resolvedInputs);
|
|
66
|
+
const retryConfig = {
|
|
67
|
+
...this.config.defaultRetry,
|
|
68
|
+
...step.retry || {}
|
|
69
|
+
};
|
|
70
|
+
let lastError = null;
|
|
71
|
+
let attempt = 0;
|
|
72
|
+
while (attempt <= retryConfig.maxRetries) {
|
|
73
|
+
try {
|
|
74
|
+
step.status = "running";
|
|
75
|
+
step.startedAt = Date.now();
|
|
76
|
+
step.retryAttempt = attempt;
|
|
77
|
+
this.emit("step:executing", step, agent);
|
|
78
|
+
const abortController = new AbortController();
|
|
79
|
+
this.runningSteps.set(step.id, abortController);
|
|
80
|
+
const timeout = step.timeout || this.config.defaultTimeout;
|
|
81
|
+
const outputs = await this.executeWithTimeout(
|
|
82
|
+
step,
|
|
83
|
+
agent,
|
|
84
|
+
resolvedInputs,
|
|
85
|
+
context,
|
|
86
|
+
timeout,
|
|
87
|
+
abortController.signal
|
|
88
|
+
);
|
|
89
|
+
this.runningSteps.delete(step.id);
|
|
90
|
+
step.status = "completed";
|
|
91
|
+
step.completedAt = Date.now();
|
|
92
|
+
step.duration = step.completedAt - step.startedAt;
|
|
93
|
+
const result = {
|
|
94
|
+
stepId: step.id,
|
|
95
|
+
success: true,
|
|
96
|
+
outputs,
|
|
97
|
+
duration: step.duration,
|
|
98
|
+
retries: attempt
|
|
99
|
+
};
|
|
100
|
+
this.emit("step:completed", step, result);
|
|
101
|
+
return result;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
104
|
+
this.runningSteps.delete(step.id);
|
|
105
|
+
if (lastError.message.includes("cancelled")) {
|
|
106
|
+
step.status = "failed";
|
|
107
|
+
step.completedAt = Date.now();
|
|
108
|
+
step.duration = step.completedAt - (step.startedAt || startTime);
|
|
109
|
+
step.error = lastError.message;
|
|
110
|
+
return {
|
|
111
|
+
stepId: step.id,
|
|
112
|
+
success: false,
|
|
113
|
+
outputs: {},
|
|
114
|
+
duration: step.duration,
|
|
115
|
+
error: step.error,
|
|
116
|
+
retries: attempt
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (attempt < retryConfig.maxRetries) {
|
|
120
|
+
if (retryConfig.condition && !retryConfig.condition(lastError, attempt)) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
const delay = this.calculateDelay(retryConfig, attempt);
|
|
124
|
+
this.emit("step:retrying", step, attempt + 1, delay);
|
|
125
|
+
await this.sleep(delay);
|
|
126
|
+
}
|
|
127
|
+
attempt++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
step.status = "failed";
|
|
131
|
+
step.completedAt = Date.now();
|
|
132
|
+
step.duration = step.completedAt - (step.startedAt || startTime);
|
|
133
|
+
step.error = lastError?.message || "Unknown error";
|
|
134
|
+
this.emit("step:failed", step, lastError || new Error("Unknown error"));
|
|
135
|
+
return {
|
|
136
|
+
stepId: step.id,
|
|
137
|
+
success: false,
|
|
138
|
+
outputs: {},
|
|
139
|
+
duration: step.duration,
|
|
140
|
+
error: step.error,
|
|
141
|
+
retries: attempt - 1
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Cancel a running step
|
|
146
|
+
*/
|
|
147
|
+
cancel(stepId) {
|
|
148
|
+
const controller = this.runningSteps.get(stepId);
|
|
149
|
+
if (controller) {
|
|
150
|
+
controller.abort();
|
|
151
|
+
this.runningSteps.delete(stepId);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if any steps are running
|
|
158
|
+
*/
|
|
159
|
+
isRunning(stepId) {
|
|
160
|
+
if (stepId) {
|
|
161
|
+
return this.runningSteps.has(stepId);
|
|
162
|
+
}
|
|
163
|
+
return this.runningSteps.size > 0;
|
|
164
|
+
}
|
|
165
|
+
// ==========================================================================
|
|
166
|
+
// PRIVATE METHODS
|
|
167
|
+
// ==========================================================================
|
|
168
|
+
/**
|
|
169
|
+
* Check if step should be skipped
|
|
170
|
+
*/
|
|
171
|
+
shouldSkip(step, context) {
|
|
172
|
+
if (!step.condition) return false;
|
|
173
|
+
if (typeof step.condition === "function") {
|
|
174
|
+
return !step.condition(context);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
return !this.evaluateCondition(step.condition, context);
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Evaluate a string condition
|
|
184
|
+
*/
|
|
185
|
+
evaluateCondition(condition, context) {
|
|
186
|
+
const stepRefRegex = /step#(\w+)\.outputs\.(\w+)/g;
|
|
187
|
+
let evaluated = condition;
|
|
188
|
+
evaluated = evaluated.replace(stepRefRegex, (_, stepId, outputKey) => {
|
|
189
|
+
const outputs = context.stepOutputs.get(stepId);
|
|
190
|
+
if (outputs && outputKey in outputs) {
|
|
191
|
+
const value2 = outputs[outputKey];
|
|
192
|
+
if (typeof value2 === "string") {
|
|
193
|
+
return `"${value2}"`;
|
|
194
|
+
}
|
|
195
|
+
return String(value2);
|
|
196
|
+
}
|
|
197
|
+
return "undefined";
|
|
198
|
+
});
|
|
199
|
+
const eqMatch = evaluated.match(/^(.+)\s*==\s*(.+)$/);
|
|
200
|
+
if (eqMatch) {
|
|
201
|
+
const [, left, right] = eqMatch;
|
|
202
|
+
return this.parseValue(left.trim()) === this.parseValue(right.trim());
|
|
203
|
+
}
|
|
204
|
+
const value = this.parseValue(evaluated.trim());
|
|
205
|
+
return Boolean(value);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Parse a string value
|
|
209
|
+
*/
|
|
210
|
+
parseValue(str) {
|
|
211
|
+
if (str === "true") return true;
|
|
212
|
+
if (str === "false") return false;
|
|
213
|
+
if (str === "null") return null;
|
|
214
|
+
if (str === "undefined") return void 0;
|
|
215
|
+
if (/^-?\d+$/.test(str)) return parseInt(str, 10);
|
|
216
|
+
if (/^-?\d+\.\d+$/.test(str)) return parseFloat(str);
|
|
217
|
+
if (str.startsWith('"') && str.endsWith('"')) {
|
|
218
|
+
return str.slice(1, -1);
|
|
219
|
+
}
|
|
220
|
+
return str;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Resolve input references
|
|
224
|
+
*/
|
|
225
|
+
resolveInputs(inputs, context) {
|
|
226
|
+
const resolved = {};
|
|
227
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
228
|
+
if (typeof value === "string") {
|
|
229
|
+
const templateRef = value.match(/^\$\{([\w-]+)\.([\w-]+)\}$/);
|
|
230
|
+
if (templateRef) {
|
|
231
|
+
const [, stepName, outputKey] = templateRef;
|
|
232
|
+
let outputs = context.stepOutputs.get(stepName);
|
|
233
|
+
if (!outputs && context.plan?.steps) {
|
|
234
|
+
const referencedStep = context.plan.steps.find(
|
|
235
|
+
(s) => s.name === stepName || s.id === stepName
|
|
236
|
+
);
|
|
237
|
+
if (referencedStep) {
|
|
238
|
+
outputs = context.stepOutputs.get(referencedStep.id);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (outputs) {
|
|
242
|
+
resolved[key] = outputs[outputKey];
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const stepRef = value.match(/^step#(\w+)\.outputs\.(\w+)$/);
|
|
247
|
+
if (stepRef) {
|
|
248
|
+
const [, stepId, outputKey] = stepRef;
|
|
249
|
+
const outputs = context.stepOutputs.get(stepId);
|
|
250
|
+
resolved[key] = outputs?.[outputKey];
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const stateRef = value.match(/^state\.(\w+)$/);
|
|
254
|
+
if (stateRef) {
|
|
255
|
+
resolved[key] = context.variables[stateRef[1]];
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
resolved[key] = value;
|
|
260
|
+
}
|
|
261
|
+
return resolved;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Execute with timeout
|
|
265
|
+
*/
|
|
266
|
+
async executeWithTimeout(step, agent, inputs, context, timeout, signal) {
|
|
267
|
+
if (!this.actionHandler) {
|
|
268
|
+
throw new Error("No action handler registered");
|
|
269
|
+
}
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const timer = setTimeout(() => {
|
|
272
|
+
this.emit("step:timeout", step, timeout);
|
|
273
|
+
reject(new Error(`Step timed out after ${timeout}ms`));
|
|
274
|
+
}, timeout);
|
|
275
|
+
signal.addEventListener("abort", () => {
|
|
276
|
+
clearTimeout(timer);
|
|
277
|
+
reject(new Error("Step was cancelled"));
|
|
278
|
+
});
|
|
279
|
+
this.actionHandler(agent, step.action, inputs, context).then((outputs) => {
|
|
280
|
+
clearTimeout(timer);
|
|
281
|
+
resolve(outputs);
|
|
282
|
+
}).catch((error) => {
|
|
283
|
+
clearTimeout(timer);
|
|
284
|
+
reject(error);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Calculate retry delay
|
|
290
|
+
*/
|
|
291
|
+
calculateDelay(config, attempt) {
|
|
292
|
+
let delay = config.delay;
|
|
293
|
+
switch (config.strategy) {
|
|
294
|
+
case "none":
|
|
295
|
+
return 0;
|
|
296
|
+
case "immediate":
|
|
297
|
+
return 0;
|
|
298
|
+
case "fixed":
|
|
299
|
+
return delay;
|
|
300
|
+
case "exponential":
|
|
301
|
+
delay = delay * Math.pow(config.backoffMultiplier || 2, attempt);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
if (config.maxDelay && delay > config.maxDelay) {
|
|
305
|
+
delay = config.maxDelay;
|
|
306
|
+
}
|
|
307
|
+
const jitter = delay * 0.1 * (Math.random() * 2 - 1);
|
|
308
|
+
return Math.round(delay + jitter);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Sleep helper
|
|
312
|
+
*/
|
|
313
|
+
sleep(ms) {
|
|
314
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var defaultExecutor = null;
|
|
318
|
+
function getDefaultExecutor() {
|
|
319
|
+
if (!defaultExecutor) {
|
|
320
|
+
defaultExecutor = new StepExecutor();
|
|
321
|
+
}
|
|
322
|
+
return defaultExecutor;
|
|
323
|
+
}
|
|
324
|
+
function resetDefaultExecutor() {
|
|
325
|
+
defaultExecutor = null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/choreography/ChoreographyPlanner.ts
|
|
329
|
+
import { CapabilityMatcher } from "@holoscript/framework/agents";
|
|
330
|
+
function generateId() {
|
|
331
|
+
return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
332
|
+
}
|
|
333
|
+
var ChoreographyPlanner = class {
|
|
334
|
+
matcher = new CapabilityMatcher();
|
|
335
|
+
/**
|
|
336
|
+
* Create a choreography plan from definition
|
|
337
|
+
*/
|
|
338
|
+
createPlan(definition) {
|
|
339
|
+
const planId = generateId();
|
|
340
|
+
const _agents = new Map(definition.agents.map((a) => [a.id, a]));
|
|
341
|
+
const steps = definition.steps.map((stepDef) => {
|
|
342
|
+
const agentId = this.resolveAgent(stepDef.agent, definition.agents);
|
|
343
|
+
if (!agentId) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`No agent found for step ${stepDef.id || stepDef.name}: ${JSON.stringify(stepDef.agent)}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
const outputs = {};
|
|
349
|
+
if (stepDef.outputs) {
|
|
350
|
+
for (const [key, value] of Object.entries(stepDef.outputs)) {
|
|
351
|
+
if (typeof value === "string") {
|
|
352
|
+
outputs[key] = { key, type: "unknown" };
|
|
353
|
+
} else {
|
|
354
|
+
outputs[key] = value;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const step = {
|
|
359
|
+
id: stepDef.id || `step_${generateId()}`,
|
|
360
|
+
name: stepDef.name,
|
|
361
|
+
description: stepDef.description,
|
|
362
|
+
agentId,
|
|
363
|
+
action: stepDef.action,
|
|
364
|
+
inputs: stepDef.inputs || {},
|
|
365
|
+
outputs,
|
|
366
|
+
dependencies: stepDef.dependencies || stepDef.dependsOn || [],
|
|
367
|
+
parallelGroup: stepDef.parallelGroup,
|
|
368
|
+
status: "pending",
|
|
369
|
+
timeout: stepDef.timeout,
|
|
370
|
+
retry: stepDef.retries !== void 0 ? { maxRetries: stepDef.retries } : void 0,
|
|
371
|
+
condition: stepDef.condition,
|
|
372
|
+
hitlGate: stepDef.hitlGate,
|
|
373
|
+
fallbackStepId: stepDef.fallbackStepId
|
|
374
|
+
};
|
|
375
|
+
return step;
|
|
376
|
+
});
|
|
377
|
+
const stepNameToId = /* @__PURE__ */ new Map();
|
|
378
|
+
for (const step of steps) {
|
|
379
|
+
if (step.name) {
|
|
380
|
+
stepNameToId.set(step.name, step.id);
|
|
381
|
+
}
|
|
382
|
+
stepNameToId.set(step.id, step.id);
|
|
383
|
+
}
|
|
384
|
+
for (const step of steps) {
|
|
385
|
+
step.dependencies = step.dependencies.map((dep) => {
|
|
386
|
+
const resolvedId = stepNameToId.get(dep);
|
|
387
|
+
if (!resolvedId) {
|
|
388
|
+
throw new Error(`Unknown step dependency "${dep}" in step "${step.name || step.id}"`);
|
|
389
|
+
}
|
|
390
|
+
return resolvedId;
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
let fallback;
|
|
394
|
+
if (definition.fallback) {
|
|
395
|
+
fallback = this.createPlan(definition.fallback);
|
|
396
|
+
}
|
|
397
|
+
const plan2 = {
|
|
398
|
+
id: planId,
|
|
399
|
+
name: definition.name,
|
|
400
|
+
goal: definition.goal,
|
|
401
|
+
steps,
|
|
402
|
+
participants: definition.agents,
|
|
403
|
+
constraints: definition.constraints || [],
|
|
404
|
+
fallback,
|
|
405
|
+
status: "draft",
|
|
406
|
+
createdAt: Date.now(),
|
|
407
|
+
priority: definition.priority,
|
|
408
|
+
tags: definition.tags,
|
|
409
|
+
metadata: definition.metadata
|
|
410
|
+
};
|
|
411
|
+
return plan2;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Validate a choreography plan
|
|
415
|
+
*/
|
|
416
|
+
validate(plan2) {
|
|
417
|
+
const errors = [];
|
|
418
|
+
const warnings = [];
|
|
419
|
+
const stepIds = new Set(plan2.steps.map((s) => s.id));
|
|
420
|
+
const agentIds = new Set(plan2.participants.map((a) => a.id));
|
|
421
|
+
if (plan2.steps.length === 0) {
|
|
422
|
+
errors.push("Plan has no steps");
|
|
423
|
+
}
|
|
424
|
+
for (const step of plan2.steps) {
|
|
425
|
+
if (!agentIds.has(step.agentId)) {
|
|
426
|
+
errors.push(`Step '${step.id}': Agent '${step.agentId}' not in participants`);
|
|
427
|
+
}
|
|
428
|
+
if (!step.action) {
|
|
429
|
+
errors.push(`Step '${step.id}': No action specified`);
|
|
430
|
+
}
|
|
431
|
+
for (const depId of step.dependencies) {
|
|
432
|
+
if (!stepIds.has(depId)) {
|
|
433
|
+
errors.push(`Step '${step.id}': Dependency '${depId}' does not exist`);
|
|
434
|
+
}
|
|
435
|
+
if (depId === step.id) {
|
|
436
|
+
errors.push(`Step '${step.id}': Cannot depend on itself`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const cycleCheck = this.detectCycles(plan2.steps);
|
|
441
|
+
if (cycleCheck) {
|
|
442
|
+
errors.push(`Circular dependency detected: ${cycleCheck.join(" -> ")}`);
|
|
443
|
+
}
|
|
444
|
+
if (!plan2.constraints.some((c) => c.type === "timeout")) {
|
|
445
|
+
warnings.push("No timeout constraint specified");
|
|
446
|
+
}
|
|
447
|
+
if (plan2.steps.some((s) => s.hitlGate) && !plan2.participants.some((a) => a.capabilities?.some((c) => c.type === "approve"))) {
|
|
448
|
+
warnings.push("HITL gate present but no approval agent in participants");
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
valid: errors.length === 0,
|
|
452
|
+
errors,
|
|
453
|
+
warnings
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Calculate execution order
|
|
458
|
+
*/
|
|
459
|
+
calculateExecutionOrder(plan2) {
|
|
460
|
+
const graph = /* @__PURE__ */ new Map();
|
|
461
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
462
|
+
for (const step of plan2.steps) {
|
|
463
|
+
graph.set(step.id, step.dependencies);
|
|
464
|
+
inDegree.set(step.id, step.dependencies.length);
|
|
465
|
+
}
|
|
466
|
+
const flatOrder = [];
|
|
467
|
+
const parallelGroups = [];
|
|
468
|
+
const queue = [];
|
|
469
|
+
for (const [stepId, degree] of inDegree.entries()) {
|
|
470
|
+
if (degree === 0) {
|
|
471
|
+
queue.push(stepId);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
while (queue.length > 0) {
|
|
475
|
+
const currentGroup = [...queue];
|
|
476
|
+
parallelGroups.push(currentGroup);
|
|
477
|
+
queue.length = 0;
|
|
478
|
+
for (const stepId of currentGroup) {
|
|
479
|
+
flatOrder.push(stepId);
|
|
480
|
+
for (const [otherId, deps] of graph.entries()) {
|
|
481
|
+
if (deps.includes(stepId)) {
|
|
482
|
+
const newDegree = (inDegree.get(otherId) || 0) - 1;
|
|
483
|
+
inDegree.set(otherId, newDegree);
|
|
484
|
+
if (newDegree === 0) {
|
|
485
|
+
queue.push(otherId);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
parallelGroups,
|
|
493
|
+
flatOrder,
|
|
494
|
+
graph
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Clone and reset a plan for re-execution
|
|
499
|
+
*/
|
|
500
|
+
resetPlan(plan2) {
|
|
501
|
+
return {
|
|
502
|
+
...plan2,
|
|
503
|
+
id: generateId(),
|
|
504
|
+
status: "draft",
|
|
505
|
+
createdAt: Date.now(),
|
|
506
|
+
startedAt: void 0,
|
|
507
|
+
completedAt: void 0,
|
|
508
|
+
duration: void 0,
|
|
509
|
+
steps: plan2.steps.map((step) => ({
|
|
510
|
+
...step,
|
|
511
|
+
status: "pending",
|
|
512
|
+
startedAt: void 0,
|
|
513
|
+
completedAt: void 0,
|
|
514
|
+
duration: void 0,
|
|
515
|
+
error: void 0,
|
|
516
|
+
retryAttempt: void 0
|
|
517
|
+
}))
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
// ==========================================================================
|
|
521
|
+
// PRIVATE METHODS
|
|
522
|
+
// ==========================================================================
|
|
523
|
+
/**
|
|
524
|
+
* Resolve agent from ID or capability query
|
|
525
|
+
*/
|
|
526
|
+
resolveAgent(agentRef, agents) {
|
|
527
|
+
if (typeof agentRef === "string") {
|
|
528
|
+
const agent = agents.find((a) => a.id === agentRef || a.name === agentRef);
|
|
529
|
+
return agent?.id || null;
|
|
530
|
+
}
|
|
531
|
+
const matches = this.matcher.findMatches(agents, agentRef);
|
|
532
|
+
if (matches.length > 0) {
|
|
533
|
+
return matches[0].manifest.id;
|
|
534
|
+
}
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Detect circular dependencies
|
|
539
|
+
*/
|
|
540
|
+
detectCycles(steps) {
|
|
541
|
+
const visited = /* @__PURE__ */ new Set();
|
|
542
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
543
|
+
const stepMap = new Map(steps.map((s) => [s.id, s]));
|
|
544
|
+
const dfs = (stepId, path) => {
|
|
545
|
+
if (recursionStack.has(stepId)) {
|
|
546
|
+
return [...path, stepId];
|
|
547
|
+
}
|
|
548
|
+
if (visited.has(stepId)) {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
visited.add(stepId);
|
|
552
|
+
recursionStack.add(stepId);
|
|
553
|
+
const step = stepMap.get(stepId);
|
|
554
|
+
if (step) {
|
|
555
|
+
for (const depId of step.dependencies) {
|
|
556
|
+
const cycle = dfs(depId, [...path, stepId]);
|
|
557
|
+
if (cycle) return cycle;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
recursionStack.delete(stepId);
|
|
561
|
+
return null;
|
|
562
|
+
};
|
|
563
|
+
for (const step of steps) {
|
|
564
|
+
const cycle = dfs(step.id, []);
|
|
565
|
+
if (cycle) return cycle;
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
var PlanBuilder = class {
|
|
571
|
+
definition;
|
|
572
|
+
planner = new ChoreographyPlanner();
|
|
573
|
+
constructor(goal) {
|
|
574
|
+
this.definition = {
|
|
575
|
+
goal,
|
|
576
|
+
agents: [],
|
|
577
|
+
steps: [],
|
|
578
|
+
constraints: []
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Set plan name
|
|
583
|
+
*/
|
|
584
|
+
name(name) {
|
|
585
|
+
this.definition.name = name;
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Add an agent to the plan
|
|
590
|
+
*/
|
|
591
|
+
agent(manifest) {
|
|
592
|
+
this.definition.agents.push(manifest);
|
|
593
|
+
return this;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Add multiple agents
|
|
597
|
+
*/
|
|
598
|
+
agents(manifests) {
|
|
599
|
+
this.definition.agents.push(...manifests);
|
|
600
|
+
return this;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Add a step to the plan
|
|
604
|
+
*/
|
|
605
|
+
step(definition) {
|
|
606
|
+
this.definition.steps.push(definition);
|
|
607
|
+
return this;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Add a constraint
|
|
611
|
+
*/
|
|
612
|
+
constraint(constraint) {
|
|
613
|
+
this.definition.constraints.push(constraint);
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Set timeout constraint
|
|
618
|
+
*/
|
|
619
|
+
timeout(ms) {
|
|
620
|
+
this.definition.constraints.push({
|
|
621
|
+
type: "timeout",
|
|
622
|
+
value: ms,
|
|
623
|
+
hard: true,
|
|
624
|
+
description: `Maximum ${ms}ms execution time`
|
|
625
|
+
});
|
|
626
|
+
return this;
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Set concurrency constraint
|
|
630
|
+
*/
|
|
631
|
+
concurrency(max) {
|
|
632
|
+
this.definition.constraints.push({
|
|
633
|
+
type: "concurrency",
|
|
634
|
+
value: max,
|
|
635
|
+
description: `Maximum ${max} concurrent steps`
|
|
636
|
+
});
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Set fallback plan
|
|
641
|
+
*/
|
|
642
|
+
fallback(fallbackPlan) {
|
|
643
|
+
this.definition.fallback = fallbackPlan;
|
|
644
|
+
return this;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Set priority
|
|
648
|
+
*/
|
|
649
|
+
priority(priority) {
|
|
650
|
+
this.definition.priority = priority;
|
|
651
|
+
return this;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Add tags
|
|
655
|
+
*/
|
|
656
|
+
tags(...tags) {
|
|
657
|
+
this.definition.tags = [...this.definition.tags || [], ...tags];
|
|
658
|
+
return this;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Set metadata
|
|
662
|
+
*/
|
|
663
|
+
metadata(meta) {
|
|
664
|
+
this.definition.metadata = { ...this.definition.metadata || {}, ...meta };
|
|
665
|
+
return this;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Build and validate the plan
|
|
669
|
+
*/
|
|
670
|
+
build() {
|
|
671
|
+
const plan2 = this.planner.createPlan(this.definition);
|
|
672
|
+
const validation = this.planner.validate(plan2);
|
|
673
|
+
if (!validation.valid) {
|
|
674
|
+
throw new Error(`Plan validation failed:
|
|
675
|
+
${validation.errors.join("\n")}`);
|
|
676
|
+
}
|
|
677
|
+
return plan2;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Build without validation (for testing)
|
|
681
|
+
*/
|
|
682
|
+
buildUnsafe() {
|
|
683
|
+
return this.planner.createPlan(this.definition);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
function plan(goal) {
|
|
687
|
+
return new PlanBuilder(goal);
|
|
688
|
+
}
|
|
689
|
+
var defaultPlanner = null;
|
|
690
|
+
function getDefaultPlanner() {
|
|
691
|
+
if (!defaultPlanner) {
|
|
692
|
+
defaultPlanner = new ChoreographyPlanner();
|
|
693
|
+
}
|
|
694
|
+
return defaultPlanner;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/choreography/ChoreographyEngine.ts
|
|
698
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
699
|
+
var DEFAULT_ENGINE_CONFIG = {
|
|
700
|
+
maxConcurrency: 4,
|
|
701
|
+
defaultTimeout: 3e4,
|
|
702
|
+
executeFallback: true,
|
|
703
|
+
autoHitlPause: true,
|
|
704
|
+
verbose: false
|
|
705
|
+
};
|
|
706
|
+
var ChoreographyEngine = class extends EventEmitter2 {
|
|
707
|
+
config;
|
|
708
|
+
planner;
|
|
709
|
+
executor;
|
|
710
|
+
registry = null;
|
|
711
|
+
activePlans = /* @__PURE__ */ new Map();
|
|
712
|
+
actionHandler = null;
|
|
713
|
+
constructor(config = {}) {
|
|
714
|
+
super();
|
|
715
|
+
this.config = { ...DEFAULT_ENGINE_CONFIG, ...config };
|
|
716
|
+
this.planner = getDefaultPlanner();
|
|
717
|
+
this.executor = getDefaultExecutor();
|
|
718
|
+
this.executor.on("step:completed", (step, result) => {
|
|
719
|
+
const state = this.findPlanWithStep(step.id);
|
|
720
|
+
if (state) {
|
|
721
|
+
this.emit("step:completed", result, state.plan);
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
this.executor.on("step:failed", (step, error) => {
|
|
725
|
+
const state = this.findPlanWithStep(step.id);
|
|
726
|
+
if (state) {
|
|
727
|
+
this.emit("step:failed", step, error, state.plan);
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
this.executor.on("step:retrying", (step, attempt, _delay) => {
|
|
731
|
+
const state = this.findPlanWithStep(step.id);
|
|
732
|
+
if (state) {
|
|
733
|
+
this.emit("step:retrying", step, attempt, state.plan);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Set the agent registry
|
|
739
|
+
*/
|
|
740
|
+
setRegistry(registry) {
|
|
741
|
+
this.registry = registry;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Set the action handler for executing agent actions
|
|
745
|
+
*/
|
|
746
|
+
setActionHandler(handler) {
|
|
747
|
+
this.actionHandler = handler;
|
|
748
|
+
this.executor.setActionHandler(handler);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Create a new choreography plan
|
|
752
|
+
*/
|
|
753
|
+
createPlan(goal, agents, steps) {
|
|
754
|
+
const plan2 = {
|
|
755
|
+
id: this.generateId(),
|
|
756
|
+
goal,
|
|
757
|
+
steps: [...steps],
|
|
758
|
+
participants: [...agents],
|
|
759
|
+
constraints: [],
|
|
760
|
+
status: "draft",
|
|
761
|
+
createdAt: Date.now()
|
|
762
|
+
};
|
|
763
|
+
this.emit("plan:created", plan2);
|
|
764
|
+
return plan2;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Execute a choreography plan
|
|
768
|
+
*/
|
|
769
|
+
async execute(plan2, variables = {}) {
|
|
770
|
+
if (!this.actionHandler) {
|
|
771
|
+
throw new Error("No action handler registered. Call setActionHandler first.");
|
|
772
|
+
}
|
|
773
|
+
const validation = this.planner.validate(plan2);
|
|
774
|
+
if (!validation.valid) {
|
|
775
|
+
throw new Error(`Plan validation failed: ${validation.errors.join(", ")}`);
|
|
776
|
+
}
|
|
777
|
+
const executionOrder = this.planner.calculateExecutionOrder(plan2);
|
|
778
|
+
const state = {
|
|
779
|
+
plan: plan2,
|
|
780
|
+
stepResults: /* @__PURE__ */ new Map(),
|
|
781
|
+
stepOutputs: /* @__PURE__ */ new Map(),
|
|
782
|
+
executionOrder,
|
|
783
|
+
currentGroup: 0,
|
|
784
|
+
paused: false,
|
|
785
|
+
cancelled: false,
|
|
786
|
+
hitlPending: /* @__PURE__ */ new Set(),
|
|
787
|
+
startTime: Date.now(),
|
|
788
|
+
variables
|
|
789
|
+
};
|
|
790
|
+
this.activePlans.set(plan2.id, state);
|
|
791
|
+
plan2.status = "running";
|
|
792
|
+
plan2.startedAt = state.startTime;
|
|
793
|
+
this.emit("plan:started", plan2);
|
|
794
|
+
try {
|
|
795
|
+
const result = await this.executePlan(state);
|
|
796
|
+
if (!result.success && plan2.fallback && this.config.executeFallback) {
|
|
797
|
+
this.emit("plan:failed", plan2, new Error(result.error || "Unknown error"));
|
|
798
|
+
const fallbackResult = await this.execute(plan2.fallback, variables);
|
|
799
|
+
return {
|
|
800
|
+
...fallbackResult,
|
|
801
|
+
usedFallback: true
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
plan2.status = result.success ? "completed" : "failed";
|
|
805
|
+
plan2.completedAt = Date.now();
|
|
806
|
+
plan2.duration = plan2.completedAt - (plan2.startedAt || state.startTime);
|
|
807
|
+
this.emit("plan:completed", result);
|
|
808
|
+
return result;
|
|
809
|
+
} catch (error) {
|
|
810
|
+
plan2.status = "failed";
|
|
811
|
+
plan2.completedAt = Date.now();
|
|
812
|
+
plan2.duration = plan2.completedAt - (plan2.startedAt || state.startTime);
|
|
813
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
814
|
+
this.emit("plan:failed", plan2, err);
|
|
815
|
+
return {
|
|
816
|
+
planId: plan2.id,
|
|
817
|
+
success: false,
|
|
818
|
+
status: "failed",
|
|
819
|
+
stepResults: Array.from(state.stepResults.values()),
|
|
820
|
+
duration: plan2.duration,
|
|
821
|
+
stepsCompleted: state.stepResults.size,
|
|
822
|
+
stepsFailed: Array.from(state.stepResults.values()).filter((r) => !r.success).length,
|
|
823
|
+
stepsSkipped: 0,
|
|
824
|
+
error: err.message,
|
|
825
|
+
finalOutputs: {}
|
|
826
|
+
};
|
|
827
|
+
} finally {
|
|
828
|
+
this.activePlans.delete(plan2.id);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Pause a running plan
|
|
833
|
+
*/
|
|
834
|
+
async pause(planId) {
|
|
835
|
+
const state = this.activePlans.get(planId);
|
|
836
|
+
if (!state) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
state.paused = true;
|
|
840
|
+
state.plan.status = "paused";
|
|
841
|
+
this.emit("plan:paused", state.plan);
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Resume a paused plan
|
|
845
|
+
*/
|
|
846
|
+
async resume(planId) {
|
|
847
|
+
const state = this.activePlans.get(planId);
|
|
848
|
+
if (!state) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (!state.paused) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
state.paused = false;
|
|
855
|
+
state.plan.status = "running";
|
|
856
|
+
this.emit("plan:resumed", state.plan);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Cancel a running plan
|
|
860
|
+
*/
|
|
861
|
+
async cancel(planId) {
|
|
862
|
+
const state = this.activePlans.get(planId);
|
|
863
|
+
if (!state) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
state.cancelled = true;
|
|
867
|
+
state.plan.status = "cancelled";
|
|
868
|
+
for (const step of state.plan.steps) {
|
|
869
|
+
if (step.status === "running") {
|
|
870
|
+
this.executor.cancel(step.id);
|
|
871
|
+
step.status = "cancelled";
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
this.emit("plan:cancelled", state.plan);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Approve a HITL gate
|
|
878
|
+
*/
|
|
879
|
+
approveHitl(planId, stepId) {
|
|
880
|
+
const state = this.activePlans.get(planId);
|
|
881
|
+
if (!state) {
|
|
882
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
883
|
+
}
|
|
884
|
+
if (!state.hitlPending.has(stepId)) {
|
|
885
|
+
throw new Error(`Step ${stepId} is not pending HITL approval`);
|
|
886
|
+
}
|
|
887
|
+
state.hitlPending.delete(stepId);
|
|
888
|
+
const step = state.plan.steps.find((s) => s.id === stepId);
|
|
889
|
+
if (step) {
|
|
890
|
+
this.emit("hitl:approved", step, state.plan);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Reject a HITL gate
|
|
895
|
+
*/
|
|
896
|
+
rejectHitl(planId, stepId, reason) {
|
|
897
|
+
const state = this.activePlans.get(planId);
|
|
898
|
+
if (!state) {
|
|
899
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
900
|
+
}
|
|
901
|
+
if (!state.hitlPending.has(stepId)) {
|
|
902
|
+
throw new Error(`Step ${stepId} is not pending HITL approval`);
|
|
903
|
+
}
|
|
904
|
+
state.hitlPending.delete(stepId);
|
|
905
|
+
const step = state.plan.steps.find((s) => s.id === stepId);
|
|
906
|
+
if (step) {
|
|
907
|
+
step.status = "failed";
|
|
908
|
+
step.error = `HITL rejected: ${reason}`;
|
|
909
|
+
this.emit("hitl:rejected", step, reason, state.plan);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Get active plan IDs
|
|
914
|
+
*/
|
|
915
|
+
getActivePlans() {
|
|
916
|
+
return Array.from(this.activePlans.keys());
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get plan status
|
|
920
|
+
*/
|
|
921
|
+
getPlanStatus(planId) {
|
|
922
|
+
const state = this.activePlans.get(planId);
|
|
923
|
+
return state?.plan.status || null;
|
|
924
|
+
}
|
|
925
|
+
// ==========================================================================
|
|
926
|
+
// PRIVATE METHODS
|
|
927
|
+
// ==========================================================================
|
|
928
|
+
/**
|
|
929
|
+
* Execute the plan
|
|
930
|
+
*/
|
|
931
|
+
async executePlan(state) {
|
|
932
|
+
const { plan: _plan, executionOrder } = state;
|
|
933
|
+
for (const group of executionOrder.parallelGroups) {
|
|
934
|
+
while (state.paused && !state.cancelled) {
|
|
935
|
+
await this.sleep(100);
|
|
936
|
+
}
|
|
937
|
+
if (state.cancelled) {
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
await this.executeGroup(state, group);
|
|
941
|
+
const failed = group.some((stepId) => {
|
|
942
|
+
const result = state.stepResults.get(stepId);
|
|
943
|
+
return result && !result.success;
|
|
944
|
+
});
|
|
945
|
+
if (failed) {
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
state.currentGroup++;
|
|
949
|
+
}
|
|
950
|
+
return this.buildResult(state);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Execute a group of steps (potentially in parallel)
|
|
954
|
+
*/
|
|
955
|
+
async executeGroup(state, stepIds) {
|
|
956
|
+
const steps = stepIds.map((id) => state.plan.steps.find((s) => s.id === id)).filter((s) => s !== void 0);
|
|
957
|
+
const concurrency = this.getConcurrencyLimit(state.plan);
|
|
958
|
+
const batches = this.chunk(steps, concurrency);
|
|
959
|
+
for (const batch of batches) {
|
|
960
|
+
if (state.cancelled) break;
|
|
961
|
+
const promises = batch.map((step) => this.executeStep(state, step));
|
|
962
|
+
await Promise.all(promises);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Execute a single step
|
|
967
|
+
*/
|
|
968
|
+
async executeStep(state, step) {
|
|
969
|
+
if (step.hitlGate && this.config.autoHitlPause) {
|
|
970
|
+
state.hitlPending.add(step.id);
|
|
971
|
+
this.emit("hitl:required", step, state.plan);
|
|
972
|
+
while (state.hitlPending.has(step.id) && !state.cancelled) {
|
|
973
|
+
await this.sleep(100);
|
|
974
|
+
}
|
|
975
|
+
if (state.cancelled) {
|
|
976
|
+
step.status = "cancelled";
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
if (step.status === "failed") {
|
|
980
|
+
state.stepResults.set(step.id, {
|
|
981
|
+
stepId: step.id,
|
|
982
|
+
success: false,
|
|
983
|
+
outputs: {},
|
|
984
|
+
duration: 0,
|
|
985
|
+
error: step.error
|
|
986
|
+
});
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const context = this.buildStepContext(state, step);
|
|
991
|
+
this.emit("step:started", step, state.plan);
|
|
992
|
+
const result = await this.executor.execute(step, context);
|
|
993
|
+
if (!result.success && step.fallbackStepId && this.config.executeFallback) {
|
|
994
|
+
const fallbackStep = state.plan.steps.find(
|
|
995
|
+
(s) => s.id === step.fallbackStepId || s.name === step.fallbackStepId
|
|
996
|
+
);
|
|
997
|
+
if (fallbackStep) {
|
|
998
|
+
let fallbackResult = state.stepResults.get(fallbackStep.id);
|
|
999
|
+
if (!fallbackResult && fallbackStep.status === "running") {
|
|
1000
|
+
while (!state.stepResults.has(fallbackStep.id) && !state.cancelled) {
|
|
1001
|
+
await this.sleep(50);
|
|
1002
|
+
}
|
|
1003
|
+
fallbackResult = state.stepResults.get(fallbackStep.id);
|
|
1004
|
+
}
|
|
1005
|
+
if (!fallbackResult && fallbackStep.status === "pending") {
|
|
1006
|
+
await this.executeStep(state, fallbackStep);
|
|
1007
|
+
fallbackResult = state.stepResults.get(fallbackStep.id);
|
|
1008
|
+
}
|
|
1009
|
+
if (fallbackResult?.success) {
|
|
1010
|
+
state.stepResults.set(step.id, {
|
|
1011
|
+
stepId: step.id,
|
|
1012
|
+
success: true,
|
|
1013
|
+
outputs: fallbackResult.outputs,
|
|
1014
|
+
duration: result.duration,
|
|
1015
|
+
usedFallback: true
|
|
1016
|
+
});
|
|
1017
|
+
state.stepOutputs.set(step.id, fallbackResult.outputs);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
state.stepResults.set(step.id, result);
|
|
1023
|
+
if (result.success) {
|
|
1024
|
+
state.stepOutputs.set(step.id, result.outputs);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Build step context
|
|
1029
|
+
*/
|
|
1030
|
+
buildStepContext(state, step) {
|
|
1031
|
+
const agents = new Map(state.plan.participants.map((a) => [a.id, a]));
|
|
1032
|
+
return {
|
|
1033
|
+
plan: state.plan,
|
|
1034
|
+
currentStep: step,
|
|
1035
|
+
agents,
|
|
1036
|
+
stepOutputs: state.stepOutputs,
|
|
1037
|
+
variables: state.variables,
|
|
1038
|
+
startTime: state.startTime,
|
|
1039
|
+
elapsedTime: Date.now() - state.startTime
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Build final result
|
|
1044
|
+
*/
|
|
1045
|
+
buildResult(state) {
|
|
1046
|
+
const stepResults = Array.from(state.stepResults.values());
|
|
1047
|
+
const completed = stepResults.filter((r) => r.success).length;
|
|
1048
|
+
const failed = stepResults.filter((r) => !r.success).length;
|
|
1049
|
+
const skipped = state.plan.steps.length - stepResults.length;
|
|
1050
|
+
const completedSteps = state.plan.steps.filter((step) => {
|
|
1051
|
+
const result = state.stepResults.get(step.id);
|
|
1052
|
+
return result?.success;
|
|
1053
|
+
});
|
|
1054
|
+
const failedSteps = state.plan.steps.filter((step) => {
|
|
1055
|
+
const result = state.stepResults.get(step.id);
|
|
1056
|
+
return result && !result.success;
|
|
1057
|
+
});
|
|
1058
|
+
const terminalSteps = state.plan.steps.filter(
|
|
1059
|
+
(s) => !state.plan.steps.some((other) => other.dependencies.includes(s.id))
|
|
1060
|
+
);
|
|
1061
|
+
const finalOutputs = {};
|
|
1062
|
+
for (const step of terminalSteps) {
|
|
1063
|
+
const outputs = state.stepOutputs.get(step.id);
|
|
1064
|
+
if (outputs) {
|
|
1065
|
+
Object.assign(finalOutputs, outputs);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const success = !state.cancelled && failed === 0 && skipped === 0;
|
|
1069
|
+
return {
|
|
1070
|
+
planId: state.plan.id,
|
|
1071
|
+
success,
|
|
1072
|
+
status: state.cancelled ? "cancelled" : success ? "completed" : "failed",
|
|
1073
|
+
stepResults,
|
|
1074
|
+
duration: Date.now() - state.startTime,
|
|
1075
|
+
stepsCompleted: completed,
|
|
1076
|
+
stepsFailed: failed,
|
|
1077
|
+
stepsSkipped: skipped,
|
|
1078
|
+
completedSteps,
|
|
1079
|
+
failedSteps,
|
|
1080
|
+
error: failed > 0 ? `${failed} step(s) failed` : void 0,
|
|
1081
|
+
finalOutputs,
|
|
1082
|
+
metrics: {
|
|
1083
|
+
peakConcurrency: this.getConcurrencyLimit(state.plan)
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Get concurrency limit from constraints
|
|
1089
|
+
*/
|
|
1090
|
+
getConcurrencyLimit(plan2) {
|
|
1091
|
+
const constraint = plan2.constraints.find((c) => c.type === "concurrency");
|
|
1092
|
+
if (constraint && typeof constraint.value === "number") {
|
|
1093
|
+
return constraint.value;
|
|
1094
|
+
}
|
|
1095
|
+
return this.config.maxConcurrency;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Find plan containing a step
|
|
1099
|
+
*/
|
|
1100
|
+
findPlanWithStep(stepId) {
|
|
1101
|
+
for (const state of this.activePlans.values()) {
|
|
1102
|
+
if (state.plan.steps.some((s) => s.id === stepId)) {
|
|
1103
|
+
return state;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return void 0;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Generate unique ID
|
|
1110
|
+
*/
|
|
1111
|
+
generateId() {
|
|
1112
|
+
return `plan_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Chunk array into batches
|
|
1116
|
+
*/
|
|
1117
|
+
chunk(array, size) {
|
|
1118
|
+
const chunks = [];
|
|
1119
|
+
for (let i = 0; i < array.length; i += size) {
|
|
1120
|
+
chunks.push(array.slice(i, i + size));
|
|
1121
|
+
}
|
|
1122
|
+
return chunks;
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Sleep helper
|
|
1126
|
+
*/
|
|
1127
|
+
sleep(ms) {
|
|
1128
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
var defaultEngine = null;
|
|
1132
|
+
function getDefaultEngine() {
|
|
1133
|
+
if (!defaultEngine) {
|
|
1134
|
+
defaultEngine = new ChoreographyEngine();
|
|
1135
|
+
}
|
|
1136
|
+
return defaultEngine;
|
|
1137
|
+
}
|
|
1138
|
+
function resetDefaultEngine() {
|
|
1139
|
+
defaultEngine = null;
|
|
1140
|
+
}
|
|
1141
|
+
export {
|
|
1142
|
+
ChoreographyEngine,
|
|
1143
|
+
ChoreographyPlanner,
|
|
1144
|
+
Constraints,
|
|
1145
|
+
DEFAULT_ENGINE_CONFIG,
|
|
1146
|
+
DEFAULT_EXECUTOR_CONFIG,
|
|
1147
|
+
DEFAULT_RETRY_CONFIG,
|
|
1148
|
+
PlanBuilder,
|
|
1149
|
+
StepExecutor,
|
|
1150
|
+
getDefaultEngine,
|
|
1151
|
+
getDefaultExecutor,
|
|
1152
|
+
getDefaultPlanner,
|
|
1153
|
+
plan,
|
|
1154
|
+
resetDefaultEngine,
|
|
1155
|
+
resetDefaultExecutor
|
|
1156
|
+
};
|