@ai.ntellect/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.mocharc.json +1 -1
  2. package/README.md +311 -272
  3. package/dist/graph/controller.js +63 -0
  4. package/dist/graph/engine.js +563 -0
  5. package/dist/index.js +6 -6
  6. package/dist/memory/adapters/meilisearch/index.js +249 -0
  7. package/dist/memory/adapters/redis/index.js +96 -0
  8. package/dist/memory/index.js +9 -0
  9. package/dist/services/agenda.js +115 -0
  10. package/dist/services/embedding.js +40 -0
  11. package/dist/services/queue.js +99 -103
  12. package/dist/test/graph/controller.test.js +170 -0
  13. package/dist/test/graph/engine.test.js +465 -0
  14. package/dist/test/memory/adapters/meilisearch.test.js +250 -0
  15. package/dist/test/memory/adapters/redis.test.js +143 -0
  16. package/dist/test/memory/base.test.js +209 -0
  17. package/dist/test/services/agenda.test.js +230 -0
  18. package/dist/test/services/queue.test.js +258 -0
  19. package/dist/types/index.js +2 -0
  20. package/dist/utils/generate-object.js +32 -11
  21. package/dist/utils/inject-actions.js +2 -2
  22. package/dist/utils/queue-item-transformer.js +2 -2
  23. package/dist/utils/state-manager.js +20 -0
  24. package/graph/controller.ts +60 -0
  25. package/{services/workflow.ts → graph/engine.ts} +331 -113
  26. package/index.ts +7 -7
  27. package/interfaces/index.ts +119 -0
  28. package/memory/adapters/meilisearch/index.ts +286 -0
  29. package/memory/adapters/redis/index.ts +103 -0
  30. package/memory/index.ts +22 -0
  31. package/package.json +7 -2
  32. package/services/agenda.ts +48 -43
  33. package/services/embedding.ts +26 -0
  34. package/services/queue.ts +2 -29
  35. package/test/.env.test +4 -0
  36. package/test/graph/controller.test.ts +186 -0
  37. package/test/graph/engine.test.ts +563 -0
  38. package/test/memory/adapters/meilisearch.test.ts +297 -0
  39. package/test/memory/adapters/redis.test.ts +160 -0
  40. package/test/memory/base.test.ts +229 -0
  41. package/test/services/agenda.test.ts +280 -0
  42. package/test/services/queue.test.ts +286 -44
  43. package/tsconfig.json +10 -10
  44. package/types/index.ts +270 -0
  45. package/utils/generate-object.js +111 -0
  46. package/utils/header-builder.js +34 -0
  47. package/utils/inject-actions.js +16 -0
  48. package/utils/queue-item-transformer.js +24 -0
  49. package/utils/queue-item-transformer.ts +8 -11
  50. package/utils/sanitize-results.js +60 -0
  51. package/utils/schema-generator.js +46 -0
  52. package/utils/state-manager.js +20 -0
  53. package/utils/state-manager.ts +17 -12
  54. package/.nvmrc +0 -1
  55. package/README.FR.md +0 -916
  56. package/agent/index.ts +0 -151
  57. package/agent/workflow/conditions.ts +0 -16
  58. package/agent/workflow/handlers/interpreter.handler.ts +0 -48
  59. package/agent/workflow/handlers/memory.handler.ts +0 -106
  60. package/agent/workflow/handlers/orchestrator.handler.ts +0 -23
  61. package/agent/workflow/handlers/queue.handler.ts +0 -34
  62. package/agent/workflow/handlers/scheduler.handler.ts +0 -61
  63. package/agent/workflow/index.ts +0 -62
  64. package/dist/agent/index.d.ts +0 -38
  65. package/dist/agent/index.js +0 -143
  66. package/dist/agent/tools/get-rss.d.ts +0 -16
  67. package/dist/agent/tools/get-rss.js +0 -62
  68. package/dist/bull.d.ts +0 -1
  69. package/dist/bull.js +0 -9
  70. package/dist/examples/index.d.ts +0 -2
  71. package/dist/examples/index.js +0 -89
  72. package/dist/index.d.ts +0 -7
  73. package/dist/llm/interpreter/context.d.ts +0 -15
  74. package/dist/llm/interpreter/context.js +0 -89
  75. package/dist/llm/interpreter/index.d.ts +0 -21
  76. package/dist/llm/interpreter/index.js +0 -87
  77. package/dist/llm/memory-manager/context.d.ts +0 -2
  78. package/dist/llm/memory-manager/context.js +0 -22
  79. package/dist/llm/memory-manager/index.d.ts +0 -17
  80. package/dist/llm/memory-manager/index.js +0 -107
  81. package/dist/llm/orchestrator/context.d.ts +0 -2
  82. package/dist/llm/orchestrator/context.js +0 -23
  83. package/dist/llm/orchestrator/index.d.ts +0 -44
  84. package/dist/llm/orchestrator/index.js +0 -139
  85. package/dist/llm/orchestrator/types.d.ts +0 -12
  86. package/dist/memory/cache.d.ts +0 -22
  87. package/dist/memory/cache.js +0 -165
  88. package/dist/memory/persistent.d.ts +0 -57
  89. package/dist/memory/persistent.js +0 -189
  90. package/dist/services/queue.d.ts +0 -13
  91. package/dist/services/redis-cache.d.ts +0 -37
  92. package/dist/services/redis-cache.js +0 -93
  93. package/dist/services/scheduler.d.ts +0 -40
  94. package/dist/services/scheduler.js +0 -99
  95. package/dist/services/telegram-monitor.d.ts +0 -0
  96. package/dist/services/telegram-monitor.js +0 -118
  97. package/dist/t.d.ts +0 -46
  98. package/dist/t.js +0 -102
  99. package/dist/test.d.ts +0 -0
  100. package/dist/test.js +0 -438
  101. package/dist/types.d.ts +0 -258
  102. package/dist/types.js +0 -22
  103. package/dist/utils/generate-object.d.ts +0 -12
  104. package/dist/utils/header-builder.d.ts +0 -11
  105. package/dist/utils/inject-actions.d.ts +0 -2
  106. package/dist/utils/queue-item-transformer.d.ts +0 -7
  107. package/dist/utils/sanitize-results.d.ts +0 -17
  108. package/dist/utils/schema-generator.d.ts +0 -16
  109. package/examples/actions/get-rss.ts +0 -71
  110. package/examples/index.ts +0 -98
  111. package/index.html +0 -42
  112. package/llm/dynamic-condition/example.ts +0 -36
  113. package/llm/dynamic-condition/index.ts +0 -108
  114. package/llm/interpreter/context.ts +0 -94
  115. package/llm/interpreter/index.ts +0 -140
  116. package/llm/memory-manager/context.ts +0 -19
  117. package/llm/memory-manager/index.ts +0 -115
  118. package/llm/orchestrator/context.ts +0 -19
  119. package/llm/orchestrator/index.ts +0 -192
  120. package/llm/orchestrator/types.ts +0 -14
  121. package/memory/cache.ts +0 -221
  122. package/memory/persistent.ts +0 -265
  123. package/script.js +0 -167
  124. package/services/cache.ts +0 -298
  125. package/services/telegram-monitor.ts +0 -138
  126. package/t.py +0 -79
  127. package/t.ts +0 -25
  128. package/test/llm/orchestrator.test.ts +0 -47
  129. package/test/llm/synthesizer.test.ts +0 -31
  130. package/types.ts +0 -367
  131. /package/dist/{llm/orchestrator/types.js → interfaces/index.js} +0 -0
