memo-grafter 0.2.2 → 0.2.3

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/README.md +81 -73
  2. package/USER_GUIDE.md +165 -64
  3. package/dist/MemoGrafterAgent.d.ts +7 -3
  4. package/dist/MemoGrafterAgent.d.ts.map +1 -1
  5. package/dist/MemoGrafterAgent.js +62 -23
  6. package/dist/MemoGrafterAgent.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/pipeline/IngestPipeline.d.ts +2 -0
  10. package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
  11. package/dist/pipeline/IngestPipeline.js +93 -17
  12. package/dist/pipeline/IngestPipeline.js.map +1 -1
  13. package/dist/store/GraphStore.d.ts +11 -1
  14. package/dist/store/GraphStore.d.ts.map +1 -1
  15. package/dist/store/postgres-pgvector/GraphStore.d.ts +15 -1
  16. package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -1
  17. package/dist/store/postgres-pgvector/GraphStore.js +229 -0
  18. package/dist/store/postgres-pgvector/GraphStore.js.map +1 -1
  19. package/dist/types.d.ts +27 -0
  20. package/dist/types.d.ts.map +1 -1
  21. package/dist/utils/drift/cosineSimilarity.test.d.ts +2 -0
  22. package/dist/utils/drift/cosineSimilarity.test.d.ts.map +1 -0
  23. package/dist/utils/drift/cosineSimilarity.test.js +25 -0
  24. package/dist/utils/drift/cosineSimilarity.test.js.map +1 -0
  25. package/dist/utils/drift/vectorAvg.test.d.ts +2 -0
  26. package/dist/utils/drift/vectorAvg.test.d.ts.map +1 -0
  27. package/dist/utils/drift/vectorAvg.test.js +36 -0
  28. package/dist/utils/drift/vectorAvg.test.js.map +1 -0
  29. package/dist/utils/text/normalizeText.test.d.ts +2 -0
  30. package/dist/utils/text/normalizeText.test.d.ts.map +1 -0
  31. package/dist/utils/text/normalizeText.test.js +23 -0
  32. package/dist/utils/text/normalizeText.test.js.map +1 -0
  33. package/dist/utils/vector/vectorLiteral.test.d.ts +2 -0
  34. package/dist/utils/vector/vectorLiteral.test.d.ts.map +1 -0
  35. package/dist/utils/vector/vectorLiteral.test.js +28 -0
  36. package/dist/utils/vector/vectorLiteral.test.js.map +1 -0
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -1,73 +1,81 @@
1
- # MemoGrafter
2
- [![npm version](https://img.shields.io/npm/v/memo-grafter.svg)](https://www.npmjs.com/package/memo-grafter)
3
-
4
- Structured memory for TypeScript chatbots.
5
-
6
- MemoGrafter helps chatbot applications remember conversations without stuffing every old message back into the prompt. It turns conversation history into topic-based memory, recalls relevant details later, and can copy useful memory from one chatbot or session into another.
7
-
8
- It is a memory framework, not an autonomous agent runtime. It does not run tools, schedule work, or decide goals for an agent.
9
-
10
- ## What It Is For
11
-
12
- - Chatbots that need long-running memory.
13
- - Assistants that should recall user preferences, prior context, and open questions.
14
- - Multi-chatbot or multi-session flows where selected memory can be grafted into another conversation.
15
- - TypeScript apps that need reusable memory, retrieval, and graph-backed conversation primitives.
16
-
17
- ## How It Works
18
-
19
- ```text
20
- chat messages
21
- -> topic-based memory
22
- -> graph links
23
- -> relevant recall
24
- -> optional memory grafting
25
- ```
26
-
27
- MemoGrafter stores conversation turns, detects topic changes, summarizes useful context, links related memories, and retrieves or grafts memory when needed.
28
-
29
- ## Install
30
-
31
- ```bash
32
- npm install memo-grafter
33
- ```
34
-
35
- MemoGrafter runs server-side on Node.js. The built-in storage backend uses PostgreSQL with `pgvector`.
36
-
37
- ## Minimal Example
38
-
39
- ```ts
40
- import "dotenv/config";
41
-
42
- import {
43
- MemoGrafterAgent,
44
- OpenAIEmbedAdapter,
45
- OpenAILLMAdapter,
46
- } from "memo-grafter";
47
-
48
- const agent = new MemoGrafterAgent({
49
- db: { connectionString: process.env.DATABASE_URL! },
50
- llm: new OpenAILLMAdapter("gpt-4o"),
51
- embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
52
- });
53
-
54
- await agent.initialize();
55
-
56
- await agent.invoke("I am planning a Japan trip.");
57
- await agent.invoke("I like quiet towns, bookstores, and local cafes.");
58
-
59
- const recall = await agent.recall("travel preferences");
60
- console.log(recall.facts);
61
-
62
- await agent.close();
63
- ```
64
-
65
- ## Learn More
66
-
67
- - [USER_GUIDE.md](https://github.com/mayhemking007/memo-grafter/blob/main/USER_GUIDE.md) covers setup, configuration, adapters, queue mode, fleet APIs, examples, and troubleshooting.
68
- - [ARCHITECTURE.md](https://github.com/mayhemking007/memo-grafter/blob/main/ARCHITECTURE.md) explains the current high-level implementation.
69
- - `examples` contains runnable demo.
70
-
71
- ## License
72
-
73
- MIT
1
+ # MemoGrafter
2
+ [![npm version](https://img.shields.io/npm/v/memo-grafter.svg)](https://www.npmjs.com/package/memo-grafter)
3
+
4
+ Structured memory for TypeScript chatbots.
5
+
6
+ MemoGrafter helps chatbot applications remember conversations without stuffing every old message back into the prompt. It turns conversation history into topic-based memory, recalls relevant details later, and can copy useful memory from one chatbot or session into another.
7
+
8
+ It is a memory framework, not an autonomous agent runtime. It does not run tools, schedule work, or decide goals for an agent.
9
+
10
+ MemoGrafter builds the memory graph incrementally. New chatbot turns append topic and memory nodes to the existing graph instead of clearing and rebuilding the session on every response, so grafted and externally enriched memory can survive later conversation turns. Use `clearSession()` explicitly when you want to reset an agent's local history and stored session memory.
11
+
12
+ ## Playground
13
+
14
+ - Try the [MemoGrafter Playground](https://mgplayground-green.vercel.app/).
15
+ - View the playground demo repo at [mayhemking007/mg-demo](https://github.com/mayhemking007/mg-demo).
16
+
17
+ ## What It Is For
18
+
19
+ - Chatbots that need long-running memory.
20
+ - Assistants that should recall user preferences, prior context, and open questions.
21
+ - Multi-chatbot or multi-session flows where selected memory can be grafted into another conversation.
22
+ - TypeScript apps that need reusable memory, retrieval, and graph-backed conversation primitives.
23
+
24
+ ## How It Works
25
+
26
+ ```text
27
+ chat messages
28
+ -> topic-based memory
29
+ -> graph links
30
+ -> relevant recall
31
+ -> optional memory grafting
32
+ ```
33
+
34
+ MemoGrafter stores conversation turns, tracks which messages have already been ingested, detects topic changes for new turns with recent context, summarizes useful context, links related memories, and retrieves or grafts memory when needed.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ npm install memo-grafter
40
+ ```
41
+
42
+ MemoGrafter runs server-side on Node.js. The built-in storage backend uses PostgreSQL with `pgvector`.
43
+
44
+ ## Minimal Example
45
+
46
+ ```ts
47
+ import "dotenv/config";
48
+
49
+ import {
50
+ MemoGrafterAgent,
51
+ OpenAIEmbedAdapter,
52
+ OpenAILLMAdapter,
53
+ } from "memo-grafter";
54
+
55
+ const agent = new MemoGrafterAgent({
56
+ db: { connectionString: process.env.DATABASE_URL! },
57
+ llm: new OpenAILLMAdapter("gpt-4o"),
58
+ embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
59
+ });
60
+
61
+ await agent.initialize();
62
+
63
+ await agent.invoke("I am planning a Japan trip.");
64
+ await agent.invoke("I like quiet towns, bookstores, and local cafes.");
65
+
66
+ const recall = await agent.recall("travel preferences");
67
+ console.log(recall.facts);
68
+
69
+ await agent.close();
70
+ ```
71
+
72
+ ## Learn More
73
+
74
+ - [USER_GUIDE.md](https://github.com/mayhemking007/memo-grafter/blob/main/USER_GUIDE.md) covers setup, configuration, adapters, queue mode, fleet APIs, examples, and troubleshooting.
75
+ - [ARCHITECTURE.md](https://github.com/mayhemking007/memo-grafter/blob/main/ARCHITECTURE.md) explains the current high-level implementation.
76
+ - `examples/basic-chat-memory` is the simplest runnable single-agent memory demo.
77
+ - `examples/chatbot-memory-demo` shows the larger two-agent grafting workflow.
78
+
79
+ ## License
80
+
81
+ MIT
package/USER_GUIDE.md CHANGED
@@ -8,6 +8,8 @@ The project is intentionally focused. MemoGrafter is a chatbot memory framework,
8
8
 
9
9
  The most important idea is memory grafting. A chatbot can build useful memory during one conversation, and another chatbot can absorb only the relevant parts.
10
10
 
11
+ MemoGrafter ingests conversation memory incrementally. New turns append topic nodes, memory nodes, and graph edges to the existing session graph instead of clearing and rebuilding the graph on every response. This keeps grafted memory and future external graph enrichment durable across normal chatbot turns.
12
+
11
13
  ## Requirements
12
14
 
13
15
  - Node.js 18 or newer.
@@ -82,9 +84,11 @@ Current v1 tables:
82
84
  - `mg_topic_nodes`
83
85
  - `mg_topic_edges`
84
86
  - `mg_memory_nodes`
85
- - `mg_memory_edges`
86
- - `mg_fleets`
87
- - `mg_fleet_agents`
87
+ - `mg_memory_edges`
88
+ - `mg_fleets`
89
+ - `mg_fleet_agents`
90
+ - `mg_session_ingest_state`
91
+ - `mg_graft_registry`
88
92
 
89
93
  ## Quick Start
90
94
 
@@ -140,7 +144,9 @@ export interface Message {
140
144
  }
141
145
  ```
142
146
 
143
- `MemoGrafterAgent` keeps an in-memory user/assistant history for the current session and stores messages in PostgreSQL during ingestion. System messages can be added to LLM calls internally when memory recall is pinned into an overflowing context window.
147
+ `MemoGrafterAgent` keeps an in-memory user/assistant history for the current session and stores messages in PostgreSQL during ingestion. When a session already has memory graph content, `invoke()` can add a system message with recalled facts before the LLM call.
148
+
149
+ Ingestion tracks the last message index that has been processed for each session. Re-running ingestion with the same history is a no-op for graph creation, while later turns append new graph state.
144
150
 
145
151
  ### Segments
146
152
 
@@ -208,13 +214,17 @@ Edges are stored in `mg_topic_edges`.
208
214
 
209
215
  ### Grafting
210
216
 
211
- Grafting is the process of selecting memory nodes and turning them into useful context for another prompt or another chatbot.
217
+ Grafting is the process of selecting topic nodes and turning them into useful context for another prompt or another chatbot.
212
218
 
213
219
  There are two common forms:
214
220
 
215
221
  - Preview memory with `graft()`.
216
222
  - Copy memory into another chatbot with `absorbFromAgent()` or `ingestGraftedNodes()`.
217
223
 
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.
227
+
218
228
  ## Using MemoGrafterAgent
219
229
 
220
230
  `MemoGrafterAgent` is the easiest API to start with.
@@ -251,17 +261,31 @@ await agent.close();
251
261
 
252
262
  On every call, `invoke()`:
253
263
 
254
- 1. Adds the user message to local history.
255
- 2. Builds the message list for the LLM.
256
- 3. If history is under the configured budget, sends the raw local history.
257
- 4. If history crosses the overflow threshold, calls targeted recall using recent conversation context, prepends the recall result as one pinned system message, and keeps only the recent raw message window.
258
- 5. Adds the assistant response to history.
259
- 6. Queues ingestion of the updated conversation into the memory graph.
264
+ 1. Checks whether the current session has topic nodes in the memory graph.
265
+ 2. If graph content exists, calls targeted recall using the current user message.
266
+ 3. Prepends the recalled memory prompt as a system message when recall returns matching facts.
267
+ 4. Builds the LLM message list from the optional memory system message, the recent raw history window, and the current user message.
268
+ 5. Adds the user message and assistant response to local history after the LLM response.
269
+ 6. Queues ingestion of only the newly added conversation turns into the memory graph.
270
+
271
+ Recall is skipped entirely when the session has no topic nodes yet. This avoids an unnecessary embed and vector search on the first turn or while async ingestion has not produced graph content.
260
272
 
261
- The overflow threshold is 80% of `inject.tokenBudget`. Recall failures are logged as warnings and fall back to the recent raw message window, so a retrieval or embedder problem should not crash the foreground chatbot turn.
273
+ Recall failures are logged as warnings and fall back to raw history only, so a retrieval or embedder problem should not crash the foreground chatbot turn.
262
274
 
263
275
  On the first turn there may be no memory to recall. Later turns can use memory created from earlier turns once ingestion has completed.
264
276
 
277
+ Normal ingestion does not call `clearSession()`. Existing topic nodes, grafted nodes, memory rows, and graph edges are preserved unless you explicitly clear the session.
278
+
279
+ ### Clearing A Session
280
+
281
+ Use `clearSession()` when you intentionally want to reset an agent:
282
+
283
+ ```ts
284
+ await agent.clearSession();
285
+ ```
286
+
287
+ 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
+
265
289
  ### Targeted Recall
266
290
 
267
291
  Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
@@ -298,38 +322,42 @@ Options:
298
322
 
299
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.
300
324
 
301
- `MemoGrafterAgent.invoke()` also calls `recall()` internally when local history overflows the configured history budget. In that automatic path, the returned `systemPrompt` is pinned as a single system message before the recent raw chat window.
325
+ `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`.
302
326
 
303
- If you call `recall()` immediately after `invoke()`, it only sees memory that has already been ingested into storage. In queue mode, wait for your background worker to finish before expecting newly created memories to appear.
304
-
305
- ## Inspecting Memory
306
-
307
- Read a complete session graph snapshot:
308
-
309
- ```ts
310
- const snapshot = await agent.getGraphSnapshot();
311
-
312
- console.log(snapshot.sessionId);
327
+ If you call `recall()` immediately after `invoke()`, it only sees memory that has already been ingested into storage. In queue mode, wait for your background worker to finish before expecting newly created memories to appear.
328
+
329
+ ## Inspecting Memory
330
+
331
+ Read a complete session graph snapshot:
332
+
333
+ ```ts
334
+ const snapshot = await agent.getGraphSnapshot();
335
+
336
+ console.log(snapshot.sessionId);
313
337
  console.log(snapshot.nodes);
338
+ console.log(snapshot.snapshotNodes);
314
339
  console.log(snapshot.edges);
315
340
  console.log(snapshot.memories);
316
341
  console.log(snapshot.capturedAt);
317
- ```
318
-
319
- `getGraphSnapshot()` returns a `GraphSnapshot`:
320
-
342
+ ```
343
+
344
+ `getGraphSnapshot()` returns a `GraphSnapshot`:
345
+
321
346
  - `sessionId`: current agent session ID.
322
347
  - `nodes`: active topic nodes for the session.
348
+ - `snapshotNodes`: active topic nodes wrapped with provenance metadata when a node came from a graft.
323
349
  - `edges`: topic edges where either endpoint belongs to a session topic node.
324
350
  - `memories`: all memory nodes for the session, including decayed or superseded rows.
325
351
  - `capturedAt`: ISO timestamp for when the snapshot was produced.
326
352
 
327
- 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.
328
-
329
- Read active topic nodes:
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.
330
354
 
331
- ```ts
332
- const nodes = await agent.getActiveNodes();
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.
356
+
357
+ Read active topic nodes:
358
+
359
+ ```ts
360
+ const nodes = await agent.getActiveNodes();
333
361
 
334
362
  for (const node of nodes) {
335
363
  console.log({
@@ -419,6 +447,38 @@ const response = await writingBot.invoke(
419
447
  console.log(response);
420
448
  ```
421
449
 
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.
481
+
422
482
  ### Absorb By Semantic Prompt
423
483
 
424
484
  ```ts
@@ -480,6 +540,8 @@ const agent = new MemoGrafterAgent({
480
540
  bufferSize: 4,
481
541
  tokenBudget: 1500,
482
542
  recentWindowSize: 20,
543
+ recallLimit: 6,
544
+ recallMinSimilarity: 0.55,
483
545
  },
484
546
  cache: {
485
547
  connectionString: process.env.REDIS_URL!,
@@ -511,14 +573,15 @@ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
511
573
 
512
574
  `GraphStore` is the public storage interface. `PostgresGraphStore` is the default PostgreSQL and pgvector implementation.
513
575
 
514
- Useful store inspection methods include:
515
-
516
- - `getNodesBySession(sessionId)`: read topic nodes for a session.
517
- - `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
518
- - `getSegmentsBySession(sessionId)`: read topic segments for a session.
519
- - `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
576
+ Useful store inspection methods include:
577
+
578
+ - `getNodesBySession(sessionId)`: read topic nodes for a session.
579
+ - `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
580
+ - `getSegmentsBySession(sessionId)`: read topic segments for a session.
581
+ - `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
520
582
  - `getEdgesBySession(sessionId)`: read all topic edges where either endpoint belongs to the session's topic nodes.
521
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.
522
585
 
523
586
  ### `llm`
524
587
 
@@ -607,7 +670,7 @@ Without reentry detection, the later database discussion is just another topic n
607
670
 
608
671
  This helps graph traversal and memory injection recover earlier related context. A later question about connection pooling can still be connected to the original PostgreSQL/ACID discussion.
609
672
 
610
- Reentry edges are written between the current rebuilt topic nodes. They do not point at deleted nodes from previous ingestion passes.
673
+ Reentry edges are written between newly ingested topic nodes, and can also point from a new node back to an existing durable topic node when the detector recognizes a return to earlier context.
611
674
 
612
675
  ### `graph`
613
676
 
@@ -630,16 +693,20 @@ inject: {
630
693
  bufferSize: 4,
631
694
  tokenBudget: 1500,
632
695
  recentWindowSize: 20,
696
+ recallLimit: 6,
697
+ recallMinSimilarity: 0.55,
633
698
  }
634
699
  ```
635
700
 
636
- Controls memory prompt sizing and the raw history window kept when chat history overflows.
701
+ Controls memory prompt sizing, invoke-time recall, and the raw history window sent to the LLM.
637
702
 
638
703
  - `bufferSize`: nearby raw messages to include.
639
- - `tokenBudget`: approximate token budget used for memory prompts and agent history overflow checks. `MemoGrafterAgent` starts overflow handling at 80% of this value.
640
- - `recentWindowSize`: number of newest raw chat messages to keep after the pinned recall block during overflow. Defaults to `20`.
704
+ - `tokenBudget`: approximate token budget used for graft prompt assembly.
705
+ - `recentWindowSize`: number of newest raw chat messages to send after the optional invoke-time memory system message. Defaults to `20`.
706
+ - `recallLimit`: max memory facts to fetch for automatic invoke-time recall. Defaults to `6`.
707
+ - `recallMinSimilarity`: similarity floor for automatic invoke-time recall. Defaults to `0.55`.
641
708
 
642
- When `MemoGrafterAgent` detects overflow, it builds a recall query from the last six message contents, calls recall with `{ limit: 5, minSimilarity: 0.65 }`, prepends the returned memory prompt as a system message, and keeps the last `recentWindowSize` raw messages. If recall fails, it uses only that recent raw window.
709
+ When `MemoGrafterAgent.invoke()` sees existing topic nodes for the session, it calls recall with the current user message, prepends the returned memory prompt as a system message when facts are found, and keeps the last `recentWindowSize` raw messages. If recall fails or returns no facts, it uses only that recent raw window plus the current user message.
643
710
 
644
711
  ### `cache`
645
712
 
@@ -680,6 +747,8 @@ const agent = new MemoGrafterAgent({
680
747
 
681
748
  Queue mode is useful when ingestion becomes too slow to run inline. Redis connection problems are logged as warnings and should not throw from normal chatbot invocation.
682
749
 
750
+ Whether ingestion runs inline or through the queue, MemoGrafter uses the stored ingest cursor to skip message ranges that have already been processed. Queue retries should therefore avoid creating duplicate topic nodes for the same message range.
751
+
683
752
  ## Custom Adapters
684
753
 
685
754
  You can use any model provider if you implement the public adapter interfaces.
@@ -765,7 +834,7 @@ Breaking changes to pipeline internals may occur in minor versions.
765
834
 
766
835
  Available pipeline exports:
767
836
 
768
- - `IngestPipeline`: segments messages, builds topic nodes, extracts memory nodes, and writes graph edges.
837
+ - `IngestPipeline`: incrementally segments new messages, builds topic nodes, extracts memory nodes, and writes graph edges.
769
838
  - `RetrieverPipeline`: embeds a query, searches memory nodes, and returns a structured `RetrievalResult`.
770
839
  - `GrafterPipeline`: traverses the topic graph and assembles a token-budget-fitted system prompt.
771
840
 
@@ -860,15 +929,29 @@ await conductor.graftByPrompt("invoice credit policy", technical, {
860
929
  });
861
930
  ```
862
931
 
863
- ## Example Project
932
+ ## Example Projects
864
933
 
865
- This repository includes a runnable example:
934
+ This repository includes two runnable examples:
866
935
 
867
936
  ```text
937
+ examples/basic-chat-memory
868
938
  examples/chatbot-memory-demo
869
939
  ```
870
940
 
871
- Run it:
941
+ Run the single-agent memory demo:
942
+
943
+ ```bash
944
+ cd D:/cohort/projects/project-memoGrafter
945
+ npm install
946
+ npm run build
947
+
948
+ cd examples/basic-chat-memory
949
+ npm install
950
+ cp .env.example .env
951
+ npm run dev
952
+ ```
953
+
954
+ Run the two-agent grafting demo:
872
955
 
873
956
  ```bash
874
957
  cd D:/cohort/projects/project-memoGrafter
@@ -977,6 +1060,18 @@ const result = await agent.recall("Japan travel preferences", {
977
1060
  });
978
1061
  ```
979
1062
 
1063
+ For grafted sessions, also confirm that the source topic had active memory nodes before it was absorbed. Absorbing copies active memory rows, but it cannot create facts for a topic if extraction produced only a topic summary and no `mg_memory_nodes` rows.
1064
+
1065
+ ### Duplicate Or Unexpected Topic Nodes
1066
+
1067
+ MemoGrafter processes only messages after the stored ingest cursor. If you see duplicate topic ranges:
1068
+
1069
+ - Confirm your database has the `mg_session_ingest_state` table.
1070
+ - Confirm the same session ID is being reused for the same agent.
1071
+ - Check whether `clearSession()` was called, which resets the cursor and removes stored session data.
1072
+
1073
+ Incremental ingest can still create semantically similar topic nodes over time. That is expected; node merge and graph compaction are separate maintenance features.
1074
+
980
1075
  ### Redis Warnings
981
1076
 
982
1077
  Redis is only required when you pass `queue` or `cache` config. If you do not need background ingestion or recall caching, remove those sections.
@@ -995,7 +1090,8 @@ Practical notes:
995
1090
  - Use PostgreSQL with `pgvector` enabled.
996
1091
  - Tune `tokenBudget` to control prompt size and cost.
997
1092
  - Use queue mode if ingestion becomes slow.
998
- - Use the optional recall cache for long sessions with repeated or automatic overflow recall.
1093
+ - Use `clearSession()` only for intentional resets; normal ingestion preserves the graph incrementally.
1094
+ - Use the optional recall cache for long sessions with repeated direct or invoke-time recall.
999
1095
  - Store your own user/session mapping outside MemoGrafter.
1000
1096
  - Call `close()` during graceful shutdown.
1001
1097
  - Do not expose database credentials or OpenAI keys to browser code.
@@ -1025,26 +1121,31 @@ Main exports:
1025
1121
  - `RetrieverConfig`
1026
1122
  - public shared and fleet types
1027
1123
 
1028
- Useful `GraphStore` inspection methods:
1029
-
1030
- - `getTopicNode(topicNodeId, sessionId?)`
1031
- - `getNodesBySession(sessionId)`
1032
- - `getSegmentsBySession(sessionId)`
1033
- - `getEdgesByType(sessionId, type)`
1034
- - `getEdgesBySession(sessionId)`
1035
- - `getMemoriesBySession(sessionId)`
1036
-
1037
- Common `MemoGrafterAgent` methods:
1124
+ Useful `GraphStore` inspection methods:
1125
+
1126
+ - `getTopicNode(topicNodeId, sessionId?)`
1127
+ - `getNodesBySession(sessionId)`
1128
+ - `getSegmentsBySession(sessionId)`
1129
+ - `getEdgesByType(sessionId, type)`
1130
+ - `getEdgesBySession(sessionId)`
1131
+ - `getMemoriesBySession(sessionId)`
1132
+ - `getSessionNodeCount(sessionId)`
1133
+ - `getSessionIngestState(sessionId)`
1134
+
1135
+ Common `MemoGrafterAgent` methods:
1038
1136
 
1039
1137
  - `initialize()`: initialize storage.
1040
1138
  - `invoke(message)`: send a user message and receive an assistant response.
1041
1139
  - `getHistory()`: read local chat history.
1042
- - `getSessionId()`: read the current session ID.
1140
+ - `getSessionId()`: read the current session ID.
1043
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.
1044
1143
  - `getActiveNodes()`: inspect topic nodes.
1045
1144
  - `getActiveSegments()`: inspect topic segments.
1046
- - `recall(query, options?)`: retrieve structured memory by semantic query.
1047
- - `graft(topicIds?)`: preview memory injection.
1048
- - `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
1049
- - `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
1050
- - `close()`: close database and queue resources.
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.
@@ -1,11 +1,12 @@
1
- import type { AbsorbFromAgentOptions, GraphSnapshot, InjectionResult, MemoGrafterConfig, Message, RetrievalResult, RetrieverConfig, TopicNode, TopicSegment } from "./types.js";
1
+ import type { AbsorbFromAgentOptions, GraftRegistryEntry, GraphSnapshot, InjectionResult, MemoGrafterConfig, Message, RetrievalResult, RetrieverConfig, TopicNode, TopicSegment } from "./types.js";
2
2
  export declare class MemoGrafterAgent {
3
3
  private readonly core;
4
4
  private readonly sessionId;
5
5
  private readonly history;
6
6
  private readonly baseSystemPrompt;
7
- private readonly historyTokenBudget;
8
7
  private readonly recentWindowSize;
8
+ private readonly recallLimit;
9
+ private readonly recallMinSimilarity;
9
10
  private readonly cacheConfig;
10
11
  private pendingIngest;
11
12
  constructor(config: MemoGrafterConfig);
@@ -16,12 +17,15 @@ export declare class MemoGrafterAgent {
16
17
  getActiveNodes(): Promise<TopicNode[]>;
17
18
  getActiveSegments(): Promise<TopicSegment[]>;
18
19
  getGraphSnapshot(): Promise<GraphSnapshot>;
20
+ getGraftRegistry(): Promise<GraftRegistryEntry[]>;
21
+ removeGraft(nodeId: string): Promise<void>;
22
+ clearSession(): Promise<void>;
19
23
  graft(topicIds?: string[]): Promise<InjectionResult>;
20
24
  ingestGraftedNodes(nodes: TopicNode[]): Promise<TopicNode[]>;
21
25
  recall(query: string, options?: RetrieverConfig): Promise<RetrievalResult>;
22
26
  absorbFromAgent(sourceAgent: MemoGrafterAgent, options?: AbsorbFromAgentOptions): Promise<TopicNode[]>;
23
27
  private enqueueBackgroundIngest;
24
- private buildHistory;
28
+ private _buildMemoryContext;
25
29
  close(): Promise<void>;
26
30
  }
27
31
  //# sourceMappingURL=MemoGrafterAgent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MemoGrafterAgent.d.ts","sourceRoot":"","sources":["../src/MemoGrafterAgent.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,aAAa,CAAoC;gBAE7C,MAAM,EAAE,iBAAiB;IAQrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYlD,UAAU,IAAI,OAAO,EAAE;IAIvB,YAAY,IAAI,MAAM;IAIhB,cAAc,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAMtC,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAM5C,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;IAe1C,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAO1D,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAItD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkB9E,eAAe,CAAC,WAAW,EAAE,gBAAgB,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAKhH,OAAO,CAAC,uBAAuB;YAUjB,YAAY;IA4B1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvB"}
1
+ {"version":3,"file":"MemoGrafterAgent.d.ts","sourceRoot":"","sources":["../src/MemoGrafterAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,aAAa,CAAoC;gBAE7C,MAAM,EAAE,iBAAiB;IASrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBlD,UAAU,IAAI,OAAO,EAAE;IAIvB,YAAY,IAAI,MAAM;IAIhB,cAAc,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAMtC,iBAAiB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAM5C,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiC1C,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAKjD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAM7B,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC;IAO1D,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAItD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkB9E,eAAe,CAAC,WAAW,EAAE,gBAAgB,EAAE,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAKhH,OAAO,CAAC,uBAAuB;YAUjB,mBAAmB;IAsBjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvB"}