@inf-minds/coordination-modes 0.0.1
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/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/modes/index.d.ts +3 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +5 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/manager-workers.d.ts +48 -0
- package/dist/modes/manager-workers.d.ts.map +1 -0
- package/dist/modes/manager-workers.js +59 -0
- package/dist/modes/manager-workers.js.map +1 -0
- package/dist/modes/pipeline.d.ts +36 -0
- package/dist/modes/pipeline.d.ts.map +1 -0
- package/dist/modes/pipeline.js +57 -0
- package/dist/modes/pipeline.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +236 -0
- package/dist/validation.js.map +1 -0
- package/package.json +35 -0
- package/src/index.ts +30 -0
- package/src/modes/index.ts +8 -0
- package/src/modes/manager-workers.ts +98 -0
- package/src/modes/pipeline.ts +86 -0
- package/src/types.ts +118 -0
- package/src/validation.ts +278 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { MindRef, GraphNode, GraphEdge, ExecutionGraph, ArtifactMapping, RoutingRules, CoordinationMode, ValidationResult, } from './types.js';
|
|
2
|
+
export { validateGraph, isAcyclic, getTopologicalOrder, } from './validation.js';
|
|
3
|
+
export { createPipelineMode, type PipelineOptions, type PipelineStep, createManagerWorkersMode, type ManagerWorkersOptions, } from './modes/index.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,SAAS,EACT,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,wBAAwB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// ABOUTME: Coordination modes package entry point
|
|
2
|
+
// ABOUTME: Exports types, validation, and built-in modes
|
|
3
|
+
// Validation exports
|
|
4
|
+
export { validateGraph, isAcyclic, getTopologicalOrder, } from './validation.js';
|
|
5
|
+
// Mode exports
|
|
6
|
+
export { createPipelineMode, createManagerWorkersMode, } from './modes/index.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,yDAAyD;AAczD,qBAAqB;AACrB,OAAO,EACL,aAAa,EACb,SAAS,EACT,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,eAAe;AACf,OAAO,EACL,kBAAkB,EAGlB,wBAAwB,GAEzB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/modes/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EACL,wBAAwB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/modes/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,+DAA+D;AAE/D,OAAO,EAAE,kBAAkB,EAA2C,MAAM,eAAe,CAAC;AAC5F,OAAO,EACL,wBAAwB,GAEzB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { CoordinationMode, MindRef } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for creating a manager-workers mode.
|
|
4
|
+
*/
|
|
5
|
+
export interface ManagerWorkersOptions {
|
|
6
|
+
/** Name of the mode */
|
|
7
|
+
name?: string;
|
|
8
|
+
/** Description of the mode */
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Mind for the manager role */
|
|
11
|
+
managerMind: MindRef;
|
|
12
|
+
/** Mind for the worker role */
|
|
13
|
+
workerMind: MindRef;
|
|
14
|
+
/** Maximum iterations for the manager loop (default: 10) */
|
|
15
|
+
maxIterations?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Exit condition for the manager.
|
|
18
|
+
* Evaluated against manager's output.
|
|
19
|
+
* Default: 'output.allTasksComplete'
|
|
20
|
+
*/
|
|
21
|
+
exitCondition?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Delegation condition.
|
|
24
|
+
* If true, manager delegates to worker.
|
|
25
|
+
* Default: 'output.delegatedTask'
|
|
26
|
+
*/
|
|
27
|
+
delegationCondition?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a manager-workers coordination mode.
|
|
31
|
+
*
|
|
32
|
+
* The manager orchestrates work by delegating tasks to workers.
|
|
33
|
+
* After each worker completes, control returns to the manager
|
|
34
|
+
* who decides whether to delegate more work or complete.
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* +--> worker --+
|
|
38
|
+
* | |
|
|
39
|
+
* --> manager <------+
|
|
40
|
+
* |
|
|
41
|
+
* +--> [exit when complete]
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param options - Manager-workers configuration
|
|
45
|
+
* @returns A CoordinationMode for manager-workers pattern
|
|
46
|
+
*/
|
|
47
|
+
export declare function createManagerWorkersMode(options: ManagerWorkersOptions): CoordinationMode;
|
|
48
|
+
//# sourceMappingURL=manager-workers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager-workers.d.ts","sourceRoot":"","sources":["../../src/modes/manager-workers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,+BAA+B;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,gBAAgB,CA8CzF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// ABOUTME: Manager-workers coordination mode
|
|
2
|
+
// ABOUTME: Central manager delegates tasks to workers in a loop
|
|
3
|
+
/**
|
|
4
|
+
* Create a manager-workers coordination mode.
|
|
5
|
+
*
|
|
6
|
+
* The manager orchestrates work by delegating tasks to workers.
|
|
7
|
+
* After each worker completes, control returns to the manager
|
|
8
|
+
* who decides whether to delegate more work or complete.
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* +--> worker --+
|
|
12
|
+
* | |
|
|
13
|
+
* --> manager <------+
|
|
14
|
+
* |
|
|
15
|
+
* +--> [exit when complete]
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param options - Manager-workers configuration
|
|
19
|
+
* @returns A CoordinationMode for manager-workers pattern
|
|
20
|
+
*/
|
|
21
|
+
export function createManagerWorkersMode(options) {
|
|
22
|
+
const { name = 'manager-workers', description, managerMind, workerMind, maxIterations = 10, exitCondition = 'output.allTasksComplete', delegationCondition = 'output.delegatedTask', } = options;
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
25
|
+
description,
|
|
26
|
+
graph: {
|
|
27
|
+
nodes: {
|
|
28
|
+
manager: {
|
|
29
|
+
id: 'manager',
|
|
30
|
+
mind: managerMind,
|
|
31
|
+
maxIterations,
|
|
32
|
+
exitCondition,
|
|
33
|
+
},
|
|
34
|
+
worker: {
|
|
35
|
+
id: 'worker',
|
|
36
|
+
mind: workerMind,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
edges: [
|
|
40
|
+
{
|
|
41
|
+
from: 'manager',
|
|
42
|
+
to: 'worker',
|
|
43
|
+
condition: delegationCondition,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
from: 'worker',
|
|
47
|
+
to: 'manager',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
entryNode: 'manager',
|
|
51
|
+
exitNodes: ['manager'],
|
|
52
|
+
},
|
|
53
|
+
defaultRouting: {
|
|
54
|
+
defaultVisibility: 'session',
|
|
55
|
+
autoPropagateArtifacts: true,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=manager-workers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager-workers.js","sourceRoot":"","sources":["../../src/modes/manager-workers.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,gEAAgE;AAgChE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAA8B;IACrE,MAAM,EACJ,IAAI,GAAG,iBAAiB,EACxB,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,GAAG,EAAE,EAClB,aAAa,GAAG,yBAAyB,EACzC,mBAAmB,GAAG,sBAAsB,GAC7C,GAAG,OAAO,CAAC;IAEZ,OAAO;QACL,IAAI;QACJ,WAAW;QACX,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,OAAO,EAAE;oBACP,EAAE,EAAE,SAAS;oBACb,IAAI,EAAE,WAAW;oBACjB,aAAa;oBACb,aAAa;iBACd;gBACD,MAAM,EAAE;oBACN,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,UAAU;iBACjB;aACF;YACD,KAAK,EAAE;gBACL;oBACE,IAAI,EAAE,SAAS;oBACf,EAAE,EAAE,QAAQ;oBACZ,SAAS,EAAE,mBAAmB;iBAC/B;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,EAAE,EAAE,SAAS;iBACd;aACF;YACD,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,CAAC,SAAS,CAAC;SACvB;QACD,cAAc,EAAE;YACd,iBAAiB,EAAE,SAAS;YAC5B,sBAAsB,EAAE,IAAI;SAC7B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CoordinationMode, MindRef } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A step in the pipeline.
|
|
4
|
+
*/
|
|
5
|
+
export interface PipelineStep {
|
|
6
|
+
/** Unique identifier for this step */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Mind to execute at this step */
|
|
9
|
+
mind: MindRef;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Options for creating a pipeline mode.
|
|
13
|
+
*/
|
|
14
|
+
export interface PipelineOptions {
|
|
15
|
+
/** Name of the pipeline */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Description of the pipeline */
|
|
18
|
+
description?: string;
|
|
19
|
+
/** Steps in the pipeline */
|
|
20
|
+
steps: PipelineStep[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a pipeline coordination mode.
|
|
24
|
+
*
|
|
25
|
+
* A pipeline executes minds sequentially, passing artifacts
|
|
26
|
+
* from one step to the next.
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* step1 -> step2 -> step3 -> ... -> stepN
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @param options - Pipeline configuration
|
|
33
|
+
* @returns A CoordinationMode for sequential execution
|
|
34
|
+
*/
|
|
35
|
+
export declare function createPipelineMode(options: PipelineOptions): CoordinationMode;
|
|
36
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/modes/pipeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAwB,MAAM,aAAa,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,mCAAmC;IACnC,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,gBAAgB,CA6C7E"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// ABOUTME: Pipeline coordination mode
|
|
2
|
+
// ABOUTME: Sequential execution of minds in a linear chain
|
|
3
|
+
/**
|
|
4
|
+
* Create a pipeline coordination mode.
|
|
5
|
+
*
|
|
6
|
+
* A pipeline executes minds sequentially, passing artifacts
|
|
7
|
+
* from one step to the next.
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* step1 -> step2 -> step3 -> ... -> stepN
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @param options - Pipeline configuration
|
|
14
|
+
* @returns A CoordinationMode for sequential execution
|
|
15
|
+
*/
|
|
16
|
+
export function createPipelineMode(options) {
|
|
17
|
+
const { steps, name = 'pipeline', description } = options;
|
|
18
|
+
if (steps.length === 0) {
|
|
19
|
+
throw new Error('Pipeline must have at least one step');
|
|
20
|
+
}
|
|
21
|
+
// Build nodes
|
|
22
|
+
const nodes = {};
|
|
23
|
+
for (const step of steps) {
|
|
24
|
+
nodes[step.id] = {
|
|
25
|
+
id: step.id,
|
|
26
|
+
mind: step.mind,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Build edges (connect each step to the next)
|
|
30
|
+
const edges = [];
|
|
31
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
32
|
+
const current = steps[i];
|
|
33
|
+
const next = steps[i + 1];
|
|
34
|
+
edges.push({
|
|
35
|
+
from: current.id,
|
|
36
|
+
to: next.id,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Safe to assert since we checked steps.length > 0
|
|
40
|
+
const firstStep = steps[0];
|
|
41
|
+
const lastStep = steps[steps.length - 1];
|
|
42
|
+
return {
|
|
43
|
+
name,
|
|
44
|
+
description,
|
|
45
|
+
graph: {
|
|
46
|
+
nodes,
|
|
47
|
+
edges,
|
|
48
|
+
entryNode: firstStep.id,
|
|
49
|
+
exitNodes: [lastStep.id],
|
|
50
|
+
},
|
|
51
|
+
defaultRouting: {
|
|
52
|
+
defaultVisibility: 'session',
|
|
53
|
+
autoPropagateArtifacts: true,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/modes/pipeline.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,2DAA2D;AA0B3D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAwB;IACzD,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAE1D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,cAAc;IACd,MAAM,KAAK,GAA8B,EAAE,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG;YACf,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,OAAO,CAAC,EAAE;YAChB,EAAE,EAAE,IAAI,CAAC,EAAE;SACZ,CAAC,CAAC;IACL,CAAC;IAED,mDAAmD;IACnD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAE1C,OAAO;QACL,IAAI;QACJ,WAAW;QACX,KAAK,EAAE;YACL,KAAK;YACL,KAAK;YACL,SAAS,EAAE,SAAS,CAAC,EAAE;YACvB,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;SACzB;QACD,cAAc,EAAE;YACd,iBAAiB,EAAE,SAAS;YAC5B,sBAAsB,EAAE,IAAI;SAC7B;KACF,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { MindConfig } from '@inf-minds/mindkit';
|
|
2
|
+
/**
|
|
3
|
+
* Reference to a mind configuration.
|
|
4
|
+
* Can be inline (with full config) or registered (by name).
|
|
5
|
+
*/
|
|
6
|
+
export interface MindRef {
|
|
7
|
+
/** Type of reference */
|
|
8
|
+
type: 'inline' | 'registered';
|
|
9
|
+
/** Full mind configuration (for inline type) */
|
|
10
|
+
config?: MindConfig;
|
|
11
|
+
/** Name of registered mind type (for registered type) */
|
|
12
|
+
mindType?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A node in the execution graph.
|
|
16
|
+
*/
|
|
17
|
+
export interface GraphNode {
|
|
18
|
+
/** Unique identifier for this node */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Mind to execute at this node */
|
|
21
|
+
mind: MindRef;
|
|
22
|
+
/** Maximum iterations for loops (default: 1) */
|
|
23
|
+
maxIterations?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Exit condition expression.
|
|
26
|
+
* Evaluated against the node's output.
|
|
27
|
+
* If true, the node exits normally.
|
|
28
|
+
* If false and maxIterations not reached, the node re-executes.
|
|
29
|
+
*/
|
|
30
|
+
exitCondition?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Artifact mapping for edge transitions.
|
|
34
|
+
*/
|
|
35
|
+
export interface ArtifactMapping {
|
|
36
|
+
/** Path patterns to include */
|
|
37
|
+
include?: string[];
|
|
38
|
+
/** Path patterns to exclude */
|
|
39
|
+
exclude?: string[];
|
|
40
|
+
/** Rename mappings (from -> to) */
|
|
41
|
+
rename?: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* An edge connecting two nodes in the execution graph.
|
|
45
|
+
*/
|
|
46
|
+
export interface GraphEdge {
|
|
47
|
+
/** Source node ID */
|
|
48
|
+
from: string;
|
|
49
|
+
/** Target node ID */
|
|
50
|
+
to: string;
|
|
51
|
+
/**
|
|
52
|
+
* Condition expression for conditional routing.
|
|
53
|
+
* Evaluated against the source node's output.
|
|
54
|
+
* If true (or no condition), the edge is traversed.
|
|
55
|
+
*/
|
|
56
|
+
condition?: string;
|
|
57
|
+
/** Artifact mapping for this edge */
|
|
58
|
+
artifactMapping?: ArtifactMapping;
|
|
59
|
+
/** Named channel for artifact routing */
|
|
60
|
+
channel?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The complete execution graph.
|
|
64
|
+
*/
|
|
65
|
+
export interface ExecutionGraph {
|
|
66
|
+
/** All nodes in the graph */
|
|
67
|
+
nodes: Record<string, GraphNode>;
|
|
68
|
+
/** All edges connecting nodes */
|
|
69
|
+
edges: GraphEdge[];
|
|
70
|
+
/** ID of the entry node */
|
|
71
|
+
entryNode: string;
|
|
72
|
+
/** IDs of exit nodes (graph completes when one is reached) */
|
|
73
|
+
exitNodes: string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Default routing rules for artifacts.
|
|
77
|
+
*/
|
|
78
|
+
export interface RoutingRules {
|
|
79
|
+
/** Default visibility for artifacts */
|
|
80
|
+
defaultVisibility?: 'private' | 'session' | 'persistent';
|
|
81
|
+
/** Auto-propagate artifacts along edges */
|
|
82
|
+
autoPropagateArtifacts?: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A coordination mode defines how multiple minds work together.
|
|
86
|
+
*/
|
|
87
|
+
export interface CoordinationMode {
|
|
88
|
+
/** Mode name */
|
|
89
|
+
name: string;
|
|
90
|
+
/** Mode description */
|
|
91
|
+
description?: string;
|
|
92
|
+
/** The execution graph */
|
|
93
|
+
graph: ExecutionGraph;
|
|
94
|
+
/** Default routing rules */
|
|
95
|
+
defaultRouting?: RoutingRules;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Result of graph validation.
|
|
99
|
+
*/
|
|
100
|
+
export interface ValidationResult {
|
|
101
|
+
/** Whether the graph is valid */
|
|
102
|
+
valid: boolean;
|
|
103
|
+
/** Validation errors */
|
|
104
|
+
errors: string[];
|
|
105
|
+
/** Validation warnings */
|
|
106
|
+
warnings: string[];
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,wBAAwB;IACxB,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9B,gDAAgD;IAChD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,mCAAmC;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;IACX;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,iCAAiC;IACjC,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;IACzD,2CAA2C;IAC3C,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,KAAK,EAAE,cAAc,CAAC;IACtB,4BAA4B;IAC5B,cAAc,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,wBAAwB;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,0EAA0E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ExecutionGraph, ValidationResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate an execution graph for correctness.
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* - Entry node exists
|
|
7
|
+
* - All exit nodes exist
|
|
8
|
+
* - All edges reference valid nodes
|
|
9
|
+
* - All nodes are reachable from entry
|
|
10
|
+
* - No infinite cycles (cycles must have exit conditions)
|
|
11
|
+
*
|
|
12
|
+
* @param graph - The execution graph to validate
|
|
13
|
+
* @returns Validation result with errors and warnings
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateGraph(graph: ExecutionGraph): ValidationResult;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a graph is acyclic.
|
|
18
|
+
*
|
|
19
|
+
* @param graph - The execution graph to check
|
|
20
|
+
* @returns true if the graph has no cycles
|
|
21
|
+
*/
|
|
22
|
+
export declare function isAcyclic(graph: ExecutionGraph): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Get topological order of nodes in an acyclic graph.
|
|
25
|
+
*
|
|
26
|
+
* @param graph - The execution graph (must be acyclic)
|
|
27
|
+
* @returns Array of node IDs in topological order, or null if graph has cycles
|
|
28
|
+
*/
|
|
29
|
+
export declare function getTopologicalOrder(graph: ExecutionGraph): string[] | null;
|
|
30
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAmDrE;AA8GD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CA4BxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,GAAG,IAAI,CAwD1E"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// ABOUTME: Graph validation for execution graphs
|
|
2
|
+
// ABOUTME: Validates structure, reachability, and detects problematic cycles
|
|
3
|
+
/**
|
|
4
|
+
* Validate an execution graph for correctness.
|
|
5
|
+
*
|
|
6
|
+
* Checks:
|
|
7
|
+
* - Entry node exists
|
|
8
|
+
* - All exit nodes exist
|
|
9
|
+
* - All edges reference valid nodes
|
|
10
|
+
* - All nodes are reachable from entry
|
|
11
|
+
* - No infinite cycles (cycles must have exit conditions)
|
|
12
|
+
*
|
|
13
|
+
* @param graph - The execution graph to validate
|
|
14
|
+
* @returns Validation result with errors and warnings
|
|
15
|
+
*/
|
|
16
|
+
export function validateGraph(graph) {
|
|
17
|
+
const errors = [];
|
|
18
|
+
const warnings = [];
|
|
19
|
+
// Check entry node exists
|
|
20
|
+
if (!graph.nodes[graph.entryNode]) {
|
|
21
|
+
errors.push(`Entry node '${graph.entryNode}' not found in nodes`);
|
|
22
|
+
}
|
|
23
|
+
// Check exit nodes exist
|
|
24
|
+
for (const exitNode of graph.exitNodes) {
|
|
25
|
+
if (!graph.nodes[exitNode]) {
|
|
26
|
+
errors.push(`Exit node '${exitNode}' not found in nodes`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Check edges reference valid nodes
|
|
30
|
+
for (const edge of graph.edges) {
|
|
31
|
+
if (!graph.nodes[edge.from]) {
|
|
32
|
+
errors.push(`Edge references unknown source node '${edge.from}'`);
|
|
33
|
+
}
|
|
34
|
+
if (!graph.nodes[edge.to]) {
|
|
35
|
+
errors.push(`Edge references unknown target node '${edge.to}'`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check all nodes are reachable from entry
|
|
39
|
+
const reachable = findReachableNodes(graph);
|
|
40
|
+
for (const nodeId of Object.keys(graph.nodes)) {
|
|
41
|
+
if (!reachable.has(nodeId)) {
|
|
42
|
+
warnings.push(`Node '${nodeId}' is not reachable from entry node`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Check for infinite cycles
|
|
46
|
+
const cycleIssues = detectInfiniteCycles(graph);
|
|
47
|
+
for (const issue of cycleIssues) {
|
|
48
|
+
errors.push(issue);
|
|
49
|
+
}
|
|
50
|
+
// Check that at least one exit node is reachable
|
|
51
|
+
const hasReachableExit = graph.exitNodes.some((exit) => reachable.has(exit));
|
|
52
|
+
if (!hasReachableExit && graph.exitNodes.length > 0) {
|
|
53
|
+
errors.push('No exit node is reachable from entry node');
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
valid: errors.length === 0,
|
|
57
|
+
errors,
|
|
58
|
+
warnings,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find all nodes reachable from the entry node.
|
|
63
|
+
*/
|
|
64
|
+
function findReachableNodes(graph) {
|
|
65
|
+
const reachable = new Set();
|
|
66
|
+
const queue = [graph.entryNode];
|
|
67
|
+
while (queue.length > 0) {
|
|
68
|
+
const current = queue.shift();
|
|
69
|
+
if (reachable.has(current)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
reachable.add(current);
|
|
73
|
+
// Find all edges from this node
|
|
74
|
+
for (const edge of graph.edges) {
|
|
75
|
+
if (edge.from === current && !reachable.has(edge.to)) {
|
|
76
|
+
queue.push(edge.to);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Self-loops (nodes with maxIterations > 1)
|
|
80
|
+
const node = graph.nodes[current];
|
|
81
|
+
if (node?.maxIterations && node.maxIterations > 1) {
|
|
82
|
+
// Node can loop back to itself
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return reachable;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Detect cycles that could cause infinite loops.
|
|
89
|
+
*
|
|
90
|
+
* A cycle is problematic if:
|
|
91
|
+
* - It has no conditional edges
|
|
92
|
+
* - Nodes in the cycle have no exit conditions
|
|
93
|
+
*/
|
|
94
|
+
function detectInfiniteCycles(graph) {
|
|
95
|
+
const issues = [];
|
|
96
|
+
const visited = new Set();
|
|
97
|
+
const recursionStack = new Set();
|
|
98
|
+
function dfs(nodeId, path) {
|
|
99
|
+
if (recursionStack.has(nodeId)) {
|
|
100
|
+
// Found a cycle - check if it's safe
|
|
101
|
+
const cycleStart = path.indexOf(nodeId);
|
|
102
|
+
const cycle = path.slice(cycleStart);
|
|
103
|
+
// Check if any node in the cycle has an exit condition
|
|
104
|
+
let hasSafeExit = false;
|
|
105
|
+
for (const cycleNodeId of cycle) {
|
|
106
|
+
const node = graph.nodes[cycleNodeId];
|
|
107
|
+
if (node?.exitCondition) {
|
|
108
|
+
hasSafeExit = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
// Check if node has limited iterations
|
|
112
|
+
if (node?.maxIterations && node.maxIterations > 1 && node.maxIterations < Infinity) {
|
|
113
|
+
hasSafeExit = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Check if any edge in the cycle is conditional
|
|
118
|
+
for (let i = 0; i < cycle.length; i++) {
|
|
119
|
+
const from = cycle[i];
|
|
120
|
+
const to = cycle[(i + 1) % cycle.length];
|
|
121
|
+
const edge = graph.edges.find((e) => e.from === from && e.to === to);
|
|
122
|
+
if (edge?.condition) {
|
|
123
|
+
hasSafeExit = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!hasSafeExit) {
|
|
128
|
+
issues.push(`Potential infinite cycle detected: ${cycle.join(' -> ')} -> ${nodeId}`);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (visited.has(nodeId)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
visited.add(nodeId);
|
|
136
|
+
recursionStack.add(nodeId);
|
|
137
|
+
// Visit all successors
|
|
138
|
+
for (const edge of graph.edges) {
|
|
139
|
+
if (edge.from === nodeId) {
|
|
140
|
+
dfs(edge.to, [...path, nodeId]);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
recursionStack.delete(nodeId);
|
|
144
|
+
}
|
|
145
|
+
// Start DFS from entry node
|
|
146
|
+
if (graph.nodes[graph.entryNode]) {
|
|
147
|
+
dfs(graph.entryNode, []);
|
|
148
|
+
}
|
|
149
|
+
return issues;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if a graph is acyclic.
|
|
153
|
+
*
|
|
154
|
+
* @param graph - The execution graph to check
|
|
155
|
+
* @returns true if the graph has no cycles
|
|
156
|
+
*/
|
|
157
|
+
export function isAcyclic(graph) {
|
|
158
|
+
const visited = new Set();
|
|
159
|
+
const recursionStack = new Set();
|
|
160
|
+
function hasCycle(nodeId) {
|
|
161
|
+
if (recursionStack.has(nodeId)) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (visited.has(nodeId)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
visited.add(nodeId);
|
|
168
|
+
recursionStack.add(nodeId);
|
|
169
|
+
for (const edge of graph.edges) {
|
|
170
|
+
if (edge.from === nodeId) {
|
|
171
|
+
if (hasCycle(edge.to)) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
recursionStack.delete(nodeId);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
return !hasCycle(graph.entryNode);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get topological order of nodes in an acyclic graph.
|
|
183
|
+
*
|
|
184
|
+
* @param graph - The execution graph (must be acyclic)
|
|
185
|
+
* @returns Array of node IDs in topological order, or null if graph has cycles
|
|
186
|
+
*/
|
|
187
|
+
export function getTopologicalOrder(graph) {
|
|
188
|
+
if (!isAcyclic(graph)) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const inDegree = {};
|
|
192
|
+
const order = [];
|
|
193
|
+
// Initialize in-degree for all nodes
|
|
194
|
+
for (const nodeId of Object.keys(graph.nodes)) {
|
|
195
|
+
inDegree[nodeId] = 0;
|
|
196
|
+
}
|
|
197
|
+
// Count in-degrees
|
|
198
|
+
for (const edge of graph.edges) {
|
|
199
|
+
const currentDegree = inDegree[edge.to];
|
|
200
|
+
if (currentDegree !== undefined) {
|
|
201
|
+
inDegree[edge.to] = currentDegree + 1;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Start with nodes that have no incoming edges
|
|
205
|
+
const queue = [];
|
|
206
|
+
for (const [nodeId, degree] of Object.entries(inDegree)) {
|
|
207
|
+
if (degree === 0) {
|
|
208
|
+
queue.push(nodeId);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Process queue
|
|
212
|
+
while (queue.length > 0) {
|
|
213
|
+
const current = queue.shift();
|
|
214
|
+
order.push(current);
|
|
215
|
+
// Decrease in-degree for successors
|
|
216
|
+
for (const edge of graph.edges) {
|
|
217
|
+
if (edge.from === current) {
|
|
218
|
+
const currentDegree = inDegree[edge.to];
|
|
219
|
+
if (currentDegree !== undefined) {
|
|
220
|
+
const newDegree = currentDegree - 1;
|
|
221
|
+
inDegree[edge.to] = newDegree;
|
|
222
|
+
if (newDegree === 0) {
|
|
223
|
+
queue.push(edge.to);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// If we processed all nodes, return order
|
|
230
|
+
if (order.length === Object.keys(graph.nodes).length) {
|
|
231
|
+
return order;
|
|
232
|
+
}
|
|
233
|
+
// Graph has a cycle (shouldn't happen since we checked isAcyclic)
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inf-minds/coordination-modes",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Execution graph definitions for multi-mind coordination",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"lint": "echo 'No linter configured yet'"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@inf-minds/mindkit": "workspace:*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"typescript": "^5.3.0",
|
|
33
|
+
"vitest": "^2.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// ABOUTME: Coordination modes package entry point
|
|
2
|
+
// ABOUTME: Exports types, validation, and built-in modes
|
|
3
|
+
|
|
4
|
+
// Type exports
|
|
5
|
+
export type {
|
|
6
|
+
MindRef,
|
|
7
|
+
GraphNode,
|
|
8
|
+
GraphEdge,
|
|
9
|
+
ExecutionGraph,
|
|
10
|
+
ArtifactMapping,
|
|
11
|
+
RoutingRules,
|
|
12
|
+
CoordinationMode,
|
|
13
|
+
ValidationResult,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
|
|
16
|
+
// Validation exports
|
|
17
|
+
export {
|
|
18
|
+
validateGraph,
|
|
19
|
+
isAcyclic,
|
|
20
|
+
getTopologicalOrder,
|
|
21
|
+
} from './validation.js';
|
|
22
|
+
|
|
23
|
+
// Mode exports
|
|
24
|
+
export {
|
|
25
|
+
createPipelineMode,
|
|
26
|
+
type PipelineOptions,
|
|
27
|
+
type PipelineStep,
|
|
28
|
+
createManagerWorkersMode,
|
|
29
|
+
type ManagerWorkersOptions,
|
|
30
|
+
} from './modes/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// ABOUTME: Built-in coordination modes
|
|
2
|
+
// ABOUTME: Exports pipeline and manager-workers mode factories
|
|
3
|
+
|
|
4
|
+
export { createPipelineMode, type PipelineOptions, type PipelineStep } from './pipeline.js';
|
|
5
|
+
export {
|
|
6
|
+
createManagerWorkersMode,
|
|
7
|
+
type ManagerWorkersOptions,
|
|
8
|
+
} from './manager-workers.js';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ABOUTME: Manager-workers coordination mode
|
|
2
|
+
// ABOUTME: Central manager delegates tasks to workers in a loop
|
|
3
|
+
|
|
4
|
+
import type { CoordinationMode, MindRef } from '../types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a manager-workers mode.
|
|
8
|
+
*/
|
|
9
|
+
export interface ManagerWorkersOptions {
|
|
10
|
+
/** Name of the mode */
|
|
11
|
+
name?: string;
|
|
12
|
+
/** Description of the mode */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Mind for the manager role */
|
|
15
|
+
managerMind: MindRef;
|
|
16
|
+
/** Mind for the worker role */
|
|
17
|
+
workerMind: MindRef;
|
|
18
|
+
/** Maximum iterations for the manager loop (default: 10) */
|
|
19
|
+
maxIterations?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Exit condition for the manager.
|
|
22
|
+
* Evaluated against manager's output.
|
|
23
|
+
* Default: 'output.allTasksComplete'
|
|
24
|
+
*/
|
|
25
|
+
exitCondition?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Delegation condition.
|
|
28
|
+
* If true, manager delegates to worker.
|
|
29
|
+
* Default: 'output.delegatedTask'
|
|
30
|
+
*/
|
|
31
|
+
delegationCondition?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a manager-workers coordination mode.
|
|
36
|
+
*
|
|
37
|
+
* The manager orchestrates work by delegating tasks to workers.
|
|
38
|
+
* After each worker completes, control returns to the manager
|
|
39
|
+
* who decides whether to delegate more work or complete.
|
|
40
|
+
*
|
|
41
|
+
* ```
|
|
42
|
+
* +--> worker --+
|
|
43
|
+
* | |
|
|
44
|
+
* --> manager <------+
|
|
45
|
+
* |
|
|
46
|
+
* +--> [exit when complete]
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param options - Manager-workers configuration
|
|
50
|
+
* @returns A CoordinationMode for manager-workers pattern
|
|
51
|
+
*/
|
|
52
|
+
export function createManagerWorkersMode(options: ManagerWorkersOptions): CoordinationMode {
|
|
53
|
+
const {
|
|
54
|
+
name = 'manager-workers',
|
|
55
|
+
description,
|
|
56
|
+
managerMind,
|
|
57
|
+
workerMind,
|
|
58
|
+
maxIterations = 10,
|
|
59
|
+
exitCondition = 'output.allTasksComplete',
|
|
60
|
+
delegationCondition = 'output.delegatedTask',
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
description,
|
|
66
|
+
graph: {
|
|
67
|
+
nodes: {
|
|
68
|
+
manager: {
|
|
69
|
+
id: 'manager',
|
|
70
|
+
mind: managerMind,
|
|
71
|
+
maxIterations,
|
|
72
|
+
exitCondition,
|
|
73
|
+
},
|
|
74
|
+
worker: {
|
|
75
|
+
id: 'worker',
|
|
76
|
+
mind: workerMind,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
edges: [
|
|
80
|
+
{
|
|
81
|
+
from: 'manager',
|
|
82
|
+
to: 'worker',
|
|
83
|
+
condition: delegationCondition,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
from: 'worker',
|
|
87
|
+
to: 'manager',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
entryNode: 'manager',
|
|
91
|
+
exitNodes: ['manager'],
|
|
92
|
+
},
|
|
93
|
+
defaultRouting: {
|
|
94
|
+
defaultVisibility: 'session',
|
|
95
|
+
autoPropagateArtifacts: true,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// ABOUTME: Pipeline coordination mode
|
|
2
|
+
// ABOUTME: Sequential execution of minds in a linear chain
|
|
3
|
+
|
|
4
|
+
import type { CoordinationMode, MindRef, GraphNode, GraphEdge } from '../types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A step in the pipeline.
|
|
8
|
+
*/
|
|
9
|
+
export interface PipelineStep {
|
|
10
|
+
/** Unique identifier for this step */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Mind to execute at this step */
|
|
13
|
+
mind: MindRef;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating a pipeline mode.
|
|
18
|
+
*/
|
|
19
|
+
export interface PipelineOptions {
|
|
20
|
+
/** Name of the pipeline */
|
|
21
|
+
name?: string;
|
|
22
|
+
/** Description of the pipeline */
|
|
23
|
+
description?: string;
|
|
24
|
+
/** Steps in the pipeline */
|
|
25
|
+
steps: PipelineStep[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a pipeline coordination mode.
|
|
30
|
+
*
|
|
31
|
+
* A pipeline executes minds sequentially, passing artifacts
|
|
32
|
+
* from one step to the next.
|
|
33
|
+
*
|
|
34
|
+
* ```
|
|
35
|
+
* step1 -> step2 -> step3 -> ... -> stepN
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @param options - Pipeline configuration
|
|
39
|
+
* @returns A CoordinationMode for sequential execution
|
|
40
|
+
*/
|
|
41
|
+
export function createPipelineMode(options: PipelineOptions): CoordinationMode {
|
|
42
|
+
const { steps, name = 'pipeline', description } = options;
|
|
43
|
+
|
|
44
|
+
if (steps.length === 0) {
|
|
45
|
+
throw new Error('Pipeline must have at least one step');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Build nodes
|
|
49
|
+
const nodes: Record<string, GraphNode> = {};
|
|
50
|
+
for (const step of steps) {
|
|
51
|
+
nodes[step.id] = {
|
|
52
|
+
id: step.id,
|
|
53
|
+
mind: step.mind,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Build edges (connect each step to the next)
|
|
58
|
+
const edges: GraphEdge[] = [];
|
|
59
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
60
|
+
const current = steps[i]!;
|
|
61
|
+
const next = steps[i + 1]!;
|
|
62
|
+
edges.push({
|
|
63
|
+
from: current.id,
|
|
64
|
+
to: next.id,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Safe to assert since we checked steps.length > 0
|
|
69
|
+
const firstStep = steps[0]!;
|
|
70
|
+
const lastStep = steps[steps.length - 1]!;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
description,
|
|
75
|
+
graph: {
|
|
76
|
+
nodes,
|
|
77
|
+
edges,
|
|
78
|
+
entryNode: firstStep.id,
|
|
79
|
+
exitNodes: [lastStep.id],
|
|
80
|
+
},
|
|
81
|
+
defaultRouting: {
|
|
82
|
+
defaultVisibility: 'session',
|
|
83
|
+
autoPropagateArtifacts: true,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// ABOUTME: Core types for coordination modes
|
|
2
|
+
// ABOUTME: Defines CoordinationMode, ExecutionGraph, GraphNode, GraphEdge
|
|
3
|
+
|
|
4
|
+
import type { MindConfig } from '@inf-minds/mindkit';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Reference to a mind configuration.
|
|
8
|
+
* Can be inline (with full config) or registered (by name).
|
|
9
|
+
*/
|
|
10
|
+
export interface MindRef {
|
|
11
|
+
/** Type of reference */
|
|
12
|
+
type: 'inline' | 'registered';
|
|
13
|
+
/** Full mind configuration (for inline type) */
|
|
14
|
+
config?: MindConfig;
|
|
15
|
+
/** Name of registered mind type (for registered type) */
|
|
16
|
+
mindType?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A node in the execution graph.
|
|
21
|
+
*/
|
|
22
|
+
export interface GraphNode {
|
|
23
|
+
/** Unique identifier for this node */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Mind to execute at this node */
|
|
26
|
+
mind: MindRef;
|
|
27
|
+
/** Maximum iterations for loops (default: 1) */
|
|
28
|
+
maxIterations?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Exit condition expression.
|
|
31
|
+
* Evaluated against the node's output.
|
|
32
|
+
* If true, the node exits normally.
|
|
33
|
+
* If false and maxIterations not reached, the node re-executes.
|
|
34
|
+
*/
|
|
35
|
+
exitCondition?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Artifact mapping for edge transitions.
|
|
40
|
+
*/
|
|
41
|
+
export interface ArtifactMapping {
|
|
42
|
+
/** Path patterns to include */
|
|
43
|
+
include?: string[];
|
|
44
|
+
/** Path patterns to exclude */
|
|
45
|
+
exclude?: string[];
|
|
46
|
+
/** Rename mappings (from -> to) */
|
|
47
|
+
rename?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An edge connecting two nodes in the execution graph.
|
|
52
|
+
*/
|
|
53
|
+
export interface GraphEdge {
|
|
54
|
+
/** Source node ID */
|
|
55
|
+
from: string;
|
|
56
|
+
/** Target node ID */
|
|
57
|
+
to: string;
|
|
58
|
+
/**
|
|
59
|
+
* Condition expression for conditional routing.
|
|
60
|
+
* Evaluated against the source node's output.
|
|
61
|
+
* If true (or no condition), the edge is traversed.
|
|
62
|
+
*/
|
|
63
|
+
condition?: string;
|
|
64
|
+
/** Artifact mapping for this edge */
|
|
65
|
+
artifactMapping?: ArtifactMapping;
|
|
66
|
+
/** Named channel for artifact routing */
|
|
67
|
+
channel?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The complete execution graph.
|
|
72
|
+
*/
|
|
73
|
+
export interface ExecutionGraph {
|
|
74
|
+
/** All nodes in the graph */
|
|
75
|
+
nodes: Record<string, GraphNode>;
|
|
76
|
+
/** All edges connecting nodes */
|
|
77
|
+
edges: GraphEdge[];
|
|
78
|
+
/** ID of the entry node */
|
|
79
|
+
entryNode: string;
|
|
80
|
+
/** IDs of exit nodes (graph completes when one is reached) */
|
|
81
|
+
exitNodes: string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Default routing rules for artifacts.
|
|
86
|
+
*/
|
|
87
|
+
export interface RoutingRules {
|
|
88
|
+
/** Default visibility for artifacts */
|
|
89
|
+
defaultVisibility?: 'private' | 'session' | 'persistent';
|
|
90
|
+
/** Auto-propagate artifacts along edges */
|
|
91
|
+
autoPropagateArtifacts?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A coordination mode defines how multiple minds work together.
|
|
96
|
+
*/
|
|
97
|
+
export interface CoordinationMode {
|
|
98
|
+
/** Mode name */
|
|
99
|
+
name: string;
|
|
100
|
+
/** Mode description */
|
|
101
|
+
description?: string;
|
|
102
|
+
/** The execution graph */
|
|
103
|
+
graph: ExecutionGraph;
|
|
104
|
+
/** Default routing rules */
|
|
105
|
+
defaultRouting?: RoutingRules;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Result of graph validation.
|
|
110
|
+
*/
|
|
111
|
+
export interface ValidationResult {
|
|
112
|
+
/** Whether the graph is valid */
|
|
113
|
+
valid: boolean;
|
|
114
|
+
/** Validation errors */
|
|
115
|
+
errors: string[];
|
|
116
|
+
/** Validation warnings */
|
|
117
|
+
warnings: string[];
|
|
118
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// ABOUTME: Graph validation for execution graphs
|
|
2
|
+
// ABOUTME: Validates structure, reachability, and detects problematic cycles
|
|
3
|
+
|
|
4
|
+
import type { ExecutionGraph, ValidationResult } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validate an execution graph for correctness.
|
|
8
|
+
*
|
|
9
|
+
* Checks:
|
|
10
|
+
* - Entry node exists
|
|
11
|
+
* - All exit nodes exist
|
|
12
|
+
* - All edges reference valid nodes
|
|
13
|
+
* - All nodes are reachable from entry
|
|
14
|
+
* - No infinite cycles (cycles must have exit conditions)
|
|
15
|
+
*
|
|
16
|
+
* @param graph - The execution graph to validate
|
|
17
|
+
* @returns Validation result with errors and warnings
|
|
18
|
+
*/
|
|
19
|
+
export function validateGraph(graph: ExecutionGraph): ValidationResult {
|
|
20
|
+
const errors: string[] = [];
|
|
21
|
+
const warnings: string[] = [];
|
|
22
|
+
|
|
23
|
+
// Check entry node exists
|
|
24
|
+
if (!graph.nodes[graph.entryNode]) {
|
|
25
|
+
errors.push(`Entry node '${graph.entryNode}' not found in nodes`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check exit nodes exist
|
|
29
|
+
for (const exitNode of graph.exitNodes) {
|
|
30
|
+
if (!graph.nodes[exitNode]) {
|
|
31
|
+
errors.push(`Exit node '${exitNode}' not found in nodes`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check edges reference valid nodes
|
|
36
|
+
for (const edge of graph.edges) {
|
|
37
|
+
if (!graph.nodes[edge.from]) {
|
|
38
|
+
errors.push(`Edge references unknown source node '${edge.from}'`);
|
|
39
|
+
}
|
|
40
|
+
if (!graph.nodes[edge.to]) {
|
|
41
|
+
errors.push(`Edge references unknown target node '${edge.to}'`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check all nodes are reachable from entry
|
|
46
|
+
const reachable = findReachableNodes(graph);
|
|
47
|
+
for (const nodeId of Object.keys(graph.nodes)) {
|
|
48
|
+
if (!reachable.has(nodeId)) {
|
|
49
|
+
warnings.push(`Node '${nodeId}' is not reachable from entry node`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check for infinite cycles
|
|
54
|
+
const cycleIssues = detectInfiniteCycles(graph);
|
|
55
|
+
for (const issue of cycleIssues) {
|
|
56
|
+
errors.push(issue);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check that at least one exit node is reachable
|
|
60
|
+
const hasReachableExit = graph.exitNodes.some((exit) => reachable.has(exit));
|
|
61
|
+
if (!hasReachableExit && graph.exitNodes.length > 0) {
|
|
62
|
+
errors.push('No exit node is reachable from entry node');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
valid: errors.length === 0,
|
|
67
|
+
errors,
|
|
68
|
+
warnings,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Find all nodes reachable from the entry node.
|
|
74
|
+
*/
|
|
75
|
+
function findReachableNodes(graph: ExecutionGraph): Set<string> {
|
|
76
|
+
const reachable = new Set<string>();
|
|
77
|
+
const queue: string[] = [graph.entryNode];
|
|
78
|
+
|
|
79
|
+
while (queue.length > 0) {
|
|
80
|
+
const current = queue.shift()!;
|
|
81
|
+
|
|
82
|
+
if (reachable.has(current)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
reachable.add(current);
|
|
87
|
+
|
|
88
|
+
// Find all edges from this node
|
|
89
|
+
for (const edge of graph.edges) {
|
|
90
|
+
if (edge.from === current && !reachable.has(edge.to)) {
|
|
91
|
+
queue.push(edge.to);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Self-loops (nodes with maxIterations > 1)
|
|
96
|
+
const node = graph.nodes[current];
|
|
97
|
+
if (node?.maxIterations && node.maxIterations > 1) {
|
|
98
|
+
// Node can loop back to itself
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return reachable;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Detect cycles that could cause infinite loops.
|
|
107
|
+
*
|
|
108
|
+
* A cycle is problematic if:
|
|
109
|
+
* - It has no conditional edges
|
|
110
|
+
* - Nodes in the cycle have no exit conditions
|
|
111
|
+
*/
|
|
112
|
+
function detectInfiniteCycles(graph: ExecutionGraph): string[] {
|
|
113
|
+
const issues: string[] = [];
|
|
114
|
+
const visited = new Set<string>();
|
|
115
|
+
const recursionStack = new Set<string>();
|
|
116
|
+
|
|
117
|
+
function dfs(nodeId: string, path: string[]): void {
|
|
118
|
+
if (recursionStack.has(nodeId)) {
|
|
119
|
+
// Found a cycle - check if it's safe
|
|
120
|
+
const cycleStart = path.indexOf(nodeId);
|
|
121
|
+
const cycle = path.slice(cycleStart);
|
|
122
|
+
|
|
123
|
+
// Check if any node in the cycle has an exit condition
|
|
124
|
+
let hasSafeExit = false;
|
|
125
|
+
for (const cycleNodeId of cycle) {
|
|
126
|
+
const node = graph.nodes[cycleNodeId];
|
|
127
|
+
if (node?.exitCondition) {
|
|
128
|
+
hasSafeExit = true;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
// Check if node has limited iterations
|
|
132
|
+
if (node?.maxIterations && node.maxIterations > 1 && node.maxIterations < Infinity) {
|
|
133
|
+
hasSafeExit = true;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check if any edge in the cycle is conditional
|
|
139
|
+
for (let i = 0; i < cycle.length; i++) {
|
|
140
|
+
const from = cycle[i];
|
|
141
|
+
const to = cycle[(i + 1) % cycle.length];
|
|
142
|
+
const edge = graph.edges.find((e) => e.from === from && e.to === to);
|
|
143
|
+
if (edge?.condition) {
|
|
144
|
+
hasSafeExit = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!hasSafeExit) {
|
|
150
|
+
issues.push(`Potential infinite cycle detected: ${cycle.join(' -> ')} -> ${nodeId}`);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (visited.has(nodeId)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
visited.add(nodeId);
|
|
160
|
+
recursionStack.add(nodeId);
|
|
161
|
+
|
|
162
|
+
// Visit all successors
|
|
163
|
+
for (const edge of graph.edges) {
|
|
164
|
+
if (edge.from === nodeId) {
|
|
165
|
+
dfs(edge.to, [...path, nodeId]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
recursionStack.delete(nodeId);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Start DFS from entry node
|
|
173
|
+
if (graph.nodes[graph.entryNode]) {
|
|
174
|
+
dfs(graph.entryNode, []);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return issues;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Check if a graph is acyclic.
|
|
182
|
+
*
|
|
183
|
+
* @param graph - The execution graph to check
|
|
184
|
+
* @returns true if the graph has no cycles
|
|
185
|
+
*/
|
|
186
|
+
export function isAcyclic(graph: ExecutionGraph): boolean {
|
|
187
|
+
const visited = new Set<string>();
|
|
188
|
+
const recursionStack = new Set<string>();
|
|
189
|
+
|
|
190
|
+
function hasCycle(nodeId: string): boolean {
|
|
191
|
+
if (recursionStack.has(nodeId)) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (visited.has(nodeId)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
visited.add(nodeId);
|
|
199
|
+
recursionStack.add(nodeId);
|
|
200
|
+
|
|
201
|
+
for (const edge of graph.edges) {
|
|
202
|
+
if (edge.from === nodeId) {
|
|
203
|
+
if (hasCycle(edge.to)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
recursionStack.delete(nodeId);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return !hasCycle(graph.entryNode);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get topological order of nodes in an acyclic graph.
|
|
218
|
+
*
|
|
219
|
+
* @param graph - The execution graph (must be acyclic)
|
|
220
|
+
* @returns Array of node IDs in topological order, or null if graph has cycles
|
|
221
|
+
*/
|
|
222
|
+
export function getTopologicalOrder(graph: ExecutionGraph): string[] | null {
|
|
223
|
+
if (!isAcyclic(graph)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const inDegree: Record<string, number> = {};
|
|
228
|
+
const order: string[] = [];
|
|
229
|
+
|
|
230
|
+
// Initialize in-degree for all nodes
|
|
231
|
+
for (const nodeId of Object.keys(graph.nodes)) {
|
|
232
|
+
inDegree[nodeId] = 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Count in-degrees
|
|
236
|
+
for (const edge of graph.edges) {
|
|
237
|
+
const currentDegree = inDegree[edge.to];
|
|
238
|
+
if (currentDegree !== undefined) {
|
|
239
|
+
inDegree[edge.to] = currentDegree + 1;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Start with nodes that have no incoming edges
|
|
244
|
+
const queue: string[] = [];
|
|
245
|
+
for (const [nodeId, degree] of Object.entries(inDegree)) {
|
|
246
|
+
if (degree === 0) {
|
|
247
|
+
queue.push(nodeId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Process queue
|
|
252
|
+
while (queue.length > 0) {
|
|
253
|
+
const current = queue.shift()!;
|
|
254
|
+
order.push(current);
|
|
255
|
+
|
|
256
|
+
// Decrease in-degree for successors
|
|
257
|
+
for (const edge of graph.edges) {
|
|
258
|
+
if (edge.from === current) {
|
|
259
|
+
const currentDegree = inDegree[edge.to];
|
|
260
|
+
if (currentDegree !== undefined) {
|
|
261
|
+
const newDegree = currentDegree - 1;
|
|
262
|
+
inDegree[edge.to] = newDegree;
|
|
263
|
+
if (newDegree === 0) {
|
|
264
|
+
queue.push(edge.to);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// If we processed all nodes, return order
|
|
272
|
+
if (order.length === Object.keys(graph.nodes).length) {
|
|
273
|
+
return order;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Graph has a cycle (shouldn't happen since we checked isAcyclic)
|
|
277
|
+
return null;
|
|
278
|
+
}
|