@@ -0,0 +1,465 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const engine_1 = require("@/graph/engine");
13
+ const chai_1 = require("chai");
14
+ const zod_1 = require("zod");
15
+ /**
16
+ * Test suite for the Graph service
17
+ * This suite tests the workflow execution engine that manages state transitions and node execution
18
+ */
19
+ describe("Graph", () => {
20
+ /**
21
+ * Test schema definition using Zod
22
+ * Defines the structure and validation rules for the workflow state
23
+ */
24
+ const TestSchema = zod_1.z.object({
25
+ status: zod_1.z.string(),
26
+ step: zod_1.z.number(),
27
+ });
28
+ let graph;
29
+ /**
30
+ * Test definition of a simple workflow graph
31
+ * Contains 3 nodes: start -> process -> end
32
+ * Each node updates the state with new status and step values
33
+ */
34
+ const testDefinition = {
35
+ name: "simple-workflow",
36
+ entryNode: "start",
37
+ nodes: {
38
+ start: {
39
+ name: "start",
40
+ description: "Starting node",
41
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
42
+ return graph.updateState({
43
+ context: Object.assign(Object.assign({}, state.context), { status: "started", step: 1 }),
44
+ });
45
+ }),
46
+ relationships: [{ name: "process" }],
47
+ },
48
+ process: {
49
+ name: "process",
50
+ description: "Processing node",
51
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
52
+ return graph.updateState({
53
+ context: Object.assign(Object.assign({}, state.context), { status: "processing", step: 2 }),
54
+ });
55
+ }),
56
+ condition: (state) => state.context.step === 1,
57
+ relationships: [{ name: "end" }],
58
+ },
59
+ end: {
60
+ name: "end",
61
+ description: "End node",
62
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
63
+ return graph.updateState({
64
+ context: Object.assign(Object.assign({}, state.context), { status: "completed", step: 3 }),
65
+ });
66
+ }),
67
+ relationships: [],
68
+ },
69
+ },
70
+ schema: TestSchema,
71
+ };
72
+ beforeEach(() => {
73
+ graph = new engine_1.GraphEngine(testDefinition);
74
+ });
75
+ describe("Workflow Execution", () => {
76
+ /**
77
+ * Tests the complete execution flow of the workflow
78
+ * Verifies that state transitions occur correctly from start to end
79
+ */
80
+ it("should execute the complete workflow sequence", () => __awaiter(void 0, void 0, void 0, function* () {
81
+ const initialState = {
82
+ context: {
83
+ status: "init",
84
+ step: 0,
85
+ },
86
+ };
87
+ // Initialiser le graph avec l'état initial
88
+ graph = new engine_1.GraphEngine(testDefinition, {
89
+ schema: TestSchema,
90
+ initialState,
91
+ });
92
+ // Exécuter le workflow
93
+ yield graph.execute(initialState, "start");
94
+ const result = graph.getState();
95
+ (0, chai_1.expect)(result.context).to.deep.equal({
96
+ status: "completed",
97
+ step: 3,
98
+ });
99
+ }));
100
+ /**
101
+ * Tests that conditional logic in nodes is respected
102
+ * The process node should only execute when step === 1
103
+ */
104
+ it("should respect conditions in workflow", () => __awaiter(void 0, void 0, void 0, function* () {
105
+ const initialState = {
106
+ context: {
107
+ status: "init",
108
+ step: 2,
109
+ },
110
+ };
111
+ // Initialiser le graph avec l'état initial
112
+ graph = new engine_1.GraphEngine(testDefinition, {
113
+ schema: TestSchema,
114
+ initialState,
115
+ });
116
+ yield graph.execute(initialState, "process");
117
+ const result = graph.getState();
118
+ (0, chai_1.expect)(result.context).to.deep.equal({
119
+ status: "init",
120
+ step: 2,
121
+ });
122
+ }));
123
+ });
124
+ describe("Graph Management", () => {
125
+ it("should add a new node to the graph", () => {
126
+ const newNode = {
127
+ name: "new-node",
128
+ description: "A new test node",
129
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
130
+ return graph.updateState({
131
+ context: Object.assign(Object.assign({}, state.context), { status: "new", step: 4 }),
132
+ });
133
+ }),
134
+ };
135
+ graph.addNode(newNode, {
136
+ relationships: [{ name: "end" }],
137
+ });
138
+ (0, chai_1.expect)(graph.nodes.has("new-node")).to.be.true;
139
+ const addedNode = graph.nodes.get("new-node");
140
+ (0, chai_1.expect)(addedNode === null || addedNode === void 0 ? void 0 : addedNode.relationships).to.have.lengthOf(1);
141
+ });
142
+ it("should update existing graph with new definition", () => {
143
+ const newDefinition = {
144
+ name: "updated-workflow",
145
+ entryNode: "start",
146
+ nodes: Object.assign(Object.assign({}, testDefinition.nodes), { "new-step": {
147
+ name: "new-step",
148
+ description: "New step node",
149
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
150
+ return graph.updateState({
151
+ context: Object.assign(Object.assign({}, state.context), { status: "new-step", step: 4 }),
152
+ });
153
+ }),
154
+ relationships: [],
155
+ } }),
156
+ schema: TestSchema,
157
+ };
158
+ graph.updateGraph(newDefinition);
159
+ (0, chai_1.expect)(graph.nodes.has("new-step")).to.be.true;
160
+ });
161
+ });
162
+ describe("State Management", () => {
163
+ it("should properly update and retrieve state", () => __awaiter(void 0, void 0, void 0, function* () {
164
+ const newState = {
165
+ context: {
166
+ status: "test",
167
+ step: 5,
168
+ },
169
+ };
170
+ graph.setState(newState);
171
+ const retrievedState = graph.getState();
172
+ (0, chai_1.expect)(retrievedState.context).to.deep.equal(newState.context);
173
+ }));
174
+ it("should merge states correctly when updating partially", () => {
175
+ const initialState = {
176
+ context: {
177
+ status: "initial",
178
+ step: 1,
179
+ },
180
+ };
181
+ graph.setState(initialState);
182
+ const partialUpdate = {
183
+ context: {
184
+ status: "updated",
185
+ },
186
+ };
187
+ const updatedState = graph.updateState(partialUpdate);
188
+ (0, chai_1.expect)(updatedState.context).to.deep.equal({
189
+ status: "updated",
190
+ step: 1,
191
+ });
192
+ });
193
+ });
194
+ describe("Error Handling", () => {
195
+ it("should handle execution errors gracefully", () => __awaiter(void 0, void 0, void 0, function* () {
196
+ const errorNode = {
197
+ name: "error-node",
198
+ execute: () => __awaiter(void 0, void 0, void 0, function* () {
199
+ throw new Error("Test error");
200
+ }),
201
+ };
202
+ graph.addNode(errorNode, {});
203
+ let errorCaught = false;
204
+ try {
205
+ yield graph.execute({ context: { status: "test", step: 1 } }, "error-node", undefined, (error) => {
206
+ (0, chai_1.expect)(error.message).to.equal("Test error");
207
+ errorCaught = true;
208
+ });
209
+ }
210
+ catch (error) {
211
+ (0, chai_1.expect)(error).to.be.instanceOf(Error);
212
+ (0, chai_1.expect)(error.message).to.equal("Test error");
213
+ }
214
+ (0, chai_1.expect)(errorCaught).to.be.true;
215
+ }));
216
+ it("should validate state against schema", () => __awaiter(void 0, void 0, void 0, function* () {
217
+ // Créer un nouveau graph avec validation stricte
218
+ const strictGraph = new engine_1.GraphEngine(testDefinition, {
219
+ schema: TestSchema,
220
+ initialState: {
221
+ context: {
222
+ status: "init",
223
+ step: 0,
224
+ },
225
+ },
226
+ });
227
+ const invalidState = {
228
+ context: {
229
+ status: 123, // Should be string
230
+ step: "invalid", // Should be number
231
+ },
232
+ };
233
+ try {
234
+ yield strictGraph.execute(invalidState, "start");
235
+ // Si on arrive ici, le test doit échouer car on s'attend à une erreur
236
+ chai_1.expect.fail("Expected validation error but none was thrown");
237
+ }
238
+ catch (error) {
239
+ (0, chai_1.expect)(error).to.be.instanceOf(Error);
240
+ const errorMessage = error.message;
241
+ (0, chai_1.expect)(errorMessage.includes("Expected string") ||
242
+ errorMessage.includes("Expected number") ||
243
+ errorMessage.includes("validation")).to.be.true;
244
+ }
245
+ }));
246
+ });
247
+ describe("Parallel Execution", () => {
248
+ /**
249
+ * Tests concurrent execution of multiple nodes
250
+ * Important: The execution order is not guaranteed due to async nature
251
+ * @param concurrency - Maximum number of nodes that can execute simultaneously
252
+ */
253
+ it("should execute multiple nodes in parallel", () => __awaiter(void 0, void 0, void 0, function* () {
254
+ const executionOrder = [];
255
+ const parallelNodes = ["node1", "node2", "node3"].map((name) => ({
256
+ name,
257
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
258
+ executionOrder.push(name);
259
+ yield new Promise((resolve) => setTimeout(resolve, 10));
260
+ return state;
261
+ }),
262
+ }));
263
+ parallelNodes.forEach((node) => {
264
+ graph.addNode(node, {});
265
+ });
266
+ yield graph.executeParallel({ context: { status: "test", step: 1 } }, ["node1", "node2", "node3"], 2);
267
+ (0, chai_1.expect)(executionOrder).to.have.lengthOf(3);
268
+ (0, chai_1.expect)(executionOrder).to.include.members(["node1", "node2", "node3"]);
269
+ }));
270
+ });
271
+ describe("Event Handling", () => {
272
+ /**
273
+ * Tests the event emission and handling system
274
+ * Events can trigger node execution asynchronously
275
+ * Note: Uses setTimeout to ensure event processing completes
276
+ */
277
+ it("should emit and handle events correctly", () => __awaiter(void 0, void 0, void 0, function* () {
278
+ const eventNode = {
279
+ name: "event-node",
280
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
281
+ return graph.updateState({
282
+ context: Object.assign(Object.assign({}, state.context), { status: "event-triggered", step: 10 }),
283
+ });
284
+ }),
285
+ events: ["test-event"],
286
+ };
287
+ graph.addNode(eventNode, { events: ["test-event"] });
288
+ // Émettre l'événement
289
+ graph.emit("test-event", {
290
+ state: { context: { status: "init", step: 0 } },
291
+ });
292
+ // Attendre un peu pour que l'événement soit traité
293
+ yield new Promise((resolve) => setTimeout(resolve, 50));
294
+ const state = graph.getState();
295
+ (0, chai_1.expect)(state.context.status).to.equal("event-triggered");
296
+ (0, chai_1.expect)(state.context.status).to.equal("event-triggered");
297
+ }));
298
+ });
299
+ describe("Subgraph Integration", () => {
300
+ /**
301
+ * Tests nested workflow execution through subgraphs
302
+ * Subgraphs allow modular workflow composition
303
+ * The main graph can delegate execution to subgraphs
304
+ */
305
+ it("should execute subgraph as part of main graph", () => __awaiter(void 0, void 0, void 0, function* () {
306
+ const subGraphDef = {
307
+ name: "sub-workflow",
308
+ entryNode: "sub-start",
309
+ nodes: {
310
+ "sub-start": {
311
+ name: "sub-start",
312
+ execute: (_params, state) => __awaiter(void 0, void 0, void 0, function* () {
313
+ return graph.updateState({
314
+ context: Object.assign(Object.assign({}, state.context), { status: "sub-completed", step: 100 }),
315
+ });
316
+ }),
317
+ relationships: [],
318
+ },
319
+ },
320
+ schema: TestSchema,
321
+ };
322
+ const subGraph = new engine_1.GraphEngine(subGraphDef);
323
+ graph.addSubGraph(subGraph, "sub-start", "sub-workflow");
324
+ const initialState = {
325
+ context: {
326
+ status: "init",
327
+ step: 0,
328
+ },
329
+ };
330
+ yield graph.execute(initialState, "sub-workflow");
331
+ const state = graph.getState();
332
+ (0, chai_1.expect)(state.context.status).to.equal("sub-completed");
333
+ (0, chai_1.expect)(state.context.step).to.equal(100);
334
+ }));
335
+ });
336
+ describe("Global Context Management", () => {
337
+ it("should manage global context correctly", () => {
338
+ graph.addToContext("testKey", "testValue");
339
+ (0, chai_1.expect)(graph.getContext("testKey")).to.equal("testValue");
340
+ graph.removeFromContext("testKey");
341
+ (0, chai_1.expect)(graph.getContext("testKey")).to.be.undefined;
342
+ });
343
+ it("should handle multiple context values", () => {
344
+ graph.addToContext("key1", "value1");
345
+ graph.addToContext("key2", { nested: "value2" });
346
+ (0, chai_1.expect)(graph.getContext("key1")).to.equal("value1");
347
+ (0, chai_1.expect)(graph.getContext("key2")).to.deep.equal({ nested: "value2" });
348
+ });
349
+ });
350
+ describe("Graph Visualization", () => {
351
+ it("should generate valid mermaid diagram", () => {
352
+ const diagram = graph.generateMermaidDiagram("Test Workflow");
353
+ (0, chai_1.expect)(diagram).to.include("flowchart TD");
354
+ (0, chai_1.expect)(diagram).to.include("subgraph Test Workflow");
355
+ (0, chai_1.expect)(diagram).to.include("start");
356
+ (0, chai_1.expect)(diagram).to.include("process");
357
+ (0, chai_1.expect)(diagram).to.include("end");
358
+ });
359
+ });
360
+ describe("Schema Visualization", () => {
361
+ /**
362
+ * Tests the schema visualization functionality
363
+ * This helps developers understand the workflow structure
364
+ * The visualization includes:
365
+ * - Node relationships
366
+ * - State schema
367
+ * - Validation rules
368
+ */
369
+ it("should generate schema visualization", () => {
370
+ // Créer un nouveau graph avec un schéma pour le test
371
+ const graphWithSchema = new engine_1.GraphEngine(testDefinition, {
372
+ schema: TestSchema,
373
+ });
374
+ const schemaVisualization = graphWithSchema.visualizeSchema();
375
+ // Vérifier les sections attendues dans la visualisation
376
+ (0, chai_1.expect)(schemaVisualization).to.include("📋 Graph:");
377
+ (0, chai_1.expect)(schemaVisualization).to.include("🔷 Nodes:");
378
+ // Vérifier les détails du schéma
379
+ (0, chai_1.expect)(schemaVisualization).to.satisfy((text) => {
380
+ return text.includes("status:") && text.includes("step:");
381
+ });
382
+ // Vérifier la présence des nœuds
383
+ (0, chai_1.expect)(schemaVisualization).to.include("start");
384
+ (0, chai_1.expect)(schemaVisualization).to.include("process");
385
+ (0, chai_1.expect)(schemaVisualization).to.include("end");
386
+ });
387
+ });
388
+ describe("Persistence Integration", () => {
389
+ it("should work with persistence layer", () => __awaiter(void 0, void 0, void 0, function* () {
390
+ const mockPersistence = {
391
+ saveState: (graphName, state, currentNode) => __awaiter(void 0, void 0, void 0, function* () {
392
+ (0, chai_1.expect)(graphName).to.equal("simple-workflow");
393
+ (0, chai_1.expect)(state.context).to.exist;
394
+ (0, chai_1.expect)(currentNode).to.exist;
395
+ }),
396
+ loadState: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
397
+ };
398
+ graph.setPersistence(mockPersistence);
399
+ yield graph.execute({ context: { status: "init", step: 0 } }, "start");
400
+ }));
401
+ });
402
+ describe("Real-time Notifications", () => {
403
+ /**
404
+ * Tests the notification system during workflow execution
405
+ * Notifications are sent for:
406
+ * - Node execution start
407
+ * - Node execution completion
408
+ * - State updates
409
+ * - Error events
410
+ */
411
+ it("should send notifications during execution", () => __awaiter(void 0, void 0, void 0, function* () {
412
+ const notifications = [];
413
+ const mockNotifier = {
414
+ notify: (event, data) => {
415
+ notifications.push({ event, data });
416
+ },
417
+ };
418
+ graph.setNotifier(mockNotifier);
419
+ yield graph.execute({ context: { status: "init", step: 0 } }, "start");
420
+ (0, chai_1.expect)(notifications).to.have.length.greaterThan(0);
421
+ (0, chai_1.expect)(notifications[0].event).to.equal("nodeExecutionStarted");
422
+ (0, chai_1.expect)(notifications).to.deep.include.members([
423
+ {
424
+ event: "nodeExecutionCompleted",
425
+ data: {
426
+ workflow: "simple-workflow",
427
+ node: "start",
428
+ state: {
429
+ context: {
430
+ status: "started",
431
+ step: 1,
432
+ },
433
+ },
434
+ },
435
+ },
436
+ ]);
437
+ }));
438
+ });
439
+ describe("Cycle Detection", () => {
440
+ /**
441
+ * Tests the cycle detection mechanism
442
+ * Cycles in workflow definitions can cause infinite loops
443
+ * The graph constructor should detect and prevent cyclic dependencies
444
+ */
445
+ it("should detect cycles in graph", () => {
446
+ const cyclicDefinition = {
447
+ name: "cyclic-workflow",
448
+ entryNode: "node1",
449
+ nodes: {
450
+ node1: {
451
+ name: "node1",
452
+ execute: (state) => __awaiter(void 0, void 0, void 0, function* () { return state; }),
453
+ relationships: [{ name: "node2" }],
454
+ },
455
+ node2: {
456
+ name: "node2",
457
+ execute: (state) => __awaiter(void 0, void 0, void 0, function* () { return state; }),
458
+ relationships: [{ name: "node1" }],
459
+ },
460
+ },
461
+ };
462
+ (0, chai_1.expect)(() => new engine_1.GraphEngine(cyclicDefinition, { autoDetectCycles: true })).to.throw;
463
+ });
464
+ });
465
+ });