@cogitator-ai/memory 0.10.0 → 0.11.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.
Files changed (65) hide show
  1. package/README.md +212 -89
  2. package/dist/adapters/index.d.ts +10 -2
  3. package/dist/adapters/index.d.ts.map +1 -1
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/adapters/mongodb.d.ts +6 -0
  6. package/dist/adapters/mongodb.d.ts.map +1 -1
  7. package/dist/adapters/mongodb.js +60 -37
  8. package/dist/adapters/mongodb.js.map +1 -1
  9. package/dist/adapters/pgvector.d.ts +46 -0
  10. package/dist/adapters/pgvector.d.ts.map +1 -0
  11. package/dist/adapters/pgvector.js +55 -0
  12. package/dist/adapters/pgvector.js.map +1 -0
  13. package/dist/adapters/postgres.d.ts +4 -0
  14. package/dist/adapters/postgres.d.ts.map +1 -1
  15. package/dist/adapters/postgres.js +149 -68
  16. package/dist/adapters/postgres.js.map +1 -1
  17. package/dist/adapters/qdrant.d.ts +1 -0
  18. package/dist/adapters/qdrant.d.ts.map +1 -1
  19. package/dist/adapters/qdrant.js +49 -42
  20. package/dist/adapters/qdrant.js.map +1 -1
  21. package/dist/adapters/sqlite-driver.d.ts +11 -0
  22. package/dist/adapters/sqlite-driver.d.ts.map +1 -0
  23. package/dist/adapters/sqlite-driver.js +2 -0
  24. package/dist/adapters/sqlite-driver.js.map +1 -0
  25. package/dist/adapters/sqlite.d.ts +1 -0
  26. package/dist/adapters/sqlite.d.ts.map +1 -1
  27. package/dist/adapters/sqlite.js +18 -18
  28. package/dist/adapters/sqlite.js.map +1 -1
  29. package/dist/compaction.d.ts +14 -1
  30. package/dist/compaction.d.ts.map +1 -1
  31. package/dist/compaction.js +4 -1
  32. package/dist/compaction.js.map +1 -1
  33. package/dist/context-builder.d.ts +5 -0
  34. package/dist/context-builder.d.ts.map +1 -1
  35. package/dist/context-builder.js +10 -11
  36. package/dist/context-builder.js.map +1 -1
  37. package/dist/core-facts.d.ts +5 -0
  38. package/dist/core-facts.d.ts.map +1 -1
  39. package/dist/core-facts.js +1 -2
  40. package/dist/core-facts.js.map +1 -1
  41. package/dist/embedding/openai.d.ts +5 -0
  42. package/dist/embedding/openai.d.ts.map +1 -1
  43. package/dist/embedding/openai.js +24 -4
  44. package/dist/embedding/openai.js.map +1 -1
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/knowledge-graph/entity-extractor.d.ts +24 -11
  50. package/dist/knowledge-graph/entity-extractor.d.ts.map +1 -1
  51. package/dist/knowledge-graph/entity-extractor.js +15 -3
  52. package/dist/knowledge-graph/entity-extractor.js.map +1 -1
  53. package/dist/knowledge-graph/graph-adapter.d.ts +1 -0
  54. package/dist/knowledge-graph/graph-adapter.d.ts.map +1 -1
  55. package/dist/knowledge-graph/graph-adapter.js +477 -453
  56. package/dist/knowledge-graph/graph-adapter.js.map +1 -1
  57. package/dist/knowledge-graph/sqlite-graph-adapter.d.ts +1 -0
  58. package/dist/knowledge-graph/sqlite-graph-adapter.d.ts.map +1 -1
  59. package/dist/knowledge-graph/sqlite-graph-adapter.js +461 -439
  60. package/dist/knowledge-graph/sqlite-graph-adapter.js.map +1 -1
  61. package/dist/schema.d.ts +4 -0
  62. package/dist/schema.d.ts.map +1 -1
  63. package/dist/schema.js +2 -0
  64. package/dist/schema.js.map +1 -1
  65. package/package.json +3 -3
package/README.md CHANGED
@@ -15,6 +15,8 @@ pnpm add mongodb # For MongoDB adapter
15
15
  pnpm add @qdrant/js-client-rest # For Qdrant embedding adapter
