@ai.ntellect/core 0.6.21 → 0.7.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 (37) hide show
  1. package/.mocharc.json +2 -1
  2. package/README.md +61 -85
  3. package/graph/controller.ts +1 -1
  4. package/graph/event-manager.ts +288 -0
  5. package/graph/index.ts +153 -367
  6. package/graph/logger.ts +70 -0
  7. package/graph/node.ts +398 -0
  8. package/graph/observer.ts +361 -0
  9. package/interfaces/index.ts +102 -1
  10. package/modules/agenda/index.ts +3 -16
  11. package/package.json +10 -5
  12. package/test/graph/index.test.ts +244 -113
  13. package/test/graph/observer.test.ts +398 -0
  14. package/test/modules/agenda/node-cron.test.ts +37 -16
  15. package/test/modules/memory/adapters/in-memory.test.ts +2 -2
  16. package/test/modules/memory/adapters/meilisearch.test.ts +28 -24
  17. package/test/modules/memory/base.test.ts +3 -3
  18. package/tsconfig.json +4 -2
  19. package/types/index.ts +23 -2
  20. package/dist/graph/controller.js +0 -72
  21. package/dist/graph/index.js +0 -501
  22. package/dist/index.js +0 -41
  23. package/dist/interfaces/index.js +0 -17
  24. package/dist/modules/agenda/adapters/node-cron/index.js +0 -29
  25. package/dist/modules/agenda/index.js +0 -140
  26. package/dist/modules/embedding/adapters/ai/index.js +0 -57
  27. package/dist/modules/embedding/index.js +0 -59
  28. package/dist/modules/memory/adapters/in-memory/index.js +0 -210
  29. package/dist/modules/memory/adapters/meilisearch/index.js +0 -320
  30. package/dist/modules/memory/adapters/redis/index.js +0 -158
  31. package/dist/modules/memory/index.js +0 -103
  32. package/dist/types/index.js +0 -2
  33. package/dist/utils/generate-action-schema.js +0 -43
  34. package/dist/utils/header-builder.js +0 -34
  35. package/test/modules/embedding/ai.test.ts +0 -78
  36. package/test/modules/memory/adapters/redis.test.ts +0 -169
  37. package/test/services/agenda.test.ts +0 -279
