@ai-setting/roy-agent-core 1.6.11 → 1.6.13
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/env/agent/index.js +3 -3
- package/dist/env/event-source/index.js +5 -5
- package/dist/env/index.js +19 -19
- package/dist/env/prompt/index.js +2 -2
- package/dist/env/task/delegate/index.js +4 -4
- package/dist/env/task/index.js +7 -7
- package/dist/env/task/plugins/index.js +4 -4
- package/dist/env/task/storage/index.js +2 -1
- package/dist/env/task/tools/operation/index.js +1 -1
- package/dist/env/tool/built-in/index.js +2 -2
- package/dist/env/tool/index.js +3 -3
- package/dist/env/workflow/engine/index.js +3 -3
- package/dist/env/workflow/index.js +7 -7
- package/dist/env/workflow/service/index.js +1 -1
- package/dist/env/workflow/tools/index.js +4 -4
- package/dist/env/workflow/utils/index.js +2 -2
- package/dist/index.js +132 -21
- package/dist/shared/@ai-setting/{roy-agent-core-0n2a8cbn.js → roy-agent-core-0crh2g0m.js} +594 -237
- package/dist/shared/@ai-setting/{roy-agent-core-0cftbg62.js → roy-agent-core-0pbmweh4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-jenchn33.js → roy-agent-core-24rmf17n.js} +8 -0
- package/dist/shared/@ai-setting/{roy-agent-core-2ek596yd.js → roy-agent-core-2b4078kz.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-z7f8hyv7.js → roy-agent-core-6jqpdjr4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-38kbfarg.js → roy-agent-core-6qbyfzsw.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-mb7b62sm.js → roy-agent-core-8cbsjkkx.js} +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-9abv4ewh.js +1122 -0
- package/dist/shared/@ai-setting/{roy-agent-core-trdpar9f.js → roy-agent-core-apz6v22z.js} +419 -60
- package/dist/shared/@ai-setting/{roy-agent-core-y5ymc4f8.js → roy-agent-core-deqpzcg7.js} +46 -14
- package/dist/shared/@ai-setting/{roy-agent-core-m38q2azm.js → roy-agent-core-fvkyv5j6.js} +4 -2
- package/dist/shared/@ai-setting/{roy-agent-core-3rs38pf7.js → roy-agent-core-fwxqevpf.js} +11 -8
- package/dist/shared/@ai-setting/{roy-agent-core-pzeexnhf.js → roy-agent-core-nv4yfy3m.js} +4 -3
- package/dist/shared/@ai-setting/roy-agent-core-rn57psp0.js +396 -0
- package/dist/shared/@ai-setting/{roy-agent-core-wqe84f84.js → roy-agent-core-v5nknfh0.js} +30 -8
- package/dist/shared/@ai-setting/{roy-agent-core-06574wqa.js → roy-agent-core-v77pp20w.js} +4 -4
- package/dist/shared/@ai-setting/roy-agent-core-vcjq65nz.js +436 -0
- package/dist/shared/@ai-setting/{roy-agent-core-8wd3qwx5.js → roy-agent-core-wjve1qr5.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-5w9a1eqa.js → roy-agent-core-wpw37qqf.js} +88 -3
- package/dist/shared/@ai-setting/{roy-agent-core-p6x6n3k4.js → roy-agent-core-x7fgze6s.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-p2vpsd1d.js → roy-agent-core-ywtmc0bh.js} +166 -6
- package/package.json +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-4yq23m5g.js +0 -421
- package/dist/shared/@ai-setting/roy-agent-core-9xby523m.js +0 -961
- package/dist/shared/@ai-setting/roy-agent-core-t94ktchq.js +0 -213
- /package/dist/shared/@ai-setting/{roy-agent-core-zs31w092.js → roy-agent-core-9nhwdq9r.js} +0 -0
|
@@ -2,12 +2,18 @@ import {
|
|
|
2
2
|
TracedAs,
|
|
3
3
|
init_decorator
|
|
4
4
|
} from "./roy-agent-core-fdm579b1.js";
|
|
5
|
+
import {
|
|
6
|
+
createLogger,
|
|
7
|
+
init_logger
|
|
8
|
+
} from "./roy-agent-core-vyygk314.js";
|
|
5
9
|
import {
|
|
6
10
|
__legacyDecorateClassTS
|
|
7
11
|
} from "./roy-agent-core-fs0mn2jk.js";
|
|
8
12
|
|
|
9
13
|
// src/env/workflow/service/workflow-service.ts
|
|
14
|
+
init_logger();
|
|
10
15
|
init_decorator();
|
|
16
|
+
var logger = createLogger("WorkflowService");
|
|
11
17
|
var TAG_MIN_COUNT = 1;
|
|
12
18
|
var TAG_MAX_COUNT = 3;
|
|
13
19
|
class TagValidationError extends Error {
|
|
@@ -27,7 +33,27 @@ class WorkflowService {
|
|
|
27
33
|
this.engineFactory = engineFactory;
|
|
28
34
|
this.sessionComponent = sessionComponent;
|
|
29
35
|
this.tagRepository = tagRepository;
|
|
36
|
+
this.engineInstances = new Map;
|
|
37
|
+
}
|
|
38
|
+
async listAllSessions() {
|
|
39
|
+
if (!this.sessionComponent?.list)
|
|
40
|
+
return [];
|
|
41
|
+
const PAGE = 1000;
|
|
42
|
+
const HARD_CAP = 50000;
|
|
43
|
+
const collected = [];
|
|
44
|
+
let offset = 0;
|
|
45
|
+
while (collected.length < HARD_CAP) {
|
|
46
|
+
const batch = await this.sessionComponent.list({ limit: PAGE, offset });
|
|
47
|
+
collected.push(...batch);
|
|
48
|
+
if (batch.length < PAGE)
|
|
49
|
+
break;
|
|
50
|
+
offset += PAGE;
|
|
51
|
+
}
|
|
52
|
+
return collected;
|
|
30
53
|
}
|
|
54
|
+
engineInstances;
|
|
55
|
+
sessionToEngine = new Map;
|
|
56
|
+
sessionToParent = new Map;
|
|
31
57
|
validateTags(tags) {
|
|
32
58
|
const effective = tags ?? [];
|
|
33
59
|
if (effective.length < TAG_MIN_COUNT) {
|
|
@@ -233,14 +259,24 @@ class WorkflowService {
|
|
|
233
259
|
};
|
|
234
260
|
}
|
|
235
261
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
262
|
+
let engine = this.engineInstances.get(workflow.name);
|
|
263
|
+
if (!engine) {
|
|
264
|
+
engine = this.engineFactory(workflow, {
|
|
265
|
+
...options,
|
|
266
|
+
input
|
|
267
|
+
});
|
|
268
|
+
this.engineInstances.set(workflow.name, engine);
|
|
269
|
+
}
|
|
240
270
|
const result = await engine.runWorkflow(workflow, {
|
|
241
271
|
...options,
|
|
242
272
|
input
|
|
243
273
|
});
|
|
274
|
+
const resolvedSessionId = result.sessionId || result.runId;
|
|
275
|
+
if (resolvedSessionId) {
|
|
276
|
+
this.sessionToEngine.set(resolvedSessionId, engine);
|
|
277
|
+
const parentSid = options?.parentSessionId;
|
|
278
|
+
this.sessionToParent.set(resolvedSessionId, typeof parentSid === "string" ? parentSid : undefined);
|
|
279
|
+
}
|
|
244
280
|
return {
|
|
245
281
|
...result,
|
|
246
282
|
runId: result.runId || result.sessionId || "",
|
|
@@ -267,8 +303,111 @@ class WorkflowService {
|
|
|
267
303
|
if (!workflow) {
|
|
268
304
|
throw new Error(`Workflow not found: ${metadata.workflowName}`);
|
|
269
305
|
}
|
|
270
|
-
const
|
|
271
|
-
|
|
306
|
+
const cachedBySession = this.sessionToEngine.get(sessionId);
|
|
307
|
+
const cachedByName = this.engineInstances.get(metadata.workflowName);
|
|
308
|
+
const engine = cachedBySession ?? cachedByName ?? this.engineFactory(workflow);
|
|
309
|
+
try {
|
|
310
|
+
await engine.stop(sessionId, reason);
|
|
311
|
+
} finally {
|
|
312
|
+
this.sessionToEngine.delete(sessionId);
|
|
313
|
+
this.sessionToParent.delete(sessionId);
|
|
314
|
+
this.pruneStaleEngines();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
pruneStaleEngines() {
|
|
318
|
+
for (const [workflowName, engine] of this.engineInstances) {
|
|
319
|
+
if (!engine?.activeSessions || engine.activeSessions.size === 0) {
|
|
320
|
+
this.engineInstances.delete(workflowName);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async dispose() {
|
|
325
|
+
for (const [, engine] of this.engineInstances) {
|
|
326
|
+
if (engine?.activeSessions) {
|
|
327
|
+
for (const sessionId of engine.activeSessions.keys()) {
|
|
328
|
+
try {
|
|
329
|
+
await engine.stop(sessionId, "WorkflowService disposed");
|
|
330
|
+
} catch {}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
this.engineInstances.clear();
|
|
335
|
+
this.sessionToEngine.clear();
|
|
336
|
+
}
|
|
337
|
+
hasActiveWorkflowForParent(parentSessionId) {
|
|
338
|
+
if (!parentSessionId)
|
|
339
|
+
return false;
|
|
340
|
+
for (const [sessionId, recordedParent] of this.sessionToParent) {
|
|
341
|
+
if (recordedParent === undefined)
|
|
342
|
+
continue;
|
|
343
|
+
if (recordedParent === parentSessionId) {
|
|
344
|
+
const engine = this.sessionToEngine.get(sessionId);
|
|
345
|
+
const active = engine?.activeSessions;
|
|
346
|
+
if (active && active.has(sessionId))
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
async stopAllForParent(parentSessionId) {
|
|
353
|
+
if (!parentSessionId || !this.sessionComponent?.list) {
|
|
354
|
+
return { stoppedSessionIds: [], errors: [] };
|
|
355
|
+
}
|
|
356
|
+
const allSessions = await this.listAllSessions();
|
|
357
|
+
const stoppedSessionIds = [];
|
|
358
|
+
const errors = [];
|
|
359
|
+
for (const session of allSessions) {
|
|
360
|
+
const meta = session.metadata;
|
|
361
|
+
if (session.parentSessionId === parentSessionId && meta?.type === "workflow" && (meta?.status === "running" || meta?.status === "paused")) {
|
|
362
|
+
try {
|
|
363
|
+
await this.stopRun(session.id, "ESC cascade stop");
|
|
364
|
+
stoppedSessionIds.push(session.id);
|
|
365
|
+
} catch (err) {
|
|
366
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
367
|
+
const stack = err instanceof Error ? err.stack : undefined;
|
|
368
|
+
logger.warn(`[WorkflowService] stopAllForParent failed for ${session.id}: ${errorMsg}`);
|
|
369
|
+
errors.push({ sessionId: session.id, error: errorMsg, stack });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return { stoppedSessionIds, errors };
|
|
374
|
+
}
|
|
375
|
+
async forceStopAll() {
|
|
376
|
+
if (!this.sessionComponent?.list || !this.sessionComponent.update) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const allSessions = await this.listAllSessions();
|
|
380
|
+
const evictedSessionIds = [];
|
|
381
|
+
for (const session of allSessions) {
|
|
382
|
+
const meta = session.metadata;
|
|
383
|
+
if (meta?.type === "workflow" && (meta?.status === "running" || meta?.status === "paused")) {
|
|
384
|
+
const cachedEngine = this.sessionToEngine.get(session.id);
|
|
385
|
+
if (cachedEngine && typeof cachedEngine.stop === "function") {
|
|
386
|
+
try {
|
|
387
|
+
await cachedEngine.stop(session.id, "force stop all");
|
|
388
|
+
} catch (err) {
|
|
389
|
+
logger.warn(`[WorkflowService] forceStopAll engine.stop failed for ${session.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
await this.sessionComponent.update(session.id, {
|
|
394
|
+
metadata: { ...meta, status: "stopped" }
|
|
395
|
+
});
|
|
396
|
+
evictedSessionIds.push(session.id);
|
|
397
|
+
} catch (err) {
|
|
398
|
+
logger.warn(`[WorkflowService] forceStopAll update failed for ${session.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
for (const sid of evictedSessionIds) {
|
|
403
|
+
const engine = this.sessionToEngine.get(sid);
|
|
404
|
+
if (engine?.activeSessions) {
|
|
405
|
+
engine.activeSessions.delete(sid);
|
|
406
|
+
}
|
|
407
|
+
this.sessionToEngine.delete(sid);
|
|
408
|
+
this.sessionToParent.delete(sid);
|
|
409
|
+
}
|
|
410
|
+
this.pruneStaleEngines();
|
|
272
411
|
}
|
|
273
412
|
async resumeSession(sessionId, options) {
|
|
274
413
|
if (!this.sessionComponent) {
|
|
@@ -364,6 +503,27 @@ __legacyDecorateClassTS([
|
|
|
364
503
|
log: false
|
|
365
504
|
})
|
|
366
505
|
], WorkflowService.prototype, "getTag", null);
|
|
506
|
+
__legacyDecorateClassTS([
|
|
507
|
+
TracedAs("workflow.service.hasActiveWorkflowForParent", {
|
|
508
|
+
recordParams: true,
|
|
509
|
+
recordResult: true,
|
|
510
|
+
log: false
|
|
511
|
+
})
|
|
512
|
+
], WorkflowService.prototype, "hasActiveWorkflowForParent", null);
|
|
513
|
+
__legacyDecorateClassTS([
|
|
514
|
+
TracedAs("workflow.service.stopAllForParent", {
|
|
515
|
+
recordParams: true,
|
|
516
|
+
recordResult: true,
|
|
517
|
+
log: true
|
|
518
|
+
})
|
|
519
|
+
], WorkflowService.prototype, "stopAllForParent", null);
|
|
520
|
+
__legacyDecorateClassTS([
|
|
521
|
+
TracedAs("workflow.service.forceStopAll", {
|
|
522
|
+
recordParams: false,
|
|
523
|
+
recordResult: true,
|
|
524
|
+
log: true
|
|
525
|
+
})
|
|
526
|
+
], WorkflowService.prototype, "forceStopAll", null);
|
|
367
527
|
__legacyDecorateClassTS([
|
|
368
528
|
TracedAs("workflow.service.resumeSession", {
|
|
369
529
|
recordParams: true,
|
package/package.json
CHANGED
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__esm
|
|
3
|
-
} from "./roy-agent-core-fs0mn2jk.js";
|
|
4
|
-
|
|
5
|
-
// src/env/workflow/engine/dag-manager.ts
|
|
6
|
-
class DAGManager {
|
|
7
|
-
workflow;
|
|
8
|
-
nodeMap;
|
|
9
|
-
extendedDefinition = null;
|
|
10
|
-
cachedAnalysis = null;
|
|
11
|
-
allowCycles;
|
|
12
|
-
constructor(workflow, options) {
|
|
13
|
-
if (!workflow.nodes || workflow.nodes.length === 0) {
|
|
14
|
-
throw new Error("At least one node is required");
|
|
15
|
-
}
|
|
16
|
-
this.workflow = workflow;
|
|
17
|
-
this.nodeMap = new Map;
|
|
18
|
-
this.allowCycles = options?.allowCycles ?? false;
|
|
19
|
-
for (const node of workflow.nodes) {
|
|
20
|
-
if (node.id === "") {
|
|
21
|
-
throw new Error("Node ID cannot be empty");
|
|
22
|
-
}
|
|
23
|
-
if (this.nodeMap.has(node.id)) {
|
|
24
|
-
throw new Error(`Duplicate node ID: ${node.id}`);
|
|
25
|
-
}
|
|
26
|
-
this.nodeMap.set(node.id, node);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
getExtendedDefinition() {
|
|
30
|
-
if (this.extendedDefinition) {
|
|
31
|
-
return this.extendedDefinition;
|
|
32
|
-
}
|
|
33
|
-
const analysis = this.analyze();
|
|
34
|
-
this.extendedDefinition = {
|
|
35
|
-
...this.workflow,
|
|
36
|
-
computedEntry: analysis.entryNodes,
|
|
37
|
-
nodeMap: this.nodeMap,
|
|
38
|
-
topologicalOrder: this.getTopologicalOrder()
|
|
39
|
-
};
|
|
40
|
-
return this.extendedDefinition;
|
|
41
|
-
}
|
|
42
|
-
analyze() {
|
|
43
|
-
if (this.cachedAnalysis) {
|
|
44
|
-
return this.cachedAnalysis;
|
|
45
|
-
}
|
|
46
|
-
const dependencies = this.buildDependenciesMap();
|
|
47
|
-
const dependents = this.buildDependentsMap(dependencies);
|
|
48
|
-
const entryNodes = this.findEntryNodes(dependencies);
|
|
49
|
-
const exitNodes = this.findExitNodes(dependents);
|
|
50
|
-
const levels = this.computeLevels(dependencies);
|
|
51
|
-
const criticalPath = this.findCriticalPath(levels, dependencies);
|
|
52
|
-
this.cachedAnalysis = {
|
|
53
|
-
entryNodes,
|
|
54
|
-
exitNodes,
|
|
55
|
-
levels,
|
|
56
|
-
dependencies,
|
|
57
|
-
dependents,
|
|
58
|
-
criticalPath
|
|
59
|
-
};
|
|
60
|
-
return this.cachedAnalysis;
|
|
61
|
-
}
|
|
62
|
-
buildDependenciesMap() {
|
|
63
|
-
const dependencies = new Map;
|
|
64
|
-
for (const node of this.workflow.nodes) {
|
|
65
|
-
const deps = node.depends_on || [];
|
|
66
|
-
dependencies.set(node.id, [...deps]);
|
|
67
|
-
}
|
|
68
|
-
if (this.workflow.edges) {
|
|
69
|
-
for (const edge of this.workflow.edges) {
|
|
70
|
-
const targetDeps = dependencies.get(edge.to);
|
|
71
|
-
if (!targetDeps)
|
|
72
|
-
continue;
|
|
73
|
-
if (targetDeps.includes(edge.from))
|
|
74
|
-
continue;
|
|
75
|
-
if (!this.isReachable(edge.from, edge.to, dependencies)) {
|
|
76
|
-
targetDeps.push(edge.from);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return dependencies;
|
|
81
|
-
}
|
|
82
|
-
isReachable(source, target, dependencies) {
|
|
83
|
-
if (source === target)
|
|
84
|
-
return true;
|
|
85
|
-
const visited = new Set;
|
|
86
|
-
const queue = [source];
|
|
87
|
-
while (queue.length > 0) {
|
|
88
|
-
const current = queue.shift();
|
|
89
|
-
if (current === target)
|
|
90
|
-
return true;
|
|
91
|
-
if (visited.has(current))
|
|
92
|
-
continue;
|
|
93
|
-
visited.add(current);
|
|
94
|
-
const deps = dependencies.get(current) || [];
|
|
95
|
-
for (const dep of deps) {
|
|
96
|
-
if (!visited.has(dep))
|
|
97
|
-
queue.push(dep);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
buildDependentsMap(dependencies) {
|
|
103
|
-
const dependentsMap = new Map;
|
|
104
|
-
for (const nodeId of dependencies.keys()) {
|
|
105
|
-
dependentsMap.set(nodeId, []);
|
|
106
|
-
}
|
|
107
|
-
for (const [nodeId, deps] of dependencies.entries()) {
|
|
108
|
-
for (const dep of deps) {
|
|
109
|
-
const nodeDependents = dependentsMap.get(dep) || [];
|
|
110
|
-
nodeDependents.push(nodeId);
|
|
111
|
-
dependentsMap.set(dep, nodeDependents);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return dependentsMap;
|
|
115
|
-
}
|
|
116
|
-
findEntryNodes(dependencies) {
|
|
117
|
-
const entryNodes = [];
|
|
118
|
-
for (const [nodeId, deps] of dependencies.entries()) {
|
|
119
|
-
if (deps.length === 0) {
|
|
120
|
-
entryNodes.push(nodeId);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return entryNodes;
|
|
124
|
-
}
|
|
125
|
-
findExitNodes(dependents) {
|
|
126
|
-
const exitNodes = [];
|
|
127
|
-
for (const [nodeId, deps] of dependents.entries()) {
|
|
128
|
-
if (deps.length === 0) {
|
|
129
|
-
exitNodes.push(nodeId);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return exitNodes;
|
|
133
|
-
}
|
|
134
|
-
computeLevels(dependencies) {
|
|
135
|
-
const levels = [];
|
|
136
|
-
const assigned = new Set;
|
|
137
|
-
const nodeLevels = new Map;
|
|
138
|
-
const dependentsMap = this.buildDependentsMap(dependencies);
|
|
139
|
-
const entryNodes = this.findEntryNodes(dependencies);
|
|
140
|
-
const queue = [];
|
|
141
|
-
for (const entry of entryNodes) {
|
|
142
|
-
queue.push({ nodeId: entry, level: 0 });
|
|
143
|
-
nodeLevels.set(entry, 0);
|
|
144
|
-
}
|
|
145
|
-
while (queue.length > 0) {
|
|
146
|
-
const { nodeId, level } = queue.shift();
|
|
147
|
-
if (assigned.has(nodeId)) {
|
|
148
|
-
const currentLevel = nodeLevels.get(nodeId) || 0;
|
|
149
|
-
if (level > currentLevel) {
|
|
150
|
-
nodeLevels.set(nodeId, level);
|
|
151
|
-
}
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
assigned.add(nodeId);
|
|
155
|
-
nodeLevels.set(nodeId, level);
|
|
156
|
-
const dependentsOfNode = dependentsMap.get(nodeId) || [];
|
|
157
|
-
for (const dependent of dependentsOfNode) {
|
|
158
|
-
if (!assigned.has(dependent)) {
|
|
159
|
-
queue.push({ nodeId: dependent, level: level + 1 });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
for (const nodeId of dependencies.keys()) {
|
|
164
|
-
if (!nodeLevels.has(nodeId)) {
|
|
165
|
-
nodeLevels.set(nodeId, 0);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
const maxLevel = Math.max(...nodeLevels.values(), 0);
|
|
169
|
-
for (let i = 0;i <= maxLevel; i++) {
|
|
170
|
-
levels.push([]);
|
|
171
|
-
}
|
|
172
|
-
for (const [nodeId, level] of nodeLevels.entries()) {
|
|
173
|
-
levels[level].push(nodeId);
|
|
174
|
-
}
|
|
175
|
-
return levels;
|
|
176
|
-
}
|
|
177
|
-
findCriticalPath(levels, dependencies) {
|
|
178
|
-
if (levels.length === 0) {
|
|
179
|
-
return [];
|
|
180
|
-
}
|
|
181
|
-
const longestPath = new Map;
|
|
182
|
-
const allNodes = [];
|
|
183
|
-
for (const level of levels) {
|
|
184
|
-
allNodes.push(...level);
|
|
185
|
-
}
|
|
186
|
-
for (const nodeId of allNodes) {
|
|
187
|
-
const deps = dependencies.get(nodeId) || [];
|
|
188
|
-
if (deps.length === 0) {
|
|
189
|
-
longestPath.set(nodeId, { length: 1, path: [nodeId] });
|
|
190
|
-
} else {
|
|
191
|
-
let maxLength2 = 0;
|
|
192
|
-
let maxPath = [];
|
|
193
|
-
for (const dep of deps) {
|
|
194
|
-
const depInfo = longestPath.get(dep);
|
|
195
|
-
if (depInfo && depInfo.length > maxLength2) {
|
|
196
|
-
maxLength2 = depInfo.length;
|
|
197
|
-
maxPath = depInfo.path;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
longestPath.set(nodeId, {
|
|
201
|
-
length: maxLength2 + 1,
|
|
202
|
-
path: [...maxPath, nodeId]
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
let maxLength = 0;
|
|
207
|
-
let criticalPath = [];
|
|
208
|
-
for (const [, info] of longestPath.entries()) {
|
|
209
|
-
if (info.length > maxLength) {
|
|
210
|
-
maxLength = info.length;
|
|
211
|
-
criticalPath = info.path;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return criticalPath;
|
|
215
|
-
}
|
|
216
|
-
getTopologicalOrder() {
|
|
217
|
-
const dependencies = this.buildDependenciesMap();
|
|
218
|
-
const dependents = this.buildDependentsMap(dependencies);
|
|
219
|
-
const inDegree = new Map;
|
|
220
|
-
for (const nodeId of dependencies.keys()) {
|
|
221
|
-
inDegree.set(nodeId, dependencies.get(nodeId)?.length || 0);
|
|
222
|
-
}
|
|
223
|
-
const queue = [];
|
|
224
|
-
for (const [nodeId, degree] of inDegree.entries()) {
|
|
225
|
-
if (degree === 0) {
|
|
226
|
-
queue.push(nodeId);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
const result = [];
|
|
230
|
-
while (queue.length > 0) {
|
|
231
|
-
const nodeId = queue.shift();
|
|
232
|
-
result.push(nodeId);
|
|
233
|
-
const nodeDependents = dependents.get(nodeId) || [];
|
|
234
|
-
for (const dependent of nodeDependents) {
|
|
235
|
-
const currentDegree = inDegree.get(dependent) || 0;
|
|
236
|
-
inDegree.set(dependent, currentDegree - 1);
|
|
237
|
-
if (currentDegree - 1 === 0) {
|
|
238
|
-
queue.push(dependent);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return result;
|
|
243
|
-
}
|
|
244
|
-
getReadyNodes(completedNodes) {
|
|
245
|
-
const dependencies = this.buildDependenciesMap();
|
|
246
|
-
const ready = [];
|
|
247
|
-
for (const nodeId of dependencies.keys()) {
|
|
248
|
-
if (completedNodes.has(nodeId)) {
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
if (this.areDependenciesMet(nodeId, completedNodes)) {
|
|
252
|
-
ready.push(nodeId);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return ready;
|
|
256
|
-
}
|
|
257
|
-
areDependenciesMet(nodeId, completedNodes) {
|
|
258
|
-
const analysis = this.analyze();
|
|
259
|
-
const deps = analysis.dependencies.get(nodeId);
|
|
260
|
-
if (!deps) {
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
|
-
for (const dep of deps) {
|
|
264
|
-
if (!completedNodes.has(dep)) {
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
270
|
-
isCyclic() {
|
|
271
|
-
const dependencies = this.buildDependenciesMap();
|
|
272
|
-
const order = this.getTopologicalOrderInternal(dependencies);
|
|
273
|
-
return order.length !== this.workflow.nodes.length;
|
|
274
|
-
}
|
|
275
|
-
getTopologicalOrderInternal(dependencies) {
|
|
276
|
-
const dependents = this.buildDependentsMap(dependencies);
|
|
277
|
-
const inDegree = new Map;
|
|
278
|
-
for (const nodeId of dependencies.keys()) {
|
|
279
|
-
inDegree.set(nodeId, dependencies.get(nodeId)?.length || 0);
|
|
280
|
-
}
|
|
281
|
-
const queue = [];
|
|
282
|
-
for (const [nodeId, degree] of inDegree.entries()) {
|
|
283
|
-
if (degree === 0) {
|
|
284
|
-
queue.push(nodeId);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
const result = [];
|
|
288
|
-
while (queue.length > 0) {
|
|
289
|
-
const nodeId = queue.shift();
|
|
290
|
-
result.push(nodeId);
|
|
291
|
-
const nodeDependents = dependents.get(nodeId) || [];
|
|
292
|
-
for (const dependent of nodeDependents) {
|
|
293
|
-
const currentDegree = inDegree.get(dependent) || 0;
|
|
294
|
-
inDegree.set(dependent, currentDegree - 1);
|
|
295
|
-
if (currentDegree - 1 === 0) {
|
|
296
|
-
queue.push(dependent);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return result;
|
|
301
|
-
}
|
|
302
|
-
validate(options) {
|
|
303
|
-
const errors = [];
|
|
304
|
-
const nodeIds = new Set;
|
|
305
|
-
for (const node of this.workflow.nodes) {
|
|
306
|
-
if (node.id === "") {
|
|
307
|
-
errors.push("Node ID cannot be empty");
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
if (nodeIds.has(node.id)) {
|
|
311
|
-
errors.push(`Duplicate node ID: ${node.id}`);
|
|
312
|
-
}
|
|
313
|
-
nodeIds.add(node.id);
|
|
314
|
-
}
|
|
315
|
-
for (const node of this.workflow.nodes) {
|
|
316
|
-
const deps = node.depends_on || [];
|
|
317
|
-
for (const dep of deps) {
|
|
318
|
-
if (!nodeIds.has(dep)) {
|
|
319
|
-
errors.push(`Node '${node.id}' depends on non-existent node '${dep}'`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (!this.allowCycles && !options?.skipCycleCheck && this.isCyclic()) {
|
|
324
|
-
errors.push("Workflow contains a cycle");
|
|
325
|
-
}
|
|
326
|
-
return {
|
|
327
|
-
valid: errors.length === 0,
|
|
328
|
-
errors
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
addNode(node) {
|
|
332
|
-
if (!node.id || node.id.trim() === "") {
|
|
333
|
-
throw new Error("Node ID cannot be empty");
|
|
334
|
-
}
|
|
335
|
-
if (this.nodeMap.has(node.id)) {
|
|
336
|
-
throw new Error(`Node with ID '${node.id}' already exists`);
|
|
337
|
-
}
|
|
338
|
-
const deps = node.depends_on || [];
|
|
339
|
-
for (const dep of deps) {
|
|
340
|
-
if (!this.nodeMap.has(dep)) {
|
|
341
|
-
throw new Error(`Cannot add node '${node.id}': depends on non-existent node '${dep}'`);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
const originalNodes = [...this.workflow.nodes];
|
|
345
|
-
this.workflow.nodes.push(node);
|
|
346
|
-
if (this.isCyclic()) {
|
|
347
|
-
this.workflow.nodes = originalNodes;
|
|
348
|
-
throw new Error(`Cannot add node '${node.id}': would create a cycle in the workflow`);
|
|
349
|
-
}
|
|
350
|
-
this.nodeMap.set(node.id, node);
|
|
351
|
-
this.cachedAnalysis = null;
|
|
352
|
-
this.extendedDefinition = null;
|
|
353
|
-
}
|
|
354
|
-
removeNode(nodeId) {
|
|
355
|
-
if (!this.nodeMap.has(nodeId)) {
|
|
356
|
-
throw new Error(`Node '${nodeId}' does not exist`);
|
|
357
|
-
}
|
|
358
|
-
const dependents = this.getDependents(nodeId);
|
|
359
|
-
if (dependents.length > 0) {
|
|
360
|
-
throw new Error(`Cannot remove node '${nodeId}': other nodes depend on it: ${dependents.join(", ")}`);
|
|
361
|
-
}
|
|
362
|
-
this.workflow.nodes = this.workflow.nodes.filter((n) => n.id !== nodeId);
|
|
363
|
-
this.nodeMap.delete(nodeId);
|
|
364
|
-
this.cachedAnalysis = null;
|
|
365
|
-
this.extendedDefinition = null;
|
|
366
|
-
}
|
|
367
|
-
getDependents(nodeId) {
|
|
368
|
-
const analysis = this.analyze();
|
|
369
|
-
return analysis.dependents.get(nodeId) || [];
|
|
370
|
-
}
|
|
371
|
-
getDownstreamUntil(fromNodeId, untilNodeId) {
|
|
372
|
-
const result = [];
|
|
373
|
-
const visited = new Set;
|
|
374
|
-
const queue = [...this.getDependents(fromNodeId)];
|
|
375
|
-
while (queue.length > 0) {
|
|
376
|
-
const nodeId = queue.shift();
|
|
377
|
-
if (visited.has(nodeId))
|
|
378
|
-
continue;
|
|
379
|
-
visited.add(nodeId);
|
|
380
|
-
result.push(nodeId);
|
|
381
|
-
if (nodeId === untilNodeId) {
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
for (const dependent of this.getDependents(nodeId)) {
|
|
385
|
-
if (!visited.has(dependent)) {
|
|
386
|
-
queue.push(dependent);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return result;
|
|
391
|
-
}
|
|
392
|
-
getDefinition() {
|
|
393
|
-
return this.workflow;
|
|
394
|
-
}
|
|
395
|
-
getNode(nodeId) {
|
|
396
|
-
return this.nodeMap.get(nodeId);
|
|
397
|
-
}
|
|
398
|
-
getNodeIds() {
|
|
399
|
-
return Array.from(this.nodeMap.keys());
|
|
400
|
-
}
|
|
401
|
-
getNodeCount() {
|
|
402
|
-
return this.nodeMap.size;
|
|
403
|
-
}
|
|
404
|
-
buildEdges(explicitEdges = []) {
|
|
405
|
-
const derivedEdges = [];
|
|
406
|
-
const explicitEdgeSet = new Set(explicitEdges.map((e) => `${e.from}->${e.to}`));
|
|
407
|
-
for (const node of this.workflow.nodes) {
|
|
408
|
-
const deps = node.depends_on || [];
|
|
409
|
-
for (const dep of deps) {
|
|
410
|
-
const edgeKey = `${dep}->${node.id}`;
|
|
411
|
-
if (!explicitEdgeSet.has(edgeKey)) {
|
|
412
|
-
derivedEdges.push({ from: dep, to: node.id });
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
return [...explicitEdges, ...derivedEdges];
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
var init_dag_manager = () => {};
|
|
420
|
-
|
|
421
|
-
export { DAGManager, init_dag_manager };
|