16
16
  ```
17
17
 
18
+ With `@cogitator-ai/core` you usually configure memory on the runtime (`new Cogitator({ memory: { adapter: 'postgres', postgres: { ... } } })`) and read it with `await cog.getMemory()`; the adapters below are for direct use. Website docs: [Memory](https://cogitator.app/docs/memory), [Adapters](https://cogitator.app/docs/memory/adapters), [Embeddings](https://cogitator.app/docs/memory/embeddings), [Hybrid Search](https://cogitator.app/docs/memory/hybrid-search), [Knowledge Graphs](https://cogitator.app/docs/memory/knowledge-graphs).
19
+
18
20
  ## Features
19
21
 
20
22
  - **Multiple Adapters** - In-memory, Redis (standalone + cluster), PostgreSQL with pgvector, SQLite, MongoDB, Qdrant
@@ -24,10 +26,10 @@ pnpm add @qdrant/js-client-rest # For Qdrant embedding adapter
24
26
  - **Context Builder** - Token-aware context with `recent`, `relevant` (embedding-ranked) and `hybrid` strategies
25
27
  - **Scoped Semantic Memory** - Embeddings tagged with `metadata.agentId` / `metadata.threadId` never leak to other agents
26
28
  - **Sessions & Compaction** - `SessionManager` for channel sessions, `CompactionService` for summarizing long histories
27
- - **Embedding Services** - OpenAI and Ollama embedding integration
29
+ - **Embedding Services** - OpenAI, Ollama and Google embedding integration
28
30
  - **Semantic Search** - Vector similarity search with pgvector
29
31
  - **Hybrid Search** - BM25 + Vector with Reciprocal Rank Fusion
30
- - **Facts Storage** - Store and retrieve agent knowledge
32
+ - **Facts Storage** - Store and retrieve agent knowledge (`FactAdapter` on PostgreSQL, `CoreFactsStore` on SQLite)
31
33
  - **Knowledge Graph** - Entity-relationship memory with multi-hop traversal
32
34
  - **Zod Schemas** - Type-safe configuration validation
33
35
 
@@ -46,11 +48,13 @@ const thread = unwrap(await memory.createThread('agent-1', { topic: 'greeting' }
46
48
  await memory.addEntry({
47
49
  threadId: thread.id,
48
50
  message: { role: 'user', content: 'Hello!' },
51
+ tokenCount: 2,
49
52
  });
50
53
 
51
54
  await memory.addEntry({
52
55
  threadId: thread.id,
53
56
  message: { role: 'assistant', content: 'Hi there!' },
57
+ tokenCount: 3,
54
58
  });
55
59
 
56
60
  const builder = new ContextBuilder(
@@ -67,7 +71,16 @@ const context = await builder.build({
67
71
  console.log(context.messages);
68
72
  ```
69
73
 
70
- Adapter calls return a `MemoryResult` — `{ success: true, data }` or `{ success: false, error }` — instead of throwing. `unwrap(result)` returns the data or throws an `Error` with the adapter's message; check `result.success` yourself where a failure is expected.
74
+ Adapter calls return a `MemoryResult` — `{ success: true, data }` or `{ success: false, error }` — instead of throwing. `unwrap(result)` returns the data or throws an `Error` with the adapter's message; check `result.success` yourself where a failure is expected:
75
+
76
+ ```typescript
77
+ const result = await memory.getThread('thread_123');
78
+ if (!result.success) {
79
+ console.error('Memory unavailable:', result.error);
80
+ } else if (result.data) {
81
+ console.log(result.data.metadata);
82
+ }
83
+ ```
71
84
 
72
85
  ---
73
86
 
