memo-grafter 0.2.3 → 0.2.5

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 (85) hide show
  1. package/USER_GUIDE.md +325 -71
  2. package/dist/MemoGrafter.d.ts +8 -5
  3. package/dist/MemoGrafter.d.ts.map +1 -1
  4. package/dist/MemoGrafter.js +12 -10
  5. package/dist/MemoGrafter.js.map +1 -1
  6. package/dist/MemoGrafterAgent.d.ts +5 -2
  7. package/dist/MemoGrafterAgent.d.ts.map +1 -1
  8. package/dist/MemoGrafterAgent.js +15 -3
  9. package/dist/MemoGrafterAgent.js.map +1 -1
  10. package/dist/crawler/ConflictDetectionPass.d.ts +6 -0
  11. package/dist/crawler/ConflictDetectionPass.d.ts.map +1 -0
  12. package/dist/crawler/ConflictDetectionPass.js +46 -0
  13. package/dist/crawler/ConflictDetectionPass.js.map +1 -0
  14. package/dist/crawler/DecayScoringPass.d.ts +17 -0
  15. package/dist/crawler/DecayScoringPass.d.ts.map +1 -0
  16. package/dist/crawler/DecayScoringPass.js +73 -0
  17. package/dist/crawler/DecayScoringPass.js.map +1 -0
  18. package/dist/crawler/MemoGrafterCrawler.d.ts +14 -0
  19. package/dist/crawler/MemoGrafterCrawler.d.ts.map +1 -0
  20. package/dist/crawler/MemoGrafterCrawler.js +108 -0
  21. package/dist/crawler/MemoGrafterCrawler.js.map +1 -0
  22. package/dist/crawler/VersioningPass.d.ts +6 -0
  23. package/dist/crawler/VersioningPass.d.ts.map +1 -0
  24. package/dist/crawler/VersioningPass.js +43 -0
  25. package/dist/crawler/VersioningPass.js.map +1 -0
  26. package/dist/crawler/decayScoring.d.ts +7 -0
  27. package/dist/crawler/decayScoring.d.ts.map +1 -0
  28. package/dist/crawler/decayScoring.js +9 -0
  29. package/dist/crawler/decayScoring.js.map +1 -0
  30. package/dist/crawler/index.d.ts +7 -0
  31. package/dist/crawler/index.d.ts.map +1 -0
  32. package/dist/crawler/index.js +5 -0
  33. package/dist/crawler/index.js.map +1 -0
  34. package/dist/crawler/memoryMaintenance.d.ts +15 -0
  35. package/dist/crawler/memoryMaintenance.d.ts.map +1 -0
  36. package/dist/crawler/memoryMaintenance.js +110 -0
  37. package/dist/crawler/memoryMaintenance.js.map +1 -0
  38. package/dist/crawler/types.d.ts +63 -0
  39. package/dist/crawler/types.d.ts.map +1 -0
  40. package/dist/crawler/types.js +2 -0
  41. package/dist/crawler/types.js.map +1 -0
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
  47. package/dist/pipeline/GrafterPipeline.js +42 -1
  48. package/dist/pipeline/GrafterPipeline.js.map +1 -1
  49. package/dist/pipeline/IngestPipeline.d.ts +7 -3
  50. package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
  51. package/dist/pipeline/IngestPipeline.js +24 -13
  52. package/dist/pipeline/IngestPipeline.js.map +1 -1
  53. package/dist/pipeline/RetrieverPipeline.d.ts +3 -0
  54. package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -1
  55. package/dist/pipeline/RetrieverPipeline.js +56 -13
  56. package/dist/pipeline/RetrieverPipeline.js.map +1 -1
  57. package/dist/pipeline/SegmentProcessor.d.ts +3 -1
  58. package/dist/pipeline/SegmentProcessor.d.ts.map +1 -1
  59. package/dist/pipeline/SegmentProcessor.js +9 -5
  60. package/dist/pipeline/SegmentProcessor.js.map +1 -1
  61. package/dist/prompts/memoryInjectionPrompt.d.ts +6 -2
  62. package/dist/prompts/memoryInjectionPrompt.d.ts.map +1 -1
  63. package/dist/prompts/memoryInjectionPrompt.js +25 -2
  64. package/dist/prompts/memoryInjectionPrompt.js.map +1 -1
  65. package/dist/queue/IngestQueue.d.ts +2 -2
  66. package/dist/queue/IngestQueue.d.ts.map +1 -1
  67. package/dist/queue/IngestQueue.js +3 -2
  68. package/dist/queue/IngestQueue.js.map +1 -1
  69. package/dist/store/GraphStore.d.ts +13 -3
  70. package/dist/store/GraphStore.d.ts.map +1 -1
  71. package/dist/store/postgres-pgvector/GraphStore.d.ts +16 -3
  72. package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -1
  73. package/dist/store/postgres-pgvector/GraphStore.js +170 -6
  74. package/dist/store/postgres-pgvector/GraphStore.js.map +1 -1
  75. package/dist/types.d.ts +33 -0
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/utils/drift/adaptiveDriftSensitivity.d.ts +20 -0
  78. package/dist/utils/drift/adaptiveDriftSensitivity.d.ts.map +1 -0
  79. package/dist/utils/drift/adaptiveDriftSensitivity.js +67 -0
  80. package/dist/utils/drift/adaptiveDriftSensitivity.js.map +1 -0
  81. package/dist/utils/tags.d.ts +2 -0
  82. package/dist/utils/tags.d.ts.map +1 -0
  83. package/dist/utils/tags.js +13 -0
  84. package/dist/utils/tags.js.map +1 -0
  85. package/package.json +1 -1