@@ -0,0 +1,398 @@
1
+ import { expect } from "chai";
2
+ import { BehaviorSubject, Subject } from "rxjs";
3
+ import { z } from "zod";
4
+ import { GraphFlow } from "../../graph";
5
+ import { GraphObserver } from "../../graph/observer";
6
+ import { GraphContext, GraphEvent, Node } from "../../types";
7
+
8
+ /**
9
+ * Test schema definition for observer tests
10
+ * Defines a schema with:
11
+ * - value: numeric value for tracking state changes
12
+ * - status: enum representing node execution status
13
+ */
14
+ const TestSchema = z.object({
15
+ value: z.number().default(0),
16
+ status: z.enum(["pending", "completed", "failed"]).optional(),
17
+ });
18
+
19
+ /**
20
+ * Test suite for the GraphObserver implementation
21
+ * This suite validates the reactive observation capabilities:
22
+ * - State observation (global and node-specific)
23
+ * - Property observation (single and multiple)
24
+ * - Event observation and correlation
25
+ * - Workflow monitoring
26
+ * - Node execution triggers
27
+ */
28
+ describe("GraphObserver", () => {
29
+ let graph: GraphFlow<typeof TestSchema>;
30
+ let eventSubject: Subject<GraphEvent<typeof TestSchema>>;
31
+ let stateSubject: BehaviorSubject<GraphContext<typeof TestSchema>>;
32
+ let destroySubject: Subject<void>;
33
+ let observer: GraphObserver<typeof TestSchema>;
34
+
35
+ beforeEach(() => {
36
+ graph = new GraphFlow("TestGraph", {
37
+ name: "TestGraph",
38
+ nodes: [],
39
+ context: { value: 0 },
40
+ schema: TestSchema,
41
+ });
42
+
43
+ // Initialize subjects
44
+ eventSubject = new Subject();
45
+ stateSubject = new BehaviorSubject(graph.getContext());
46
+ destroySubject = new Subject();
47
+
48
+ // Create observer instance
49
+ observer = new GraphObserver(
50
+ graph,
51
+ eventSubject,
52
+ stateSubject,
53
+ destroySubject
54
+ );
55
+ });
56
+
57
+ /**
58
+ * Test suite for state observation functionality
59
+ * Validates the ability to track and react to state changes
60
+ * in the graph context
61
+ */
62
+ describe("State Observation", () => {
63
+ /**
64
+ * Tests sequential state change observation
65
+ * Validates that:
66
+ * - All state changes are captured in order
67
+ * - Multiple state changes in a single node are tracked
68
+ * - Initial state is included in observations
69
+ */
70
+ it("should observe state changes", async () => {
71
+ const states: any[] = [];
72
+ const testNode: Node<typeof TestSchema> = {
73
+ name: "testNode",
74
+ execute: async (context) => {
75
+ context.value = 1;
76
+ context.value = 2;
77
+ context.value = 3;
78
+ },
79
+ };
80
+
81
+ graph.addNode(testNode);
82
+ const subscription = graph
83
+ .observe()
84
+ .state()
85
+ .subscribe((state) => {
86
+ if (state.value !== undefined) {
87
+ states.push(state.value);
88
+ }
89
+ });
90
+
91
+ await graph.execute("testNode");
92
+ await new Promise((resolve) => setTimeout(resolve, 50));
93
+ subscription.unsubscribe();
94
+
95
+ expect(states).to.deep.equal([0, 1, 2, 3]);
96
+ });
97
+
98
+ /**
99
+ * Tests node-specific state observation
100
+ * Validates that:
101
+ * - State changes for specific nodes are tracked
102
+ * - Status transitions are captured correctly
103
+ * - Async state changes are properly observed
104
+ */
105
+ it("should observe specific node state changes", async () => {
106
+ const states: any[] = [];
107
+ const testNode: Node<typeof TestSchema> = {
108
+ name: "testNode",
109
+ execute: async (context) => {
110
+ context.status = "pending";
111
+ await new Promise((resolve) => setTimeout(resolve, 10));
112
+ context.status = "completed";
113
+ },
114
+ };
115
+
116
+ graph.addNode(testNode);
117
+ const subscription = graph
118
+ .observe()
119
+ .node("testNode")
120
+ .subscribe((state) => state.status && states.push(state.status));
121
+
122
+ await graph.execute("testNode");
123
+ await new Promise((resolve) => setTimeout(resolve, 50));
124
+ subscription.unsubscribe();
125
+
126
+ expect(states).to.deep.equal(["pending", "completed"]);
127
+ });
128
+
129
+ /**
130
+ * Tests multi-node state observation
131
+ * Validates that:
132
+ * - Multiple nodes can be observed simultaneously
133
+ * - State changes are correctly attributed to nodes
134
+ * - Sequential execution is properly tracked
135
+ */
136
+ it("should observe multiple nodes", async () => {
137
+ const states: any[] = [];
138
+ const node1: Node<typeof TestSchema> = {
139
+ name: "node1",
140
+ execute: async (context) => {
141
+ context.value = 1;
142
+ },
143
+ };
144
+ const node2: Node<typeof TestSchema> = {
145
+ name: "node2",
146
+ execute: async (context) => {
147
+ context.value = 2;
148
+ },
149
+ };
150
+
151
+ graph.addNode(node1);
152
+ graph.addNode(node2);
153
+
154
+ const subscription = graph
155
+ .observe()
156
+ .nodes(["node1", "node2"])
157
+ .subscribe((state) => state.value && states.push(state.value));
158
+
159
+ await graph.execute("node1");
160
+ await new Promise((resolve) => setTimeout(resolve, 50));
161
+ await graph.execute("node2");
162
+ await new Promise((resolve) => setTimeout(resolve, 50));
163
+ subscription.unsubscribe();
164
+
165
+ expect(states).to.deep.equal([1, 2]);
166
+ });
167
+ });
168
+
169
+ /**
170
+ * Test suite for property observation functionality
171
+ * Validates the ability to track specific properties
172
+ * of the graph context
173
+ */
174
+ describe("Property Observation", () => {
175
+ /**
176
+ * Tests single property observation
177
+ * Validates that:
178
+ * - Individual property changes are tracked
179
+ * - Initial property value is captured
180
+ * - Property updates trigger observations
181
+ */
182
+ it("should observe single property", async () => {
183
+ const values: any[] = [];
184
+ const testNode: Node<typeof TestSchema> = {
185
+ name: "testNode",
186
+ execute: async (context) => {
187
+ context.value = 42;
188
+ },
189
+ };
190
+
191
+ graph.addNode(testNode);
192
+ const subscription = graph
193
+ .observe()
194
+ .property("value")
195
+ .subscribe((state) => values.push(state.value));
196
+
197
+ await graph.execute("testNode");
198
+ subscription.unsubscribe();
199
+
200
+ expect(values).to.deep.equal([0, 42]);
201
+ });
202
+
203
+ /**
204
+ * Tests multiple property observation
205
+ * Validates that:
206
+ * - Multiple properties can be tracked simultaneously
207
+ * - Changes to any observed property trigger updates
208
+ * - Final state contains all tracked properties
209
+ */
210
+ it("should observe multiple properties", async () => {
211
+ const values: any[] = [];
212
+ const testNode: Node<typeof TestSchema> = {
213
+ name: "testNode",
214
+ execute: async (context) => {
215
+ context.value = 42;
216
+ context.status = "completed";
217
+ },
218
+ };
219
+
220
+ graph.addNode(testNode);
221
+ const subscription = graph
222
+ .observe()
223
+ .property(["value", "status"])
224
+ .subscribe((change) => {
225
+ const { name, ...rest } = change;
226
+ values.push(rest);
227
+ });
228
+
229
+ await graph.execute("testNode");
230
+ await new Promise((resolve) => setTimeout(resolve, 50));
231
+ subscription.unsubscribe();
232
+
233
+ expect(values[values.length - 1]).to.deep.equal({
234
+ value: 42,
235
+ status: "completed",
236
+ });
237
+ });
238
+
239
+ /**
240
+ * Tests condition-based observation
241
+ * Validates that:
242
+ * - Multiple conditions can be combined
243
+ * - Observation completes when conditions are met
244
+ * - State reflects all required changes
245
+ */
246
+ it("should wait for multiple conditions", async () => {
247
+ const testNode: Node<typeof TestSchema> = {
248
+ name: "testNode",
249
+ execute: async (context) => {
250
+ context.value = 42;
251
+ context.status = "completed";
252
+ },
253
+ };
254
+
255
+ graph.addNode(testNode);
256
+ const promise = graph
257
+ .observe()
258
+ .until(
259
+ graph.observe().property(["value", "status"]),
260
+ (state) => state.value === 42 && state.status === "completed"
261
+ );
262
+
263
+ await graph.execute("testNode");
264
+ await promise;
265
+
266
+ const context = graph.getContext();
267
+ expect(context.value).to.equal(42);
268
+ expect(context.status).to.equal("completed");
269
+ });
270
+ });
271
+
272
+ /**
273
+ * Test suite for event observation functionality
274
+ * Validates the handling of events and event correlation
275
+ */
276
+ describe("Event Observation", () => {
277
+ /**
278
+ * Tests specific event observation
279
+ */
280
+ it("should observe specific events", async () => {
281
+ const events: GraphEvent<typeof TestSchema>[] = [];
282
+ const subscription = graph
283
+ .observe()
284
+ .event("testEvent")
285
+ .subscribe((e) => events.push(e));
286
+
287
+ await graph.emit("testEvent", { value: 1 });
288
+ await new Promise((resolve) => setTimeout(resolve, 100));
289
+ subscription.unsubscribe();
290
+
291
+ expect(events.length).to.equal(1);
292
+ expect(events[0].type).to.equal("testEvent");
293
+ });
294
+
295
+ /**
296
+ * Tests debounced event handling
297
+ */
298
+ it("should handle debounced events", async () => {
299
+ const events: GraphEvent<typeof TestSchema>[] = [];
300
+ const subscription = graph
301
+ .observe()
302
+ .event("event1")
303
+ .subscribe((e) => events.push(e));
304
+
305
+ await graph.emit("event1", { value: 1 });
306
+ await graph.emit("event1", { value: 2 });
307
+ await new Promise((resolve) => setTimeout(resolve, 100));
308
+ subscription.unsubscribe();
309
+
310
+ expect(events.length).to.equal(2);
311
+ });
312
+
313
+ /**
314
+ * Tests event correlation functionality
315
+ * Validates that:
316
+ * - Multiple events can be correlated
317
+ * - Correlation conditions are properly evaluated
318
+ * - Timeout handling works correctly
319
+ * - Events are captured in correct order
320
+ */
321
+ it("should wait for correlated events", async () => {
322
+ // Create test events
323
+ const eventA = {
324
+ type: "eventA",
325
+ payload: { eventPayload: { status: "success" } },
326
+ timestamp: Date.now(),
327
+ } as GraphEvent<typeof TestSchema>;
328
+
329
+ const eventB = {
330
+ type: "eventB",
331
+ payload: { eventPayload: { status: "success" } },
332
+ timestamp: Date.now(),
333
+ } as GraphEvent<typeof TestSchema>;
334
+
335
+ // Emit events after a short delay
336
+ setTimeout(() => {
337
+ eventSubject.next(eventA);
338
+ eventSubject.next(eventB);
339
+ }, 100);
340
+
341
+ const events = await observer.waitForCorrelatedEvents(
342
+ ["eventA", "eventB"],
343
+ 2000,
344
+ (events) =>
345
+ events.every((e) => e.payload.eventPayload?.status === "success")
346
+ );
347
+
348
+ expect(events.length).to.equal(2);
349
+ expect(events[0].type).to.equal("eventA");
350
+ expect(events[1].type).to.equal("eventB");
351
+ });
352
+
353
+ afterEach(() => {
354
+ destroySubject.next();
355
+ destroySubject.complete();
356
+ eventSubject.complete();
357
+ stateSubject.complete();
358
+ });
359
+ });
360
+
361
+ /**
362
+ * Test suite for workflow observation functionality
363
+ * Validates the ability to monitor complete workflow execution
364
+ */
365
+ describe("Workflow Observation", () => {
366
+ /**
367
+ * Tests complete workflow observation
368
+ * Validates that:
369
+ * - Entire workflow execution is tracked
370
+ * - State transitions are captured
371
+ * - Final state reflects completed workflow
372
+ */
373
+ it("should observe complete workflow", async () => {
374
+ const states: any[] = [];
375
+ const node: Node<typeof TestSchema> = {
376
+ name: "testNode",
377
+ execute: async (context) => {
378
+ context.status = "pending";
379
+ await new Promise((resolve) => setTimeout(resolve, 10));
380
+ context.status = "completed";
381
+ },
382
+ };
383
+
384
+ graph.addNode(node);
385
+ const subscription = graph
386
+ .observe()
387
+ .node("testNode")
388
+ .subscribe((state) => states.push(state));
389
+
390
+ await graph.execute("testNode");
391
+ await new Promise((resolve) => setTimeout(resolve, 50));
392
+ subscription.unsubscribe();
393
+
394
+ expect(states.length).to.be.greaterThan(0);
395
+ expect(states[states.length - 1].status).to.equal("completed");
396
+ });
397
+ });
398
+ });
@@ -1,7 +1,8 @@
1
1
  import { expect } from "chai";