@@ -124,6 +137,8 @@ const memory = new PostgresAdapter({
124
137
  await memory.connect();
125
138
  ```
126
139
 
140
+ On `connect()` the adapter creates its tables and an HNSW cosine index on the embeddings, which works from the first row on (no training data needed). An `ivfflat` index left by an earlier version is replaced on the first connect after upgrading, see the [adapters docs](https://cogitator.app/docs/memory/adapters) for building the new index ahead of time on a large table. `search()` raises `hnsw.ef_search` to the requested limit and, on pgvector 0.8+, uses iterative scans, so large limits and filters still get every matching row. Messages, tool calls, tool results and metadata are stored as `jsonb`.
141
+
127
142
  ### SQLite Adapter
128
143
 
129
144
  ```typescript
@@ -131,9 +146,11 @@ import { SQLiteAdapter } from '@cogitator-ai/memory';
131
146
 
132
147
  const memory = new SQLiteAdapter({
133
148
  provider: 'sqlite',
134
- path: './cogitator.db',
149
+ path: './cogitator.db', // ':memory:' for an in-memory database
135
150
  walMode: true,
136
151
  });
152
+
153
+ await memory.connect();
137
154
  ```
138
155
 
139
156
  ### MongoDB Adapter
@@ -146,8 +163,12 @@ const memory = new MongoDBAdapter({
146
163
  uri: 'mongodb://localhost:27017',
147
164
  database: 'cogitator',
148
165
  });
166
+
167
+ await memory.connect();
149
168
  ```
150
169
 
170
+ Threads and entries live in the `<collectionPrefix>threads` and `<collectionPrefix>entries` collections (prefix `memory_` by default), indexed on `agentId` and `(threadId, createdAt)`. Fields left `undefined` are omitted from the stored documents, not stored as `null`. A failed `connect()` leaves the adapter disconnected, so a later `connect()` retries.
171
+
151
172
  ### Qdrant Adapter (Embedding)
152
173
 
153
174
  ```typescript
@@ -159,8 +180,12 @@ const embeddings = new QdrantAdapter({
159
180
  collection: 'cogitator',
160
181
  dimensions: 1536,
161
182
  });
183
+
184
+ await embeddings.connect();
162
185
  ```
163
186
 
187
+ `QdrantAdapter` is an `EmbeddingAdapter` (store and search vectors), not a thread store; pair it with one of the adapters above. Points are stored under a deterministic UUID derived from the embedding id (Qdrant accepts only integer and UUID point ids); the `emb_…` id stays in `payload.embeddingId` and is what `search` returns.
188
+
164
189
  ### Factory Function
165
190
 
166
191
  ```typescript
@@ -187,8 +212,13 @@ const qdrant = await createEmbeddingAdapter({
187
212
  provider: 'qdrant',
188
213
  dimensions: 1536,
189
214
  });
215
+
216
+ await postgres.connect(); // the factories create adapters; connect them before use
217
+ await qdrant.connect(); // typed as ConnectableEmbeddingAdapter, no cast needed
190
218
  ```
191
219
 
220
+ `createMemoryAdapter` loads the adapter's driver lazily, so only the drivers you use need to be installed.
221
+
192
222
  ---
193
223
 
194
224
  ## MemoryAdapter Interface
@@ -220,13 +250,14 @@ interface MemoryAdapter {
220
250
  `createThread(agentId, metadata, threadId)` with an existing `threadId` is an upsert: entries and `createdAt` are kept and only the metadata is updated, so it is safe to call on every run.
221
251
 
222
252
  ```typescript
223
- const result = await memory.createThread('agent-1', {
224
- topic: 'support',
225
- user: 'user-123',
226
- });
227
- const thread = result.data!;
253
+ const thread = unwrap(
254
+ await memory.createThread('agent-1', {
255
+ topic: 'support',
256
+ user: 'user-123',
257
+ })
258
+ );
228
259
 
229
- const threadResult = await memory.getThread(thread.id);
260
+ const found = unwrap(await memory.getThread(thread.id)); // Thread | null
230
261
 
231
262
  await memory.updateThread(thread.id, {
232
263
  resolved: true,
@@ -238,21 +269,26 @@ await memory.deleteThread(thread.id);
238
269
  ### Entry Operations
239
270
 
240
271
  ```typescript
241
- const entry = await memory.addEntry({
242
- threadId: thread.id,
243
- message: { role: 'user', content: 'Hello' },
244
- tokenCount: 10,
245
- });
272
+ const entry = unwrap(
273
+ await memory.addEntry({
274
+ threadId: thread.id,
275
+ message: { role: 'user', content: 'Hello' },
276
+ tokenCount: 10,
277
+ })
278
+ );
246
279
 
247
- const entries = await memory.getEntries({
248
- threadId: thread.id,
249
- limit: 50,
250
- includeToolCalls: true,
251
- });
280
+ const entries = unwrap(
281
+ await memory.getEntries({
282
+ threadId: thread.id,
283
+ limit: 50, // the most recent 50, oldest first
284
+ includeToolCalls: true,
285
+ // before / after: Date bounds
286
+ })
287
+ );
252
288
 
253
- const single = await memory.getEntry(entry.data!.id);
289
+ const single = unwrap(await memory.getEntry(entry.id));
254
290
 
255
- await memory.deleteEntry(entry.data!.id);
291
+ await memory.deleteEntry(entry.id);
256
292
 
257
293
  await memory.clearThread(thread.id);
258
294
  ```
@@ -273,12 +309,14 @@ interface ContextBuilderConfig {
273
309
  includeSystemPrompt?: boolean;
274
310
  includeFacts?: boolean;
275
311
  includeSemanticContext?: boolean;
312
+ includeGraphContext?: boolean;
313
+ graphContextOptions?: GraphContextOptions;
276
314
  }
277
315
  ```
278
316
 
279
317
  Strategies:
280
318
 
281
- - `recent` - newest messages that fit into the token budget
319
+ - `recent` - the newest messages that fit into the token budget, as one unbroken run: the window stops at the first message that does not fit, so a reply never appears without the message it answers
282
320
  - `relevant` - messages ranked by embedding similarity to `currentInput` (requires `embeddingService`; falls back to `recent` without input), returned in chronological order
283
321
  - `hybrid` - always keeps the latest messages and fills the remaining budget with the most relevant older ones
284
322
 
@@ -346,6 +384,7 @@ interface BuiltContext {
346
384
  messages: Message[];
347
385
  facts: Fact[];
348
386
  semanticResults: (Embedding & { score: number })[];
387
+ graphContext?: GraphContext; // with includeGraphContext and a graph adapter
349
388
  tokenCount: number;
350
389
  truncated: boolean;
351
390
  metadata: {
@@ -364,12 +403,26 @@ interface BuiltContext {
364
403
  `SessionManager` maps channel conversations (user + channel + agent) onto memory threads. `CompactionService` replaces old history with an LLM summary placed before the most recent messages.
365
404
 
366
405
  ```typescript
406
+ import { createLLMBackend } from '@cogitator-ai/core';
367
407
  import { InMemoryAdapter, SessionManager, CompactionService } from '@cogitator-ai/memory';
368
408
 
369
409
  const memory = new InMemoryAdapter();
410
+ const llm = createLLMBackend('google', {
411
+ providers: { google: { apiKey: process.env.GOOGLE_API_KEY! } },
412
+ });
413
+
370
414
  const compaction = new CompactionService({
371
415
  adapter: memory,
372
- summarize: async (messages) => `Summary of ${messages.length} messages`,
416
+ summarize: async (messages, { model, prompt } = {}) => {
417
+ const response = await llm.chat({
418
+ model: model ?? 'gemini-3.5-flash-lite',
419
+ messages: [
420
+ { role: 'system', content: prompt ?? 'Summarize this conversation in a few sentences.' },
421
+ ...messages,
422
+ ],
423
+ });
424
+ return response.content;
425
+ },
373
426
  });
374
427
  const sessions = new SessionManager(memory, { compaction });
375
428
 
@@ -383,10 +436,36 @@ const session = await sessions.getOrCreate({
383
436
  const all = await sessions.list();
384
437
  const mine = await sessions.list({ userId: 'user-1', status: 'active', limit: 20 });
385
438
 
386
- await sessions.compact(session.id, { strategy: 'summary', threshold: 8000, keepRecent: 10 });
439
+ await sessions.compact(session.id, {
440
+ strategy: 'summary',
441
+ threshold: 8000,
442
+ keepRecent: 10,
443
+ summaryModel: 'gemini-3.5-flash',
444
+ summaryPrompt: 'Keep names, decisions and open questions.',
445
+ });
387
446
  ```
388
447
 
389
- `list()` works with or without a `userId` filter (sessions are tracked in an index thread). `compact()` requires the `compaction` option; without it use `CompactionService` directly.
448
+ `list()` works with or without a `userId` filter (sessions are tracked in an index thread). `compact()` requires the `compaction` option; without it use `CompactionService` directly. The summarizer (`SummarizeFn`) is called as `summarize(messages, options)`, where `options` is a `SummarizeOptions` `{ model?, prompt? }` filled from the `summaryModel` / `summaryPrompt` of the compaction config.
449
+
450
+ ---
451
+
452
+ ## Core Facts
453
+
454
+ `CoreFactsStore` keeps a small set of key/value facts about the user in SQLite (with history), formatted for a system prompt:
455
+
456
+ ```typescript
457
+ import { CoreFactsStore } from '@cogitator-ai/memory';
458
+
459
+ const facts = new CoreFactsStore({ path: './facts.db' }); // or { db } to share a better-sqlite3 database
460
+ await facts.initialize();
461
+
462
+ await facts.set('name', 'Alice');
463
+ await facts.get('name'); // 'Alice'
464
+ await facts.getAll(); // { name: 'Alice' }
465
+ await facts.getHistory('name'); // [{ value, setAt }]
466
+ const prompt = await facts.formatForPrompt();
467
+ await facts.close();
468
+ ```
390
469
 
391
470
  ---
392
471
 
@@ -425,10 +504,12 @@ import { OpenAIEmbeddingService, createEmbeddingService } from '@cogitator-ai/me
425
504
 
426
505
  const embeddings = new OpenAIEmbeddingService({
427
506
  apiKey: process.env.OPENAI_API_KEY!,
428
- model: 'text-embedding-3-small',
507
+ model: 'text-embedding-3-small', // default
508
+ dimensions: 512, // optional, sent to text-embedding-3 models (gateway ids like openai/text-embedding-3-small too)
429
509
  });
430
510
 
431
- const embeddings = createEmbeddingService({
511
+ // or through the factory
512
+ const viaFactory = createEmbeddingService({
432
513
  provider: 'openai',
433
514
  apiKey: process.env.OPENAI_API_KEY!,
434
515
  });
@@ -438,6 +519,8 @@ const vector = await embeddings.embed('Hello, world!');
438
519
  const vectors = await embeddings.embedBatch(['Hello', 'World']);
439
520
  ```
440
521
 
522
+ For other models `dimensions` declares the size they return. When a response holds vectors of another size than the configured `dimensions`, `embed()` and `embedBatch()` throw instead of handing them to a store sized for it.
523
+
441
524
  ### Ollama Embeddings
442
525
 
443
526
  ```typescript
@@ -448,7 +531,7 @@ const embeddings = new OllamaEmbeddingService({
448
531
  baseUrl: 'http://localhost:11434',
449
532
  });
450
533
 
451
- const embeddings = createEmbeddingService({
534
+ const viaFactory = createEmbeddingService({
452
535
  provider: 'ollama',
453
536
  model: 'nomic-embed-text',
454
537
  });
@@ -467,7 +550,7 @@ const embeddings = new GoogleEmbeddingService({
467
550
  dimensions: 768,
468
551
  });
469
552
 
470
- const embeddings = createEmbeddingService({
553
+ const viaFactory = createEmbeddingService({
471
554
  provider: 'google',
472
555
  apiKey: process.env.GOOGLE_API_KEY!,
473
556
  });
@@ -530,15 +613,15 @@ const hybridResults = await search.search({
530
613
  });
531
614
 
532
615
  // Results include both scores
533
- hybridResults.data.forEach((result) => {
616
+ for (const result of unwrap(hybridResults)) {
534
617
  console.log(`${result.content} — score: ${result.score}`);
535
618
  console.log(` vector: ${result.vectorScore}, keyword: ${result.keywordScore}`);
536
- });
619
+ }
537
620
  ```
538
621
 
539
622
  ### Document Indexing
540
623
 
541
- For keyword search, documents must be indexed:
624
+ With a `keywordAdapter`, keyword search runs on that adapter's own index (embeddings added to `InMemoryEmbeddingAdapter` or `PostgresAdapter` are searchable right away). Without one, `HybridSearch` keeps a local BM25 index that you fill yourself (it ignores `filter`):
542
625
 
543
626
  ```typescript
544
627
  // Add documents to BM25 index
@@ -653,12 +736,12 @@ import type {
653
736
  ### Conversation History
654
737
 
655
738
  ```typescript
656
- import { InMemoryAdapter } from '@cogitator-ai/memory';
739
+ import { InMemoryAdapter, countMessageTokens, unwrap } from '@cogitator-ai/memory';
657
740
 
658
741
  const memory = new InMemoryAdapter();
659
742
  await memory.connect();
660
743
 
661
- const thread = await memory.createThread('chatbot');
744
+ const thread = unwrap(await memory.createThread('chatbot'));
662
745
 
663
746
  const messages = [
664
747
  { role: 'user' as const, content: 'What is AI?' },
@@ -668,15 +751,18 @@ const messages = [
668
751
 
669
752
  for (const msg of messages) {
670
753
  await memory.addEntry({
671
- threadId: thread.data!.id,
754
+ threadId: thread.id,
672
755
  message: msg,
756
+ tokenCount: countMessageTokens(msg),
673
757
  });
674
758
  }
675
759
 
676
- const history = await memory.getEntries({
677
- threadId: thread.data!.id,
678
- limit: 10,
679
- });
760
+ const history = unwrap(
761
+ await memory.getEntries({
762
+ threadId: thread.id,
763
+ limit: 10,
764
+ })
765
+ );
680
766
  ```
681
767
 
682
768
  ### Token-Limited Context
@@ -708,7 +794,7 @@ console.log(
708
794
  ### Semantic Memory with PostgreSQL
709
795
 
710
796
  ```typescript
711
- import { PostgresAdapter, OpenAIEmbeddingService } from '@cogitator-ai/memory';
797
+ import { PostgresAdapter, OpenAIEmbeddingService, unwrap } from '@cogitator-ai/memory';
712
798
 
713
799
  const memory = new PostgresAdapter({
714
800
  provider: 'postgres',
@@ -722,7 +808,22 @@ const embeddings = new OpenAIEmbeddingService({
722
808
  await memory.connect();
723
809
 
724
810
  const content = 'Machine learning is a subset of AI...';
725
- const vector = await embeddings.embed(content);
811
+ await memory.addEmbedding({
812
+ sourceId: 'doc-1',
813
+ sourceType: 'document',
814
+ content,
815
+ vector: await embeddings.embed(content),
816
+ metadata: { agentId: 'agent-1' },
817
+ });
818
+
819
+ const matches = unwrap(
820
+ await memory.search({
821
+ vector: await embeddings.embed('What is ML?'),
822
+ limit: 5,
823
+ threshold: 0.7,
824
+ filter: { agentId: 'agent-1' },
825
+ })
826
+ );
726
827
  ```
727
828
 
728
829
  ---
@@ -733,32 +834,39 @@ Entity-relationship memory with multi-hop traversal and semantic reasoning.
733
834
 
734
835
  ### PostgresGraphAdapter
735
836
 
736
- ```typescript
737
- import { PostgresGraphAdapter } from '@cogitator-ai/memory';
837
+ Graph adapters return `MemoryResult`s like the memory adapters. `PostgresGraphAdapter` takes an existing `pg` pool and creates its tables on first use; `SQLiteGraphAdapter` takes `{ path, walMode? }`.
738
838
 
739
- const graph = new PostgresGraphAdapter({
740
- connectionString: process.env.DATABASE_URL!,
741
- });
839
+ ```typescript
840
+ import { PostgresGraphAdapter, unwrap } from '@cogitator-ai/memory';
841
+ import pg from 'pg';
742
842
 
743
- await graph.connect();
843
+ const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
844
+ const graph = new PostgresGraphAdapter({ pool, vectorDimensions: 1536 }); // default 768
744
845
 
745
- const person = await graph.addNode({
746
- agentId: 'agent-1',
747
- type: 'person',
748
- name: 'Alice',
749
- description: 'Software engineer',
750
- properties: { role: 'developer' },
751
- confidence: 1.0,
752
- source: 'user',
753
- });
846
+ const person = unwrap(
847
+ await graph.addNode({
848
+ agentId: 'agent-1',
849
+ type: 'person',
850
+ name: 'Alice',
851
+ aliases: [],
852
+ description: 'Software engineer',
853
+ properties: { role: 'developer' },
854
+ confidence: 1.0,
855
+ source: 'user',
856
+ })
857
+ );
754
858
 
755
- const company = await graph.addNode({
756
- agentId: 'agent-1',
757
- type: 'organization',
758
- name: 'TechCorp',
759
- confidence: 1.0,
760
- source: 'extracted',
761
- });
859
+ const company = unwrap(
860
+ await graph.addNode({
861
+ agentId: 'agent-1',
862
+ type: 'organization',
863
+ name: 'TechCorp',
864
+ aliases: [],
865
+ properties: {},
866
+ confidence: 1.0,
867
+ source: 'extracted',
868
+ })
869
+ );
762
870
 
763
871
  await graph.addEdge({
764
872
  agentId: 'agent-1',
@@ -766,6 +874,8 @@ await graph.addEdge({
766
874
  targetNodeId: company.id,
767
875
  type: 'works_at',
768
876
  weight: 1.0,
877
+ bidirectional: false,
878
+ properties: {},
769
879
  confidence: 0.95,
770
880
  source: 'extracted',
771
881
  });
@@ -774,12 +884,15 @@ await graph.addEdge({
774
884
  ### Multi-hop Traversal
775
885
 
776
886
  ```typescript
777
- const result = await graph.traverse({
778
- startNodeId: person.id,
779
- maxDepth: 3,
780
- direction: 'outgoing',
781
- edgeTypes: ['works_at', 'knows', 'located_in'],
782
- });
887
+ const result = unwrap(
888
+ await graph.traverse({
889
+ agentId: 'agent-1',
890
+ startNodeId: person.id,
891
+ maxDepth: 3,
892
+ direction: 'outgoing', // 'outgoing' | 'incoming' | 'both'
893
+ edgeTypes: ['works_at', 'knows', 'located_in'],
894
+ })
895
+ );
783
896
 
784
897
  console.log('Visited nodes:', result.visitedNodes);
785
898
  console.log('Paths found:', result.paths);
@@ -788,7 +901,7 @@ console.log('Paths found:', result.paths);
788
901
  ### Shortest Path
789
902
 
790
903
  ```typescript
791
- const path = await graph.findShortestPath(startNodeId, endNodeId);
904
+ const path = unwrap(await graph.findShortestPath('agent-1', person.id, company.id, 5));
792
905
  if (path) {
793
906
  console.log('Path:', path.nodes.map((n) => n.name).join(' -> '));
794
907
  console.log('Total weight:', path.totalWeight);
@@ -798,12 +911,14 @@ if (path) {
798
911
  ### Semantic Node Search
799
912
 
800
913
  ```typescript
801
- const similar = await graph.searchNodesSemantic({
802
- agentId: 'agent-1',
803
- query: 'machine learning engineer',
804
- limit: 10,
805
- threshold: 0.7,
806
- });
914
+ const similar = unwrap(
915
+ await graph.searchNodesSemantic({
916
+ agentId: 'agent-1',
917
+ vector: await embeddingService.embed('machine learning engineer'), // a vector is required
918
+ limit: 10,
919
+ threshold: 0.7,
920
+ })
921
+ ); // nodes with a `score`
807
922
  ```
808
923
 
809
924
  ### LLM Entity Extraction
@@ -811,32 +926,39 @@ const similar = await graph.searchNodesSemantic({
811
926
  ```typescript
812
927
  import { LLMEntityExtractor } from '@cogitator-ai/memory';
813
928
 
814
- const extractor = new LLMEntityExtractor(llmBackend, {
929
+ const backend = cog.getLLMBackend('openai/gpt-5.5');
930
+
931
+ const extractor = new LLMEntityExtractor(backend, {
932
+ model: 'gpt-5.5', // required with an LLMBackend
815
933
  minConfidence: 0.7,
816
934
  maxEntitiesPerText: 20,
935
+ maxRelationsPerText: 30,
817
936
  });
818
937
 
819
938
  const result = await extractor.extract(
820
- 'Alice works at TechCorp in San Francisco. She knows Bob from the AI conference.'
939
+ 'Alice works at TechCorp in San Francisco. She knows Bob from the AI conference.',
940
+ { agentId: 'agent-1' }
821
941
  );
822
942
 
823
943
  console.log('Entities:', result.entities);
824
944
  console.log('Relations:', result.relations);
825
945
  ```
826
946
 
947
+ Any object with a `chat({ model?, messages, responseFormat })` method resolving to `{ content }` (`LLMBackendMinimal`) also works as the first argument; there `model` is optional and, when set, is passed through in each request.
948
+
827
949
  ### Graph Inference Engine
828
950
 
829
951
  ```typescript
830
952
  import { GraphInferenceEngine } from '@cogitator-ai/memory';
831
953
 
832
- const engine = new GraphInferenceEngine(graph, {
833
- minConfidence: 0.5,
834
- maxInferredEdges: 100,
835
- });
954
+ const engine = new GraphInferenceEngine(graph); // built-in rules; pass false to start without them
955
+
956
+ const inferred = await engine.infer('agent-1', { minConfidence: 0.5, maxInferences: 100 });
957
+ for (const edge of inferred) {
958
+ console.log(edge.type, edge.sourceNodeId, '->', edge.targetNodeId, 'via rule', edge.ruleId);
959
+ }
836
960
 
837
- const inferred = await engine.infer('agent-1');
838
- console.log('Inferred edges:', inferred.edges);
839
- console.log('Rules applied:', inferred.rulesApplied);
961
+ await engine.materialize(inferred); // store them as edges
840
962
  ```
841
963
 
842
964
  ### Graph Context Builder
@@ -850,13 +972,14 @@ const contextBuilder = new GraphContextBuilder(graph, embeddingService, {
850
972
  includeInferred: true,
851
973
  });
852
974
 
853
- const context = await contextBuilder.buildContext({
854
- agentId: 'agent-1',
855
- query: 'Tell me about Alice and her work',
975
+ const context = await contextBuilder.buildContext('agent-1', 'Tell me about Alice and her work', {
976
+ maxDepth: 2,
977
+ userId: 'user-1', // leave out other users' nodes (metadata.userId)
856
978
  });
857
979
 
858
980
  console.log('Relevant nodes:', context.nodes);
859
981
  console.log('Relationships:', context.edges);
982
+ console.log(context.formattedContext); // ready for a system prompt
860
983
  ```
861
984
 
862
985
  ---
@@ -1,11 +1,19 @@
1
1
  /**
2
2
  * Memory adapter exports and factory
3
3
  */
4
- import type { MemoryAdapter, EmbeddingAdapter, InMemoryAdapterConfig, RedisAdapterConfig, PostgresAdapterConfig, SQLiteAdapterConfig, MongoDBAdapterConfig, QdrantAdapterConfig } from '@cogitator-ai/types';
4
+ import type { MemoryAdapter, MemoryResult, EmbeddingAdapter, InMemoryAdapterConfig, RedisAdapterConfig, PostgresAdapterConfig, SQLiteAdapterConfig, MongoDBAdapterConfig, QdrantAdapterConfig } from '@cogitator-ai/types';
5
5
  export { BaseMemoryAdapter } from './base.js';
6
6
  export { InMemoryAdapter } from './memory.js';
7
7
  export type MemoryAdapterConfigUnion = InMemoryAdapterConfig | RedisAdapterConfig | PostgresAdapterConfig | SQLiteAdapterConfig | MongoDBAdapterConfig;
8
8
  export type EmbeddingAdapterConfigUnion = QdrantAdapterConfig;
9
+ /**
10
+ * An embedding store that talks to a server, as `createEmbeddingAdapter` returns it: call
11
+ * `connect()` before the first operation and `disconnect()` when done.
12
+ */
13
+ export interface ConnectableEmbeddingAdapter extends EmbeddingAdapter {
14
+ connect(): Promise<MemoryResult<void>>;
15
+ disconnect(): Promise<MemoryResult<void>>;
16
+ }
9
17
  export declare function createMemoryAdapter(config: MemoryAdapterConfigUnion): Promise<MemoryAdapter>;
10
- export declare function createEmbeddingAdapter(config: EmbeddingAdapterConfigUnion): Promise<EmbeddingAdapter>;
18
+ export declare function createEmbeddingAdapter(config: EmbeddingAdapterConfigUnion): Promise<ConnectableEmbeddingAdapter>;
11
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAChC,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAE9D,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,aAAa,CAAC,CAgCxB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,gBAAgB,CAAC,CAO3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,wBAAwB,GAChC,qBAAqB,GACrB,kBAAkB,GAClB,qBAAqB,GACrB,mBAAmB,GACnB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAE9D;;;GAGG;AACH,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACnE,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;CAC3C;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,aAAa,CAAC,CAgCxB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,2BAA2B,CAAC,CAOtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAW3C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAgC;IAEhC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QAErC,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvD,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAED,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YACrD,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,MAAM,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,4BAA6B,UAAuC,CAAC,QAAQ,EAAE,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAmC;IAEnC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAoB3C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAgC;IAEhC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ;YACX,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QAErC,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvD,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAED,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YACrD,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,MAAM,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,4BAA6B,UAAuC,CAAC,QAAQ,EAAE,CAChF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAmC;IAEnC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACnD,OAAO,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -4,13 +4,19 @@ export declare class MongoDBAdapter extends BaseMemoryAdapter {
4
4
  readonly provider: MemoryProvider;
5
5
  private client;
6
6
  private db;
7
+ private connecting;
7
8
  private uri;
8
9
  private database;
9
10
  private prefix;
10
11
  constructor(config: MongoDBAdapterConfig);
11
12
  private get threads();
12
13
  private get entries();
14
+ /**
15
+ * Opens the client and builds the indexes. Concurrent calls share one attempt, and a failed
16
+ * attempt closes its client and leaves the adapter disconnected, so the next call tries again.
17
+ */
13
18
  connect(): Promise<MemoryResult<void>>;
19
+ private open;
14
20
  disconnect(): Promise<MemoryResult<void>>;
15
21
  createThread(agentId: string, metadata?: Record<string, unknown>, threadId?: string): Promise<MemoryResult<Thread>>;
16
22
  getThread(threadId: string): Promise<MemoryResult<Thread | null>>;
@@ -1 +1 @@
1
- {"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../src/adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAuD3C,qBAAa,cAAe,SAAQ,iBAAiB;IACnD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAa;IAE9C,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,EAAE,CAAmB;IAC7B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB;IAOxC,OAAO,KAAK,OAAO,GAGlB;IAED,OAAO,KAAK,OAAO,GAGlB;IAEK,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAyBtC,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAezC,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACtC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAyB1B,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAmBjE,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAwB1B,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAY3D,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IA0B1F,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IAuC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAsBpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAWzD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;CAUjE"}
1
+ {"version":3,"file":"mongodb.d.ts","sourceRoot":"","sources":["../../src/adapters/mongodb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,YAAY,EAEZ,oBAAoB,EACpB,cAAc,EAGf,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAyF3C,qBAAa,cAAe,SAAQ,iBAAiB;IACnD,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAa;IAE9C,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,EAAE,CAA8B;IACxC,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB;IAOxC,OAAO,KAAK,OAAO,GAGlB;IAED,OAAO,KAAK,OAAO,GAGlB;IAED;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAQ9B,IAAI;IA2BZ,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAgBzC,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACtC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAyB1B,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAmBjE,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAwB1B,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAY3D,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IA0B1F,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IAyB7E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAWpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAWzD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;CAUjE"}