package/USER_GUIDE.md CHANGED
@@ -84,11 +84,13 @@ Current v1 tables:
84
84
  - `mg_topic_nodes`
85
85
  - `mg_topic_edges`
86
86
  - `mg_memory_nodes`
87
- - `mg_memory_edges`
88
- - `mg_fleets`
89
- - `mg_fleet_agents`
90
- - `mg_session_ingest_state`
91
- - `mg_graft_registry`
87
+ - `mg_memory_edges`
88
+ - `mg_fleets`
89
+ - `mg_fleet_agents`
90
+ - `mg_session_ingest_state`
91
+ - `mg_graft_registry`
92
+
93
+ `mg_topic_nodes` and `mg_memory_nodes` include optional `tags TEXT[]` columns. Tags default to an empty array, so existing untagged sessions continue to work normally.
92
94
 
93
95
  ## Quick Start
94
96
 
@@ -179,6 +181,7 @@ Important fields:
179
181
  - `label`: short label.
180
182
  - `summary`: structured summary of the segment.
181
183
  - `embedding`: vector used for semantic search.
184
+ - `tags`: optional normalized tags such as `"project:memo-grafter"` or `"planning"`.
182
185
  - `messageRange`: source message range.
183
186
  - `topicOrder`: chronological order.
184
187
  - `driftScore`: topic-change score.
@@ -196,11 +199,15 @@ Important fields:
196
199
  - `subject`, `predicate`, `value`: the structured memory triple.
197
200
  - `confidence`: confidence score from `0` to `1`.
198
201
  - `topicNodeId`: parent topic node ID.
202
+ - `tags`: optional normalized tags copied from the session or ingest call.
199
203
  - `decayed`: whether the memory is stale.
204
+ - `hasConflict`: whether crawler maintenance found a conflicting active fact.
200
205
  - `supersededBy`: newer memory ID when this memory has been replaced.
201
206
 
202
207
  Memory nodes are stored in `mg_memory_nodes`.
203
208
 
209
+ `decayed`, `hasConflict`, and `supersededBy` are maintenance fields. Normal ingestion creates active memories. Optional crawler passes can later annotate existing memory rows, but they do not delete rows or rewrite topic summaries.
210
+
204
211
  ### Graph Edges
205
212
 
206
213
  Edges connect related topic nodes. They can represent temporal, semantic, grafted, or reentry relationships.
@@ -212,6 +219,19 @@ Edges connect related topic nodes. They can represent temporal, semantic, grafte
212
219
 
213
220
  Edges are stored in `mg_topic_edges`.
214
221
 
222
+ Memory edges are stored separately in `mg_memory_edges`. They can represent:
223
+
224
+ - `semantic`: two memory facts are similar.
225
+ - `conflicts`: two active memory facts disagree.
226
+ - `updates`: a newer memory supersedes an older memory.
227
+ - `related`: reserved for broader memory relationships.
228
+
229
+ For version edges, MemoGrafter uses this direction:
230
+
231
+ ```text
232
+ newer_memory --updates--> older_memory
233
+ ```
234
+
215
235
  ### Grafting
216
236
 
217
237
  Grafting is the process of selecting topic nodes and turning them into useful context for another prompt or another chatbot.
@@ -221,9 +241,9 @@ There are two common forms:
221
241
  - Preview memory with `graft()`.
222
242
  - Copy memory into another chatbot with `absorbFromAgent()` or `ingestGraftedNodes()`.
223
243
 
224
- When topic nodes are absorbed into another session, MemoGrafter also copies their active memory nodes so targeted recall can find the transferred facts. Copied memory rows get fresh IDs, keep their existing embeddings, and are copied as active memories only when the source row is not decayed or superseded.
225
-
226
- Absorbed topic nodes are also registered in `mg_graft_registry`. The registry records the destination session, copied node ID, source session ID, source node ID, and graft timestamp so applications can inspect provenance or remove a graft later.
244
+ When topic nodes are absorbed into another session, MemoGrafter also copies their active memory nodes so targeted recall can find the transferred facts. Copied memory rows get fresh IDs, keep their existing embeddings, and are copied as active memories only when the source row is not decayed or superseded.
245
+
246
+ Absorbed topic nodes are also registered in `mg_graft_registry`. The registry records the destination session, copied node ID, source session ID, source node ID, and graft timestamp so applications can inspect provenance or remove a graft later.
227
247
 
228
248
  ## Using MemoGrafterAgent
229
249
 
@@ -286,6 +306,32 @@ await agent.clearSession();
286
306
 
287
307
  This waits for pending ingestion, clears the agent's local in-memory history, removes stored messages, topic nodes, memory nodes, graph edges, segments, and resets the session ingest cursor. It is a destructive operation and is not part of normal `invoke()` processing.
288
308
 