2
2
  import sinon from "sinon";
3
3
  import { Agenda } from "../../../modules/agenda";
4
- import { NodeCronAdapter } from "../../../modules/agenda/adapters/cron/node-cron";
4
+ import { NodeCronAdapter } from "../../../modules/agenda/adapters/node-cron";
5
+ import { Memory } from "../../../modules/memory";
5
6
  import { InMemoryAdapter } from "../../../modules/memory/adapters/in-memory";
6
7
 
7
8
  before(function () {
@@ -14,8 +15,9 @@ describe("Agenda Service", () => {
14
15
 
15
16
  beforeEach(() => {
16
17
  const cronService = new NodeCronAdapter();
17
- const jobStorage = new InMemoryAdapter();
18
- agenda = new Agenda(cronService, jobStorage);
18
+ const inMemory = new InMemoryAdapter();
19
+ const memory = new Memory(inMemory);
20
+ agenda = new Agenda(cronService, memory);
19
21
  });
20
22
 
21
23
  afterEach(async () => {
@@ -42,9 +44,9 @@ describe("Agenda Service", () => {
42
44
  scheduledIds.push(id); // Track the ID
43
45
 
44
46
  expect(id).to.be.a("string");
45
- expect(agenda.getScheduledRequests()).to.have.lengthOf(1);
47
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
46
48
 
47
- const scheduledRequest = agenda.getScheduledRequests()[0];
49
+ const scheduledRequest = (await agenda.getScheduledRequests())[0];
48
50
  expect(scheduledRequest.originalRequest).to.equal(
49
51
  request.originalRequest
50
52
  );
@@ -103,13 +105,11 @@ describe("Agenda Service", () => {
103
105
 
104
106
  const id = await agenda.scheduleRequest(request);
105
107
  scheduledIds.push(id);
106
- const requests = await agenda.getScheduledRequests();
107
- expect(requests).to.have.lengthOf(1);
108
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(1);
108
109
 
109
110
  const cancelled = await agenda.cancelScheduledRequest(id);
110
111
  expect(cancelled).to.be.true;
111
- const remainingRequests = await agenda.getScheduledRequests();
112
- expect(remainingRequests).to.have.lengthOf(0);
112
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
113
113
  });
114
114
 
115
115
  it("should return false when cancelling non-existent request", async () => {
@@ -139,6 +139,28 @@ describe("Agenda Service", () => {
139
139
  expect(scheduledRequests[0].originalRequest).to.equal("request 1");
140
140
  expect(scheduledRequests[1].originalRequest).to.equal("request 2");
141
141
  });
142
+
143
+ it("should stop all scheduled requests", async () => {
144
+ const requests = [
145
+ {
146
+ originalRequest: "request 1",
147
+ cronExpression: "*/1 * * * *",
148
+ },
149
+ {
150
+ originalRequest: "request 2",
151
+ cronExpression: "*/5 * * * *",
152
+ },
153
+ ];
154
+
155
+ for (const request of requests) {
156
+ await agenda.scheduleRequest(request);
157
+ }
158
+
159
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
160
+
161
+ await agenda.stopAll();
162
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
163
+ });
142
164
  });
143
165
 
144
166
  describe("Global Management", () => {
@@ -158,12 +180,10 @@ describe("Agenda Service", () => {
158
180
  await agenda.scheduleRequest(request);
159
181
  }
160
182
 
161
- const initialRequests = await agenda.getScheduledRequests();
162
- expect(initialRequests).to.have.lengthOf(2);
183
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(2);
163
184
 
164
185
  await agenda.stopAll();
165
- const remainingRequests = await agenda.getScheduledRequests();
166
- expect(remainingRequests).to.have.lengthOf(0);
186
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
167
187
  });
168
188
  });
169
189
 
@@ -223,7 +243,7 @@ describe("Agenda Service", () => {
223
243
  }
224
244
 
225
245
  expect(onExecutedSpy.calledOnce).to.be.true;
226
- expect(agenda.getScheduledRequests()).to.have.lengthOf(0);
246
+ expect(await agenda.getScheduledRequests()).to.have.lengthOf(0);
227
247
  });
228
248
 
229
249
  it("should log execution status", async function () {
@@ -270,8 +290,9 @@ describe("Agenda Service", () => {
270
290
  let globalAgenda: Agenda;
271
291
  before(() => {
272
292
  const cronService = new NodeCronAdapter();
273
- const jobStorage = new InMemoryAdapter();
274
- globalAgenda = new Agenda(cronService, jobStorage);
293
+ const inMemoryAdapter = new InMemoryAdapter();
294
+ const memory = new Memory(inMemoryAdapter);
295
+ globalAgenda = new Agenda(cronService, memory);
275
296
  });
276
297
 
277
298
  after(async () => {
@@ -1,6 +1,6 @@
1
1
  import { expect } from "chai";
2
- import { InMemoryAdapter } from "../../../memory/adapters/in-memory";
3
- import { BaseMemoryType } from "../../../types";
2
+ import { InMemoryAdapter } from "../../../../modules/memory/adapters/in-memory";
3
+ import { BaseMemoryType } from "../../../../types";
4
4
 
5
5
  describe("InMemoryAdapter", () => {
6
6
  let inMemoryAdapter: InMemoryAdapter;
@@ -1,8 +1,7 @@
1
1
  import { expect } from "chai";
2
2
  import dotenv from "dotenv";
3
- import { MeilisearchAdapter } from "../../../memory/adapters/meilisearch";
4
- import { BaseMemoryType } from "../../../types";
5
-
3
+ import { MeilisearchAdapter } from "../../../../modules/memory/adapters/meilisearch";
4
+ import { BaseMemoryType } from "../../../../types";
6
5
  // Load environment variables
7
6
  dotenv.config();
8
7
 
@@ -126,7 +125,6 @@ describe("MeilisearchAdapter", () => {
126
125
 
127
126
  try {
128
127
  await meilisearchAdapter.init(TEST_ROOM_ID);
129
- await meilisearchAdapter.initializeStorage(TEST_ROOM_ID);
130
128
  } catch (error) {
131
129
  console.error("Failed to initialize:", error);
132
130
  throw error;
@@ -134,14 +132,14 @@ describe("MeilisearchAdapter", () => {
134
132
  });
135
133
 
136
134
  it("should create memory", async () => {
137
- const result = await meilisearchAdapter.createMemory({
135
+ const input = {
136
+ id: "test-id",
137
+ roomId: "test-room",
138
138
  data: "test data",
139
- roomId: TEST_ROOM_ID,
140
- });
139
+ };
141
140
 
142
- expect(result).to.exist;
143
- expect(result?.data).to.equal("test data");
144
- expect(result?.embedding).to.be.null;
141
+ const result = await meilisearchAdapter.createMemory(input);
142
+ expect(result?.embedding).to.be.undefined;
145
143
  });
146
144
 
147
145
  it("should search memories", async () => {
@@ -161,27 +159,34 @@ describe("MeilisearchAdapter", () => {
161
159
  });
162
160
 
163
161
  it("should handle memory retrieval by ID", async () => {
162
+ const testDate = new Date("2025-02-11T08:24:38.251Z");
163
+
164
+ const mockData = {
165
+ id: "test-id",
166
+ roomId: "test-room",
167
+ data: "test data",
168
+ createdAt: testDate.toISOString(),
169
+ embedding: undefined,
170
+ };
171
+
164
172
  global.fetch = async (input: RequestInfo | URL) => {
165
173
  const url = input.toString();
166
- if (url.includes(`/indexes/${TEST_ROOM_ID}/documents/test-id`)) {
167
- return new Response(
168
- JSON.stringify({
169
- ...testMemory,
170
- createdAt: testMemory.createdAt.toISOString(),
171
- })
172
- );
174
+ if (url.includes(`/indexes/test-room/documents/test-id`)) {
175
+ return new Response(JSON.stringify(mockData));
173
176
  }
174
177
  return new Response(JSON.stringify({}));
175
178
  };
176
179
 
177
180
  const result = await meilisearchAdapter.getMemoryById(
178
181
  "test-id",
179
- TEST_ROOM_ID
182
+ "test-room"
180
183
  );
181
- if (result) {
182
- result.createdAt = new Date(result.createdAt);
183
- }
184
- expect(result).to.deep.equal(testMemory);
184
+
185
+ expect(result?.id).to.equal(mockData.id);
186
+ expect(result?.roomId).to.equal(mockData.roomId);
187
+ expect(result?.data).to.equal(mockData.data);
188
+ expect(result?.createdAt).to.equal(mockData.createdAt); // Compare directement les strings
189
+ expect(result?.embedding).to.be.undefined;
185
190
  });
186
191
 
187
192
  it("should handle non-existent memory", async () => {
@@ -276,8 +281,7 @@ describe("MeilisearchAdapter", () => {
276
281
  return new Response(JSON.stringify({}));
277
282
  };
278
283
 
279
- await expect(meilisearchAdapter.initializeStorage(TEST_ROOM_ID)).to.not
280
- .throw;
284
+ await expect(meilisearchAdapter.init(TEST_ROOM_ID)).to.not.throw;
281
285
  });
282
286
  });
283
287
  });
@@ -201,14 +201,14 @@ describe("Memory", () => {
201
201
  describe("Edge Cases", () => {
202
202
  it("should handle undefined embedding", async () => {
203
203
  const input = {
204
- data: "test data",
205
- query: "test query",
204
+ id: "test-id",
206
205
  roomId: "test-room",
206
+ data: "test data",
207
207
  embedding: undefined,
208
208
  };
209
209
 
210
210
  const result = await memory.createMemory(input);
211
- expect(result?.embedding).to.be.null;
211
+ expect(result?.embedding).to.be.undefined;
212
212
  });
213
213
 
214
214
  it("should handle empty query results", async () => {
package/tsconfig.json CHANGED
@@ -7,7 +7,9 @@
7
7
  "strict": true,
8
8
  "skipLibCheck": true,
9
9
  "baseUrl": ".",
10
- "outDir": "./dist"
10
+ "outDir": "./dist",
11
+ "types": ["node", "mocha"]
11
12
  },
12
- "exclude": ["node_modules", "examples", "test", "app"]
13
+ "include": ["src/**/*", "graph/**/*", "types/**/*", "interfaces/**/*"],
14
+ "exclude": ["node_modules", "dist", "test", "examples"]
13
15
  }
package/types/index.ts CHANGED
@@ -94,10 +94,18 @@ export interface Node<T extends ZodSchema, I = any> {
94
94
  condition?: (context: GraphContext<T>) => boolean;
95
95
  /** Array of next node names */
96
96
  next?: string[] | ((context: GraphContext<T>) => string[]);
97
- /** Array of event names */
97
+ /** Array of event names that trigger this node */
98
98
  events?: string[];
99
- /** Wait for event */
99
+ /** Wait for a single event before continuing */
100
100
  waitForEvent?: boolean;
101
+ /** Wait for multiple events configuration */
102
+ waitForEvents?: WaitForEvents;
103
+ /** Event correlation configuration */
104
+ correlateEvents?: {
105
+ events: string[];
106
+ correlation: (events: GraphEvent<T>[]) => boolean;
107
+ timeout?: number;
108
+ };
101
109
  /** Retry configuration */
102
110
  retry?: {
103
111
  /** Maximum number of retry attempts */
@@ -164,3 +172,16 @@ export type MeilisearchSettings = {
164
172
  /** Array of sortable attributes */
165
173
  sortableAttributes?: string[];
166
174
  };
175
+
176
+ export interface GraphEvent<T extends ZodSchema> {
177
+ type: string;
178
+ payload?: any;
179
+ timestamp: number;
180
+ }
181
+
182
+ export interface WaitForEvents {
183
+ events: string[];
184
+ timeout?: number;
185
+ strategy: "all" | "any" | "race";
186
+ onSuccess?: <T extends ZodSchema>(context: GraphContext<T>) => Promise<void>;
187
+ }