309
+ ### Session Tags
310
+
311
+ Use session tags when you want to organize memory by project, planning area, week, domain, or worker route.
312
+
313
+ ```ts
314
+ await agent.setSessionTags([
315
+ "project:memo-grafter",
316
+ "planning",
317
+ "week:2026-05-25",
318
+ ]);
319
+
320
+ console.log(agent.getSessionTags());
321
+ ```
322
+
323
+ Tags are optional. They are normalized by trimming whitespace, lowercasing, deduplicating, and sorting. Calling `setSessionTags()` waits for pending ingestion, updates existing topic and memory rows for the current session, and applies the same tags to future memories created by `invoke()`.
324
+
325
+ You can also tag direct ingestion:
326
+
327
+ ```ts
328
+ await memo.ingest(messages, sessionId, {
329
+ tags: ["project:memo-grafter", "planning"],
330
+ });
331
+ ```
332
+
333
+ Tags do not replace `sessionId`. By default, MemoGrafter still reads from the current session. Tag filters are opt-in.
334
+
289
335
  ### Targeted Recall
290
336
 
291
337
  Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
@@ -295,6 +341,13 @@ const result = await agent.recall("deployment config", {
295
341
  limit: 8,
296
342
  minSimilarity: 0.55,
297
343
  tokenBudget: 1000,
344
+ tags: ["project:memo-grafter"],
345
+ tagMode: "all",
346
+ scope: "session-and-tags",
347
+ scoring: {
348
+ similarityWeight: 0.7,
349
+ confidenceWeight: 0.3,
350
+ },
298
351
  cache: {
299
352
  ttlSeconds: 90,
300
353
  },
@@ -318,9 +371,26 @@ Options:
318
371
  - `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
319
372
  - `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
320
373
  - `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
374
+ - `tags`: optional normalized tag filter.
375
+ - `tagMode`: `"all"` requires every requested tag, `"any"` accepts at least one requested tag. Defaults to `"all"`.
376
+ - `scope`: `"session"` keeps normal current-session recall, `"session-and-tags"` filters current-session recall by tags, and `"tagged"` searches across sessions matching the tags.
377
+ - `scoring.similarityWeight`: weight applied to semantic similarity when ranking retrieved facts. Defaults to `0.7`.
378
+ - `scoring.confidenceWeight`: weight applied to memory confidence when ranking retrieved facts. Defaults to `0.3`.
321
379
  - `cache.ttlSeconds`: per-call recall cache TTL override when `MemoGrafterConfig.cache` is enabled. Values are clamped to 60-120 seconds.
322
380
 
323
- `recall()` is side-effect free. It does not call `invoke()`, does not trigger a new LLM completion, and does not mutate local history. Your application can call it directly to display memories, add `result.systemPrompt` to a model call, or ignore the result.
381
+ `recall()` is side-effect free. It does not call `invoke()`, does not trigger a new LLM completion, and does not mutate local history. Your application can call it directly to display memories, add `result.systemPrompt` to a model call, or ignore the result. Retrieval still uses `minSimilarity` for the vector search floor, then ranks returned active facts with `similarity * similarityWeight + confidence * confidenceWeight`.
382
+
383
+ Cross-session tagged recall is explicit:
384
+
385
+ ```ts
386
+ const projectMemory = await agent.recall("deployment decisions", {
387
+ tags: ["project:memo-grafter"],
388
+ scope: "tagged",
389
+ minSimilarity: 0.3,
390
+ });
391
+ ```
392
+
393
+ This can return matching active memories from older or different sessions with the same tag. If your development database contains repeated smoke-test runs, you may see more than one matching fact because the older tagged rows are still present.
324
394
 
325
395
  `MemoGrafterAgent.invoke()` also calls `recall()` internally before answering when the session has topic nodes. In that automatic path, the returned `systemPrompt` is pinned as a single system message before the recent raw chat window. Automatic recall uses `inject.recallLimit` and `inject.recallMinSimilarity`, defaulting to `6` and `0.55`.
326
396
 
@@ -334,25 +404,29 @@ Read a complete session graph snapshot:
334
404
  const snapshot = await agent.getGraphSnapshot();
335
405
 
336
406
  console.log(snapshot.sessionId);
337
- console.log(snapshot.nodes);
338
- console.log(snapshot.snapshotNodes);
339
- console.log(snapshot.edges);
340
- console.log(snapshot.memories);
341
- console.log(snapshot.capturedAt);
407
+ console.log(snapshot.nodes);
408
+ console.log(snapshot.snapshotNodes);
409
+ console.log(snapshot.edges);
410
+ console.log(snapshot.memories);
411
+ console.log(snapshot.memoryEdges);
412
+ console.log(snapshot.capturedAt);
342
413
  ```
343
414
 
344
415
  `getGraphSnapshot()` returns a `GraphSnapshot`:
345
416
 
346
- - `sessionId`: current agent session ID.
347
- - `nodes`: active topic nodes for the session.
348
- - `snapshotNodes`: active topic nodes wrapped with provenance metadata when a node came from a graft.
349
- - `edges`: topic edges where either endpoint belongs to a session topic node.
350
- - `memories`: all memory nodes for the session, including decayed or superseded rows.
351
- - `capturedAt`: ISO timestamp for when the snapshot was produced.
352
-
353
- Each `snapshotNodes` entry contains the topic `node` and an optional `graftOrigin` with `sourceSessionId`, `sourceNodeId`, and `graftedAt`. The plain `nodes` array remains available for callers that only need the topic nodes.
354
-
355
- This method is read-only. It does not include raw `mg_message_buffer` content and does not add rendering, layout, or color decisions. Like `getActiveNodes()` and `getActiveSegments()`, it waits for the agent's pending ingest work before reading. If called immediately after `invoke()` in queue mode, it waits for the current ingest job to settle before returning.
417
+ - `sessionId`: current agent session ID.
418
+ - `nodes`: active topic nodes for the session.
419
+ - `snapshotNodes`: active topic nodes wrapped with provenance metadata when a node came from a graft.
420
+ - `edges`: topic edges where either endpoint belongs to a session topic node.
421
+ - `memories`: all memory nodes for the session, including decayed or superseded rows.
422
+ - `memoryEdges`: memory-level edges such as `semantic`, `conflicts`, `updates`, and `related`.
423
+ - `capturedAt`: ISO timestamp for when the snapshot was produced.
424
+
425
+ Each `snapshotNodes` entry contains the topic `node` and an optional `graftOrigin` with `sourceSessionId`, `sourceNodeId`, and `graftedAt`. The plain `nodes` array remains available for callers that only need the topic nodes.
426
+
427
+ This method is read-only. It does not include raw `mg_message_buffer` content and does not add rendering, layout, or color decisions. Like `getActiveNodes()` and `getActiveSegments()`, it waits for the agent's pending ingest work before reading. If called immediately after `invoke()` in queue mode, it waits for the current ingest job to settle before returning.
428
+
429
+ Graph snapshots intentionally include stale and maintenance metadata so visualizers can show memory lifecycle state. For example, a UI can fade `decayed` memories, show `hasConflict` badges, draw `conflicts` edges between contradictory facts, and draw `updates` edges from the current fact to the older fact it replaced.
356
430
 
357
431
  Read active topic nodes:
358
432
 
@@ -371,6 +445,15 @@ for (const node of nodes) {
371
445
  }
372
446
  ```
373
447
 
448
+ Filter active topic nodes by tag:
449
+
450
+ ```ts
451
+ const planningNodes = await agent.getActiveNodes({
452
+ tags: ["planning"],
453
+ tagMode: "all",
454
+ });
455
+ ```
456
+
374
457
  Read active segments:
375
458
 
376
459
  ```ts
@@ -417,6 +500,117 @@ const graft = await agent.graft([nodes[0]!.id]);
417
500
  - `nodes`: selected topic nodes.
418
501
  - `tokenCount`: estimated token count.
419
502
 
503
+ Graft prompts include topic summaries because they preserve useful conversation context. Topic summaries are historical: if an older topic summary says "the user lives in Delhi" and a later memory says "the user lives in Bangalore", MemoGrafter does not rewrite the old summary. Instead, when crawler maintenance has marked a contradiction or supersession, graft prompt assembly adds deterministic maintenance notes and active memory facts:
504
+
505
+ ```text
506
+ Memory maintenance notes:
507
+ - The fact "user location: Delhi" was superseded by "Bangalore".
508
+ - Prefer active memory facts over contradictory historical summary details.
509
+ Active memory facts:
510
+ - user location: Bangalore
511
+ ```
512
+
513
+ This keeps history intact while telling the downstream model which fact is current.
514
+
515
+ ## Maintaining Memory With The Crawler
516
+
517
+ `MemoGrafterCrawler` is an optional graph maintenance worker. It can run once on demand or on a simple in-process interval. It does not use Redis, BullMQ, OpenAI, embeddings, or LLMs for the built-in conflict/versioning/decay passes.
518
+
519
+ Typical usage with the real store:
520
+
521
+ ```ts
522
+ import {
523
+ ConflictDetectionPass,
524
+ DecayScoringPass,
525
+ MemoGrafter,
526
+ MemoGrafterCrawler,
527
+ VersioningPass,
528
+ } from "memo-grafter";
529
+
530
+ const memo = new MemoGrafter(config);
531
+ await memo.initialize();
532
+
533
+ const crawler = new MemoGrafterCrawler({
534
+ store: memo.store,
535
+ intervalMs: 60_000,
536
+ passes: [
537
+ new ConflictDetectionPass(),
538
+ new VersioningPass(),
539
+ new DecayScoringPass({
540
+ halfLifeDays: 90,
541
+ minScore: 0.25,
542
+ }),
543
+ ],
544
+ });
545
+
546
+ const report = await crawler.runOnce();
547
+ console.log(report);
548
+ ```
549
+
550
+ `runOnce()` executes the configured passes exactly one time and returns a `CrawlerReport`. `intervalMs` does not affect `runOnce()`.
551
+
552
+ To run the crawler in-process on a schedule:
553
+
554
+ ```ts
555
+ crawler.start();
556
+
557
+ // Later, during shutdown:
558
+ crawler.stop();
559
+ await memo.close();
560
+ ```
561
+
562
+ `start()` is safe to call more than once; it does not create duplicate intervals. If a scheduled tick fires while the previous run is still executing, that tick is skipped.
563
+
564
+ The built-in conflict/versioning passes use deterministic matching:
565
+
566
+ - they group active memories by session, normalized `subject`, and normalized `predicate`;
567
+ - a group conflicts when it has different normalized `value` strings;
568
+ - decayed memories are skipped;
569
+ - already superseded memories are skipped;
570
+ - broad topic memories with generic subject/predicate pairs are skipped unless they match a recognized competing trip-plan pattern;
571
+ - the newest conflicting fact wins by `createdAt`;
572
+ - if timestamps tie, deterministic ID ordering is used.
573
+
574
+ Conflict detection is meant for mutually exclusive fact slots such as `user location Delhi` versus `user location Bangalore`. For generic "things discussed" memories, MemoGrafter only recognizes a narrow travel destination plan pattern by default. That means `Goa trip plan` and `Vietnam trip plan` can conflict, while `how to cook rajma chawal`, `food in Vietnam`, and `places to visit Vietnam` do not all conflict just because extraction used a generic subject and predicate.
575
+
576
+ `DecayScoringPass` uses confidence-weighted exponential recency decay:
577
+
578
+ ```text
579
+ recency_factor = exp(-(ln(2) / half_life_days) * age_days)
580
+ decay_score = confidence * recency_factor
581
+ ```
582
+
583
+ If `decay_score < minScore`, the memory is marked `decayed: true`. Superseded memories and already decayed memories are skipped. Conservative defaults are used when options are omitted:
584
+
585
+ ```ts
586
+ new DecayScoringPass({
587
+ halfLifeDays: 90,
588
+ minScore: 0.25,
589
+ });
590
+ ```
591
+
592
+ By default the pass does not change stored `confidence`; confidence is treated as extraction confidence, while decay score is temporal freshness. If you explicitly want the score written back as confidence, pass `updateConfidence: true`.
593
+
594
+ When conflicts are found:
595
+
596
+ - both active facts get `hasConflict: true`;
597
+ - a `conflicts` memory edge is created;
598
+ - the older fact gets `supersededBy` pointing to the newer fact;
599
+ - an `updates` edge is created as `newer_memory --updates--> older_memory`.
600
+ - stale active memories can be marked `decayed: true` by the decay pass.
601
+
602
+ Crawler maintenance is non-destructive. It annotates existing memory rows and creates memory edges. It does not delete nodes, does not rebuild topics, and does not rewrite topic summaries.
603
+
604
+ Existing incorrect conflict edges are not automatically deleted. If an older crawler run created a false-positive edge, handle cleanup with a future explicit pruning pass or filter displayed memory edges to active memories in your app.
605
+
606
+ Do not put crawler behavior inside `clearSession()`. `clearSession()` is a destructive reset. The crawler is a non-destructive maintenance worker. If you intentionally rebuild a session graph, use this order:
607
+
608
+ ```ts
609
+ await agent.clearSession();
610
+ await memo.ingestNow(messages, sessionId);
611
+ await crawler.runOnce();
612
+ ```
613
+
420
614
  ## Absorbing Memory Into Another Chatbot
421
615
 
422
616
  Use `absorbFromAgent()` to copy selected memory from one chatbot into another.
@@ -447,37 +641,37 @@ const response = await writingBot.invoke(
447
641
  console.log(response);
448
642
  ```
449
643
 
450
- Absorbing copies selected topic nodes into the target session and creates `grafted` edges back to their source nodes. It also copies active memory facts attached to those topic nodes into the target session so future `recall()` and `invoke()` calls can surface the transferred context. Decayed or superseded source memories are not copied.
451
-
452
- Each copied topic node is recorded in the graft registry. Registry entries let you answer where a graft came from and which copied destination node owns it.
453
-
454
- Known limitation: if a source topic node has no associated memory rows in `mg_memory_nodes`, there are no facts to copy. The topic node can still be grafted, but targeted recall will not return facts for that node unless memory extraction produced them.
455
-
456
- ### Inspect Graft Registry
457
-
458
- ```ts
459
- const registry = await writingBot.getGraftRegistry();
460
-
461
- for (const entry of registry) {
462
- console.log({
463
- nodeId: entry.nodeId,
464
- sourceSessionId: entry.sourceSessionId,
465
- sourceNodeId: entry.sourceNodeId,
466
- graftedAt: entry.graftedAt,
467
- });
468
- }
469
- ```
470
-
471
- Use this when your app needs to display transferred memory provenance or decide which graft to remove.
472
-
473
- ### Remove A Graft
474
-
475
- ```ts
476
- const registry = await writingBot.getGraftRegistry();
477
- await writingBot.removeGraft(registry[0]!.nodeId);
478
- ```
479
-
480
- `removeGraft()` removes the copied graft node from the current session. The registry row is removed with it. The method is scoped to the current agent session and throws if the node is not a registered graft for that session.
644
+ Absorbing copies selected topic nodes into the target session and creates `grafted` edges back to their source nodes. It also copies active memory facts attached to those topic nodes into the target session so future `recall()` and `invoke()` calls can surface the transferred context. Decayed or superseded source memories are not copied.
645
+
646
+ Each copied topic node is recorded in the graft registry. Registry entries let you answer where a graft came from and which copied destination node owns it.
647
+
648
+ Known limitation: if a source topic node has no associated memory rows in `mg_memory_nodes`, there are no facts to copy. The topic node can still be grafted, but targeted recall will not return facts for that node unless memory extraction produced them.
649
+
650
+ ### Inspect Graft Registry
651
+
652
+ ```ts
653
+ const registry = await writingBot.getGraftRegistry();
654
+
655
+ for (const entry of registry) {
656
+ console.log({
657
+ nodeId: entry.nodeId,
658
+ sourceSessionId: entry.sourceSessionId,
659
+ sourceNodeId: entry.sourceNodeId,
660
+ graftedAt: entry.graftedAt,
661
+ });
662
+ }
663
+ ```
664
+
665
+ Use this when your app needs to display transferred memory provenance or decide which graft to remove.
666
+
667
+ ### Remove A Graft
668
+
669
+ ```ts
670
+ const registry = await writingBot.getGraftRegistry();
671
+ await writingBot.removeGraft(registry[0]!.nodeId);
672
+ ```
673
+
674
+ `removeGraft()` removes the copied graft node from the current session. The registry row is removed with it. The method is scoped to the current agent session and throws if the node is not a registered graft for that session.
481
675
 
482
676
  ### Absorb By Semantic Prompt
483
677
 
@@ -527,6 +721,9 @@ const agent = new MemoGrafterAgent({
527
721
  mode: "intent",
528
722
  windowSize: 5,
529
723
  driftSensitivity: "medium",
724
+ adaptiveSensitivity: {
725
+ enabled: false,
726
+ },
530
727
  minSegmentMessages: 3,
531
728
  llmAmbiguityDetection: false,
532
729
  reentryDetection: true,
@@ -575,13 +772,15 @@ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
575
772
 
576
773
  Useful store inspection methods include:
577
774
 
578
- - `getNodesBySession(sessionId)`: read topic nodes for a session.
775
+ - `getNodesBySession(sessionId, options?)`: read topic nodes for a session, optionally filtered by tags.
579
776
  - `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
580
777
  - `getSegmentsBySession(sessionId)`: read topic segments for a session.
581
778
  - `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
582
- - `getEdgesBySession(sessionId)`: read all topic edges where either endpoint belongs to the session's topic nodes.
583
- - `getMemoriesBySession(sessionId)`: read all memory nodes for a session, including decayed and superseded rows.
584
- - `getGraftRegistry(sessionId)`: read graft provenance entries for a session.
779
+ - `getEdgesBySession(sessionId)`: read all topic edges where either endpoint belongs to the session's topic nodes.
780
+ - `getMemoriesBySession(sessionId)`: read all memory nodes for a session, including decayed and superseded rows.
781
+ - `getMemoryEdgesBySession(sessionId)`: read memory-level edges such as `conflicts` and `updates`.
782
+ - `getGraftRegistry(sessionId)`: read graft provenance entries for a session.
783
+ - `setSessionTags(sessionId, tags)`: replace the normalized tag set on existing topic and memory rows for a session.
585
784
 
586
785
  ### `llm`
587
786
 
@@ -628,6 +827,9 @@ drift: {
628
827
  mode: "intent",
629
828
  windowSize: 5,
630
829
  driftSensitivity: "medium",
830
+ adaptiveSensitivity: {
831
+ enabled: false,
832
+ },
631
833
  minSegmentMessages: 3,
632
834
  llmAmbiguityDetection: false,
633
835
  reentryDetection: true,
@@ -640,6 +842,7 @@ Controls topic boundary detection.
640
842
  - `mode`: `"intent"` or `"window"`.
641
843
  - `windowSize`: message window size for window mode.
642
844
  - `driftSensitivity`: preferred sensitivity preset, one of `"low"`, `"medium"`, or `"high"`.
845
+ - `adaptiveSensitivity`: optional session-history based threshold tuning. Disabled by default.
643
846
  - `threshold`: deprecated numeric threshold. It still works when `driftSensitivity` is not set, but MemoGrafter logs a one-time warning.
644
847
  - `minSegmentMessages`: minimum messages before a boundary.
645
848
  - `llmAmbiguityDetection`: optional LLM check for borderline topic shifts. Defaults to `false`.
@@ -658,6 +861,30 @@ Use `"medium"` first. Boundaries are cut when a drift score exceeds the resolved
658
861
 
659
862
  If both `driftSensitivity` and `threshold` are provided, `driftSensitivity` wins.
660
863
 
864
+ #### Adaptive Sensitivity
865
+
866
+ Adaptive sensitivity is opt-in and keeps the configured `driftSensitivity` as its baseline. When enabled, MemoGrafter looks at recent saved segments for the session and nudges the resolved threshold by a small bounded step:
867
+
868
+ ```ts
869
+ drift: {
870
+ mode: "intent",
871
+ driftSensitivity: "medium",
872
+ adaptiveSensitivity: {
873
+ enabled: true,
874
+ minSegments: 4,
875
+ lookbackSegments: 8,
876
+ targetSegmentMessages: {
877
+ min: 3,
878
+ max: 8,
879
+ },
880
+ adjustmentStep: 0.05,
881
+ maxAdjustment: 0.1,
882
+ },
883
+ }
884
+ ```
885
+
886
+ If recent segments are consistently short, MemoGrafter raises the threshold slightly to reduce fragmentation. If recent segments are consistently long, it lowers the threshold slightly to split more readily. It does not adapt until enough segment history exists, and it skips adjustment when recent segment lengths are too erratic.
887
+
661
888
  #### Reentry Detection
662
889
 
663
890
  Reentry detection handles conversations that leave a topic and later return to it:
@@ -724,6 +951,20 @@ Enables an opt-in Redis cache for targeted recall. MemoGrafter creates one share
724
951
 
725
952
  Recall cache keys include the session ID, `limit`, `minSimilarity`, and a deterministic hash of the query embedding. Redis failures are logged as warnings and recall falls back to PostgreSQL search. The cache is disabled unless this section is present.
726
953
 
954
+ When tag-aware recall is used, cache keys also include recall `scope`, `tagMode`, and the normalized tag list. This prevents untagged, session-filtered, and cross-session tagged recall from sharing cached search results.
955
+
956
+ ## Manual Smoke Tests
957
+
958
+ From this repository, run the session-tagging smoke with a real PostgreSQL database:
959
+
960
+ ```powershell
961
+ npx tsx --env-file=.env ./tests/manual/graft/session-tags-smoke.ts
962
+ ```
963
+
964
+ Use the forward-slash path in PowerShell. An unquoted backslash path can be collapsed before `tsx` receives it.
965
+
966
+ The smoke creates two tagged sessions, writes one memory into each, verifies current-session tag filtering with `getActiveNodes()`, and verifies cross-session project recall with `recall(..., { scope: "tagged" })`. If you run it repeatedly against the same database, tagged recall can return rows from previous smoke runs because those historical sessions are still present.
967
+
727
968
  ## Queue Mode
728
969
 
729
970
  Without queue config, ingestion runs synchronously after `invoke()`.
@@ -856,6 +1097,10 @@ const retriever = new RetrieverPipeline(store, embedder, {
856
1097
  limit: 8,
857
1098
  minSimilarity: 0.55,
858
1099
  tokenBudget: 1000,
1100
+ scoring: {
1101
+ similarityWeight: 0.7,
1102
+ confidenceWeight: 0.3,
1103
+ },
859
1104
  });
860
1105
 
861
1106
  const result = await retriever.run(
@@ -1112,6 +1357,10 @@ Main exports:
1112
1357
  - `OpenAILLMAdapter`
1113
1358
  - `OpenAIEmbedAdapter`
1114
1359
  - `PostgresGraphStore`
1360
+ - `MemoGrafterCrawler`
1361
+ - `ConflictDetectionPass`
1362
+ - `DecayScoringPass`
1363
+ - `VersioningPass`
1115
1364
  - `GrafterPipeline`
1116
1365
  - `IngestPipeline`
1117
1366
  - `RetrieverPipeline`
@@ -1119,16 +1368,19 @@ Main exports:
1119
1368
  - `FleetAgentRecord`
1120
1369
  - `RetrievalResult`
1121
1370
  - `RetrieverConfig`
1371
+ - `TagFilterOptions`
1372
+ - `IngestOptions`
1122
1373
  - public shared and fleet types
1123
1374
 
1124
1375
  Useful `GraphStore` inspection methods:
1125
1376
 
1126
1377
  - `getTopicNode(topicNodeId, sessionId?)`
1127
- - `getNodesBySession(sessionId)`
1378
+ - `getNodesBySession(sessionId, options?)`
1128
1379
  - `getSegmentsBySession(sessionId)`
1129
1380
  - `getEdgesByType(sessionId, type)`
1130
1381
  - `getEdgesBySession(sessionId)`
1131
1382
  - `getMemoriesBySession(sessionId)`
1383
+ - `getMemoryEdgesBySession(sessionId)`
1132
1384
  - `getSessionNodeCount(sessionId)`
1133
1385
  - `getSessionIngestState(sessionId)`
1134
1386
 
@@ -1138,14 +1390,16 @@ Common `MemoGrafterAgent` methods:
1138
1390
  - `invoke(message)`: send a user message and receive an assistant response.
1139
1391
  - `getHistory()`: read local chat history.
1140
1392
  - `getSessionId()`: read the current session ID.
1141
- - `getGraphSnapshot()`: read nodes, edges, memories, session ID, and capture timestamp for visualization or inspection.
1142
- - `getGraftRegistry()`: inspect provenance for grafted nodes in the current session.
1143
- - `getActiveNodes()`: inspect topic nodes.
1144
- - `getActiveSegments()`: inspect topic segments.
1145
- - `clearSession()`: explicitly clear local history and stored session memory.
1146
- - `recall(query, options?)`: retrieve structured memory by semantic query.
1147
- - `graft(topicIds?)`: preview memory injection.
1148
- - `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
1149
- - `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
1150
- - `removeGraft(nodeId)`: remove a registered graft node from the current session.
1151
- - `close()`: close database and queue resources.
1393
+ - `getGraphSnapshot()`: read nodes, edges, memories, session ID, and capture timestamp for visualization or inspection.
1394
+ - `getGraftRegistry()`: inspect provenance for grafted nodes in the current session.
1395
+ - `getActiveNodes(options?)`: inspect topic nodes, optionally filtered by tags.
1396
+ - `getActiveSegments()`: inspect topic segments.
1397
+ - `setSessionTags(tags)`: replace tags on the current session and apply them to future ingested memories.
1398
+ - `getSessionTags()`: read the current agent's normalized session tags.
1399
+ - `clearSession()`: explicitly clear local history and stored session memory.
1400
+ - `recall(query, options?)`: retrieve structured memory by semantic query, optionally filtered by tags.
1401
+ - `graft(topicIds?)`: preview memory injection.
1402
+ - `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
1403
+ - `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
1404
+ - `removeGraft(nodeId)`: remove a registered graft node from the current session.
1405
+ - `close()`: close database and queue resources.
@@ -2,7 +2,7 @@ import { Redis } from "ioredis";
2
2
  import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
3
3
  import type { MemoGrafterFleetOptions } from "./fleet/types.js";
4
4
  import type { GraphStore } from "./store/index.js";
5
- import type { AbsorbFromAgentOptions, EmbedAdapter, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
5
+ import type { AbsorbFromAgentOptions, EmbedAdapter, IngestOptions, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
6
6
  export declare class MemoGrafter {
7
7
  readonly llm: LLMAdapter;
8
8
  readonly embedder: EmbedAdapter;
@@ -13,10 +13,13 @@ export declare class MemoGrafter {
13
13
  private readonly ingestQueue;
14
14
  constructor(config: MemoGrafterConfig);
15
15
  initialize(): Promise<void>;
16
- ingest(messages: Message[], sessionId: string): Promise<TopicNode[]>;
17
- ingestNow(messages: Message[], sessionId: string): Promise<TopicNode[]>;
18
- enqueueIngest(messages: Message[], sessionId: string): Promise<void>;
19
- getTopics(sessionId: string): Promise<{
16
+ ingest(messages: Message[], sessionId: string, options?: IngestOptions): Promise<TopicNode[]>;
17
+ ingestNow(messages: Message[], sessionId: string, options?: IngestOptions): Promise<TopicNode[]>;
18
+ enqueueIngest(messages: Message[], sessionId: string, options?: IngestOptions): Promise<void>;
19
+ getTopics(sessionId: string, options?: {
20
+ tags?: string[];
21
+ tagMode?: "all" | "any";
22
+ }): Promise<{
20
23
  nodes: TopicNode[];
21
24
  segments: TopicSegment[];
22
25
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"MemoGrafter.d.ts","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,WAAW;IACtB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAErC,MAAM,EAAE,iBAAiB;IAmDrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQpE,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIjE,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAM7F,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAIjE,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMrF,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAmBpG,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMpF,WAAW,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;IAI9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,uBAAuB;CAUhC"}
1
+ {"version":3,"file":"MemoGrafter.d.ts","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAKhC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,WAAW;IACtB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAErC,MAAM,EAAE,iBAAiB;IAqDrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQjG,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAI9F,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IASjG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,CAAA;KAAO,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IAMzJ,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAIjE,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMrF,oBAAoB,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAmBpG,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMpF,WAAW,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB;IAI9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,OAAO,CAAC,uBAAuB;CAUhC"}
@@ -22,6 +22,7 @@ export class MemoGrafter {
22
22
  const llmAmbiguityDetection = config.drift?.llmAmbiguityDetection;
23
23
  const reentryDetection = config.drift?.reentryDetection;
24
24
  const reentryThreshold = config.drift?.reentryThreshold;
25
+ const adaptiveSensitivity = config.drift?.adaptiveSensitivity;
25
26
  const topK = config.graph?.topK ?? 5;
26
27
  const hopDepth = config.graph?.hopDepth ?? 1;
27
28
  const bufferSize = config.inject?.bufferSize ?? 1;
@@ -51,6 +52,7 @@ export class MemoGrafter {
51
52
  ...(llmAmbiguityDetection !== undefined ? { llmAmbiguityDetection } : {}),
52
53
  ...(reentryDetection !== undefined ? { reentryDetection } : {}),
53
54
  ...(reentryThreshold !== undefined ? { reentryThreshold } : {}),
55
+ ...(adaptiveSensitivity !== undefined ? { adaptiveSensitivity } : {}),
54
56
  });
55
57
  this.grafterPipeline = new GrafterPipeline(this.store, {
56
58
  hopDepth,
@@ -62,24 +64,24 @@ export class MemoGrafter {
62
64
  initialize() {
63
65
  return this.store.initialize();
64
66
  }
65
- ingest(messages, sessionId) {
67
+ ingest(messages, sessionId, options = {}) {
66
68
  if (this.ingestQueue) {
67
- return this.enqueueIngest(messages, sessionId).then(() => []);
69
+ return this.enqueueIngest(messages, sessionId, options).then(() => []);
68
70
  }
69
- return this.ingestPipeline.run(messages, sessionId);
71
+ return this.ingestPipeline.run(messages, sessionId, options);
70
72
  }
71
- ingestNow(messages, sessionId) {
72
- return this.ingestPipeline.run(messages, sessionId);
73
+ ingestNow(messages, sessionId, options = {}) {
74
+ return this.ingestPipeline.run(messages, sessionId, options);
73
75
  }
74
- async enqueueIngest(messages, sessionId) {
76
+ async enqueueIngest(messages, sessionId, options = {}) {
75
77
  if (this.ingestQueue) {
76
- await this.ingestQueue.enqueue(messages, sessionId);
78
+ await this.ingestQueue.enqueue(messages, sessionId, options);
77
79
  return;
78
80
  }
79
- await this.ingestPipeline.run(messages, sessionId);
81
+ await this.ingestPipeline.run(messages, sessionId, options);
80
82
  }
81
- async getTopics(sessionId) {
82
- const nodes = await this.store.getNodesBySession(sessionId);
83
+ async getTopics(sessionId, options = {}) {
84
+ const nodes = await this.store.getNodesBySession(sessionId, options);
83
85
  const segments = await this.store.getSegmentsBySession(sessionId);
84
86
  return { nodes, segments };
85
87
  }