memo-grafter 0.1.1 → 0.2.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 (130) hide show
  1. package/.env.example +1 -0
  2. package/README.md +19 -41
  3. package/USER_GUIDE.md +329 -21
  4. package/dist/MemoGrafter.d.ts +3 -1
  5. package/dist/MemoGrafter.d.ts.map +1 -1
  6. package/dist/MemoGrafter.js +31 -5
  7. package/dist/MemoGrafter.js.map +1 -1
  8. package/dist/MemoGrafterAgent.d.ts +9 -1
  9. package/dist/MemoGrafterAgent.d.ts.map +1 -1
  10. package/dist/MemoGrafterAgent.js +62 -8
  11. package/dist/MemoGrafterAgent.js.map +1 -1
  12. package/dist/adapters/AnthropicAdapter.d.ts.map +1 -1
  13. package/dist/adapters/AnthropicAdapter.js +8 -2
  14. package/dist/adapters/AnthropicAdapter.js.map +1 -1
  15. package/dist/adapters/GeminiAdapter.d.ts +15 -0
  16. package/dist/adapters/GeminiAdapter.d.ts.map +1 -0
  17. package/dist/adapters/GeminiAdapter.js +48 -0
  18. package/dist/adapters/GeminiAdapter.js.map +1 -0
  19. package/dist/fleet/FleetStore.d.ts +1 -1
  20. package/dist/fleet/FleetStore.d.ts.map +1 -1
  21. package/dist/index.d.ts +7 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +5 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/pipeline/GrafterPipeline.d.ts +8 -3
  26. package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
  27. package/dist/pipeline/GrafterPipeline.js +8 -6
  28. package/dist/pipeline/GrafterPipeline.js.map +1 -1
  29. package/dist/pipeline/IngestPipeline.d.ts +19 -4
  30. package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
  31. package/dist/pipeline/IngestPipeline.js +40 -6
  32. package/dist/pipeline/IngestPipeline.js.map +1 -1
  33. package/dist/pipeline/RetrieverPipeline.d.ts +24 -0
  34. package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -0
  35. package/dist/pipeline/RetrieverPipeline.js +106 -0
  36. package/dist/pipeline/RetrieverPipeline.js.map +1 -0
  37. package/dist/pipeline/SegmentProcessor.d.ts +3 -2
  38. package/dist/pipeline/SegmentProcessor.d.ts.map +1 -1
  39. package/dist/pipeline/SegmentProcessor.js +50 -18
  40. package/dist/pipeline/SegmentProcessor.js.map +1 -1
  41. package/dist/pipeline/TopicDriftDetector.d.ts +21 -3
  42. package/dist/pipeline/TopicDriftDetector.d.ts.map +1 -1
  43. package/dist/pipeline/TopicDriftDetector.js +143 -17
  44. package/dist/pipeline/TopicDriftDetector.js.map +1 -1
  45. package/dist/prompts/factRetrievalPrompt.d.ts +4 -0
  46. package/dist/prompts/factRetrievalPrompt.d.ts.map +1 -0
  47. package/dist/prompts/factRetrievalPrompt.js +25 -0
  48. package/dist/prompts/factRetrievalPrompt.js.map +1 -0
  49. package/dist/prompts/historyCompressionPrompt.d.ts +3 -0
  50. package/dist/prompts/historyCompressionPrompt.d.ts.map +1 -0
  51. package/dist/prompts/historyCompressionPrompt.js +4 -0
  52. package/dist/prompts/historyCompressionPrompt.js.map +1 -0
  53. package/dist/prompts/intentShiftPrompt.d.ts +3 -0
  54. package/dist/prompts/intentShiftPrompt.d.ts.map +1 -0
  55. package/dist/prompts/intentShiftPrompt.js +11 -0
  56. package/dist/prompts/intentShiftPrompt.js.map +1 -0
  57. package/dist/prompts/segmentExtractionPrompt.d.ts.map +1 -1
  58. package/dist/prompts/segmentExtractionPrompt.js +68 -12
  59. package/dist/prompts/segmentExtractionPrompt.js.map +1 -1
  60. package/dist/store/GraphStore.d.ts +16 -19
  61. package/dist/store/GraphStore.d.ts.map +1 -1
  62. package/dist/store/GraphStore.js +1 -576
  63. package/dist/store/GraphStore.js.map +1 -1
  64. package/dist/store/index.d.ts +3 -0
  65. package/dist/store/index.d.ts.map +1 -0
  66. package/dist/store/index.js +2 -0
  67. package/dist/store/index.js.map +1 -0
  68. package/dist/store/postgres-pgvector/GraphStore.d.ts +75 -0
  69. package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -0
  70. package/dist/store/postgres-pgvector/GraphStore.js +782 -0
  71. package/dist/store/postgres-pgvector/GraphStore.js.map +1 -0
  72. package/dist/types.d.ts +79 -1
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/utils/drift/driftMarkers.d.ts +4 -0
  75. package/dist/utils/drift/driftMarkers.d.ts.map +1 -0
  76. package/dist/utils/drift/driftMarkers.js +31 -0
  77. package/dist/utils/drift/driftMarkers.js.map +1 -0
  78. package/dist/utils/drift/driftScore.d.ts +3 -0
  79. package/dist/utils/drift/driftScore.d.ts.map +1 -0
  80. package/dist/utils/drift/driftScore.js +12 -0
  81. package/dist/utils/drift/driftScore.js.map +1 -0
  82. package/dist/utils/drift/driftThreshold.d.ts +7 -0
  83. package/dist/utils/drift/driftThreshold.d.ts.map +1 -0
  84. package/dist/utils/drift/driftThreshold.js +21 -0
  85. package/dist/utils/drift/driftThreshold.js.map +1 -0
  86. package/dist/utils/drift/reentryMatch.d.ts +3 -0
  87. package/dist/utils/drift/reentryMatch.d.ts.map +1 -0
  88. package/dist/utils/drift/reentryMatch.js +10 -0
  89. package/dist/utils/drift/reentryMatch.js.map +1 -0
  90. package/dist/utils/extraction/segmentExtraction.d.ts +5 -0
  91. package/dist/utils/extraction/segmentExtraction.d.ts.map +1 -0
  92. package/dist/utils/extraction/segmentExtraction.js +82 -0
  93. package/dist/utils/extraction/segmentExtraction.js.map +1 -0
  94. package/dist/utils/reentry/reentryCues.d.ts +3 -0
  95. package/dist/utils/reentry/reentryCues.d.ts.map +1 -0
  96. package/dist/utils/reentry/reentryCues.js +14 -0
  97. package/dist/utils/reentry/reentryCues.js.map +1 -0
  98. package/dist/utils/reentry/reentryEdges.d.ts +16 -0
  99. package/dist/utils/reentry/reentryEdges.d.ts.map +1 -0
  100. package/dist/utils/reentry/reentryEdges.js +80 -0
  101. package/dist/utils/reentry/reentryEdges.js.map +1 -0
  102. package/dist/utils/reentry/reentrySimilarity.d.ts +5 -0
  103. package/dist/utils/reentry/reentrySimilarity.d.ts.map +1 -0
  104. package/dist/utils/reentry/reentrySimilarity.js +26 -0
  105. package/dist/utils/reentry/reentrySimilarity.js.map +1 -0
  106. package/dist/utils/reentry/reentryText.d.ts +6 -0
  107. package/dist/utils/reentry/reentryText.d.ts.map +1 -0
  108. package/dist/utils/reentry/reentryText.js +29 -0
  109. package/dist/utils/reentry/reentryText.js.map +1 -0
  110. package/dist/utils/reentry/types.d.ts +6 -0
  111. package/dist/utils/reentry/types.d.ts.map +1 -0
  112. package/dist/utils/reentry/types.js +2 -0
  113. package/dist/utils/reentry/types.js.map +1 -0
  114. package/dist/utils/text/normalizeText.d.ts +2 -0
  115. package/dist/utils/text/normalizeText.d.ts.map +1 -0
  116. package/dist/utils/text/normalizeText.js +5 -0
  117. package/dist/utils/text/normalizeText.js.map +1 -0
  118. package/dist/utils/text/terms.d.ts +3 -0
  119. package/dist/utils/text/terms.d.ts.map +1 -0
  120. package/dist/utils/text/terms.js +51 -0
  121. package/dist/utils/text/terms.js.map +1 -0
  122. package/dist/utils/text/tokenCount.d.ts +3 -0
  123. package/dist/utils/text/tokenCount.d.ts.map +1 -0
  124. package/dist/utils/text/tokenCount.js +7 -0
  125. package/dist/utils/text/tokenCount.js.map +1 -0
  126. package/dist/utils/vector/vectorLiteral.d.ts +3 -0
  127. package/dist/utils/vector/vectorLiteral.d.ts.map +1 -0
  128. package/dist/utils/vector/vectorLiteral.js +19 -0
  129. package/dist/utils/vector/vectorLiteral.js.map +1 -0
  130. package/package.json +11 -3
package/.env.example CHANGED
@@ -1,4 +1,5 @@
1
1
  DATABASE_URL=postgres://postgres:postgres@localhost:5432/memograffer
2
2
  ANTHROPIC_API_KEY=
3
+ GEMINI_API_KEY=
3
4
  OPENAI_API_KEY=
4
5
  REDIS_URL=redis://localhost:6379
package/README.md CHANGED
@@ -3,32 +3,28 @@
3
3
 
4
4
  Structured memory for TypeScript chatbots.
5
5
 
6
- MemoGrafter helps chatbot applications remember conversations without stuffing every old message back into the prompt. It turns conversations into topic-based memory, retrieves the relevant parts later, and can copy useful memory from one chatbot or session into another.
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
7
 
8
- It is a memory framework, not an autonomous agent runtime. It does not run tools, schedule tasks, or decide goals for an agent.
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
9
 
10
- ## What You Can Build
10
+ ## What It Is For
11
11
 
12
- - Chatbots that remember user preferences across long conversations.
13
- - Support or tutoring assistants that recall prior topics without replaying full history.
14
- - Multi-chatbot demos where one bot can absorb selected memory from another.
15
- - Prototypes for graph-based conversational memory, retrieval, and memory transfer.
16
- - Server-side TypeScript apps that need reusable LLM memory primitives.
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.
17
16
 
18
17
  ## How It Works
19
18
 
20
- At a high level:
21
-
22
19
  ```text
23
20
  chat messages
24
- -> topic segments
25
- -> memory nodes
21
+ -> topic-based memory
26
22
  -> graph links
27
- -> relevant memory injection
23
+ -> relevant recall
28
24
  -> optional memory grafting
29
25
  ```
30
26
 
31
- MemoGrafter stores conversation turns, detects topic shifts, summarizes segments into memory nodes, links related nodes, and injects relevant memory into future LLM calls. Memory grafting lets one chatbot or session copy selected memory from another.
27
+ MemoGrafter stores conversation turns, detects topic changes, summarizes useful context, links related memories, and retrieves or grafts memory when needed.
32
28
 
33
29
  ## Install
34
30
 
@@ -36,12 +32,7 @@ MemoGrafter stores conversation turns, detects topic shifts, summarizes segments
36
32
  npm install memo-grafter
37
33
  ```
38
34
 
39
- MemoGrafter runs server-side on Node.js and requires PostgreSQL with `pgvector`. The included OpenAI adapters also require `OPENAI_API_KEY`.
40
-
41
- ```bash
42
- DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
43
- OPENAI_API_KEY=sk-...
44
- ```
35
+ MemoGrafter runs server-side on Node.js. The built-in storage backend uses PostgreSQL with `pgvector`.
45
36
 
46
37
  ## Minimal Example
47
38
 
@@ -62,33 +53,20 @@ const agent = new MemoGrafterAgent({
62
53
 
63
54
  await agent.initialize();
64
55
 
65
- console.log(await agent.invoke("I am planning a Japan trip."));
66
- console.log(await agent.invoke("I like quiet towns, bookstores, and local cafes."));
67
- console.log(await agent.invoke("What do you remember about my travel preferences?"));
68
-
69
- await agent.close();
70
- ```
71
-
72
- ## Memory Grafting
56
+ await agent.invoke("I am planning a Japan trip.");
57
+ await agent.invoke("I like quiet towns, bookstores, and local cafes.");
73
58
 
74
- Memory grafting is the core idea behind the name: one chatbot can build memory, and another chatbot can absorb only the useful parts.
59
+ const recall = await agent.recall("travel preferences");
60
+ console.log(recall.facts);
75
61
 
76
- ```ts
77
- await writingBot.absorbFromAgent(travelBot, {
78
- prompt: "Japan travel preferences",
79
- limit: 3,
80
- });
62
+ await agent.close();
81
63
  ```
82
64
 
83
65
  ## Learn More
84
66
 
85
- Read [USER_GUIDE.md](https://github.com/mayhemking007/memo-grafter/blob/main/USER_GUIDE.md) for setup, configuration, queue mode, custom adapters, fleet APIs, examples, and troubleshooting.
86
-
87
- This repository also includes a runnable demo:
88
-
89
- ```text
90
- examples/chatbot-memory-demo
91
- ```
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.
92
70
 
93
71
  ## License
94
72
 
package/USER_GUIDE.md CHANGED
@@ -12,12 +12,13 @@ The most important idea is memory grafting. A chatbot can build useful memory du
12
12
 
13
13
  - Node.js 18 or newer.
14
14
  - TypeScript or modern JavaScript using ES modules.
15
- - PostgreSQL with the `pgvector` extension enabled.
15
+ - PostgreSQL with the `pgvector` extension enabled for the built-in `PostgresGraphStore`.
16
16
  - An LLM adapter.
17
17
  - An embedding adapter.
18
18
  - An OpenAI API key only if using the included OpenAI adapters.
19
19
  - An Anthropic API key only if using the included Anthropic LLM adapter.
20
- - Redis only if enabling queue mode.
20
+ - A Gemini API key only if using the included Gemini adapters.
21
+ - Redis only if enabling queue mode or the optional recall cache.
21
22
 
22
23
  MemoGrafter is server-side only. Do not run it in browser code.
23
24
 
@@ -51,17 +52,20 @@ Create a `.env` file in your app:
51
52
  ```bash
52
53
  DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
53
54
  ANTHROPIC_API_KEY=sk-ant-...
55
+ GEMINI_API_KEY=...
54
56
  OPENAI_API_KEY=sk-...
55
57
  REDIS_URL=redis://localhost:6379
56
58
  ```
57
59
 
58
- `DATABASE_URL` is required.
60
+ `DATABASE_URL` is required when using the built-in PostgreSQL storage.
59
61
 
60
62
  `OPENAI_API_KEY` is required only when using `OpenAILLMAdapter` or `OpenAIEmbedAdapter`.
61
63
 
62
64
  `ANTHROPIC_API_KEY` is required only when using `AnthropicLLMAdapter`.
63
65
 
64
- `REDIS_URL` is optional and only needed when you pass `queue` config.
66
+ `GEMINI_API_KEY` is required only when using `GeminiLLMAdapter` or `GeminiEmbedAdapter`.
67
+
68
+ `REDIS_URL` is optional and only needed when you pass `queue` or `cache` config.
65
69
 
66
70
  Enable `pgvector` in PostgreSQL:
67
71
 
@@ -69,7 +73,7 @@ Enable `pgvector` in PostgreSQL:
69
73
  CREATE EXTENSION IF NOT EXISTS vector;
70
74
  ```
71
75
 
72
- MemoGrafter creates its own tables during `initialize()`.
76
+ The built-in `PostgresGraphStore` creates its own tables during `initialize()`.
73
77
 
74
78
  Current v1 tables:
75
79
 
@@ -77,6 +81,8 @@ Current v1 tables:
77
81
  - `mg_segments`
78
82
  - `mg_topic_nodes`
79
83
  - `mg_topic_edges`
84
+ - `mg_memory_nodes`
85
+ - `mg_memory_edges`
80
86
  - `mg_fleets`
81
87
  - `mg_fleet_agents`
82
88
 
@@ -129,17 +135,25 @@ A message is one user or assistant turn:
129
135
 
130
136
  ```ts
131
137
  export interface Message {
132
- role: "user" | "assistant";
138
+ role: "system" | "user" | "assistant";
133
139
  content: string;
134
140
  }
135
141
  ```
136
142
 
137
- `MemoGrafterAgent` keeps an in-memory history for the current session and stores messages in PostgreSQL during ingestion.
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.
138
144
 
139
145
  ### Segments
140
146
 
141
147
  A segment is a range of messages that belong to the same topic. MemoGrafter uses drift detection to decide where topic boundaries are.
142
148
 
149
+ Drift detection combines several signals:
150
+
151
+ - how far the current message is from the current topic embedding,
152
+ - whether the current message is a sharp pivot from the previous user message,
153
+ - whether the message contains structural phrases such as "by the way", "different topic", or "going back to",
154
+ - short-message dampening so filler like "okay" or "got it" is less likely to create false boundaries,
155
+ - optional LLM classification for ambiguous topic-shift scores.
156
+
143
157
  Example:
144
158
 
145
159
  ```text
@@ -166,9 +180,29 @@ Important fields:
166
180
 
167
181
  Topic nodes are stored in `mg_topic_nodes`.
168
182
 
183
+ ### Memory Nodes
184
+
185
+ Memory nodes are typed atomic memories attached to topic nodes. They are the units used by targeted recall.
186
+
187
+ Important fields:
188
+
189
+ - `memoryType`: `"fact"`, `"insight"`, `"question"`, `"task"`, or `"reference"`.
190
+ - `subject`, `predicate`, `value`: the structured memory triple.
191
+ - `confidence`: confidence score from `0` to `1`.
192
+ - `topicNodeId`: parent topic node ID.
193
+ - `decayed`: whether the memory is stale.
194
+ - `supersededBy`: newer memory ID when this memory has been replaced.
195
+
196
+ Memory nodes are stored in `mg_memory_nodes`.
197
+
169
198
  ### Graph Edges
170
199
 
171
- Edges connect related topic nodes. They can represent temporal, semantic, or grafted relationships.
200
+ Edges connect related topic nodes. They can represent temporal, semantic, grafted, or reentry relationships.
201
+
202
+ - `temporal`: one topic followed another in the conversation.
203
+ - `semantic`: two topics are similar by embedding search.
204
+ - `grafted`: a topic was copied from another session or chatbot.
205
+ - `reentry`: the conversation returned to an earlier topic after discussing something else.
172
206
 
173
207
  Edges are stored in `mg_topic_edges`.
174
208
 
@@ -218,13 +252,55 @@ await agent.close();
218
252
  On every call, `invoke()`:
219
253
 
220
254
  1. Adds the user message to local history.
221
- 2. Loads existing topic nodes for the session.
222
- 3. Builds a memory injection prompt from those nodes.
223
- 4. Sends history plus memory prompt to the LLM.
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.
224
258
  5. Adds the assistant response to history.
225
- 6. Ingests the updated conversation into the memory graph.
259
+ 6. Queues ingestion of the updated conversation into the memory graph.
260
+
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.
262
+
263
+ 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
+
265
+ ### Targeted Recall
266
+
267
+ Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
268
+
269
+ ```ts
270
+ const result = await agent.recall("deployment config", {
271
+ limit: 8,
272
+ minSimilarity: 0.55,
273
+ tokenBudget: 1000,
274
+ cache: {
275
+ ttlSeconds: 90,
276
+ },
277
+ });
278
+
279
+ console.log(result.facts);
280
+ console.log(result.nodes);
281
+ console.log(result.systemPrompt);
282
+ console.log(result.tokenCount);
283
+ ```
284
+
285
+ `recall()` returns a `RetrievalResult`:
286
+
287
+ - `facts`: matching memory nodes with a `similarity` score.
288
+ - `nodes`: parent topic nodes for the included facts.
289
+ - `systemPrompt`: a formatted memory block that can be passed to an LLM if you choose.
290
+ - `tokenCount`: approximate token count for the included fact blocks.
291
+
292
+ Options:
293
+
294
+ - `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
295
+ - `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
296
+ - `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
297
+ - `cache.ttlSeconds`: per-call recall cache TTL override when `MemoGrafterConfig.cache` is enabled. Values are clamped to 60-120 seconds.
298
+
299
+ `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
+
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.
226
302
 
227
- On the first turn there may be no memory to inject. Later turns can use memory created from earlier turns.
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.
228
304
 
229
305
  ## Inspecting Memory
230
306
 
@@ -368,8 +444,11 @@ const agent = new MemoGrafterAgent({
368
444
  drift: {
369
445
  mode: "intent",
370
446
  windowSize: 5,
371
- threshold: 0.3,
447
+ driftSensitivity: "medium",
372
448
  minSegmentMessages: 3,
449
+ llmAmbiguityDetection: false,
450
+ reentryDetection: true,
451
+ reentryThreshold: 0.85,
373
452
  },
374
453
  graph: {
375
454
  topK: 5,
@@ -378,6 +457,11 @@ const agent = new MemoGrafterAgent({
378
457
  inject: {
379
458
  bufferSize: 4,
380
459
  tokenBudget: 1500,
460
+ recentWindowSize: 20,
461
+ },
462
+ cache: {
463
+ connectionString: process.env.REDIS_URL!,
464
+ ttlSeconds: 90,
381
465
  },
382
466
  });
383
467
  ```
@@ -390,7 +474,27 @@ db: {
390
474
  }
391
475
  ```
392
476
 
393
- PostgreSQL connection string.
477
+ PostgreSQL connection string used by the built-in `PostgresGraphStore`.
478
+
479
+ MemoGrafter currently constructs `PostgresGraphStore` from this config internally. Advanced users can also import the storage contract directly:
480
+
481
+ ```ts
482
+ import {
483
+ PostgresGraphStore,
484
+ type GraphStore,
485
+ } from "memo-grafter";
486
+
487
+ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
488
+ ```
489
+
490
+ `GraphStore` is the public storage interface. `PostgresGraphStore` is the default PostgreSQL and pgvector implementation.
491
+
492
+ Useful store inspection methods include:
493
+
494
+ - `getNodesBySession(sessionId)`: read topic nodes for a session.
495
+ - `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
496
+ - `getSegmentsBySession(sessionId)`: read topic segments for a session.
497
+ - `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
394
498
 
395
499
  ### `llm`
396
500
 
@@ -406,6 +510,12 @@ Anthropic models can be used with the included Anthropic adapter:
406
510
  llm: new AnthropicLLMAdapter("claude-sonnet-4-5")
407
511
  ```
408
512
 
513
+ Gemini models can be used with the included Gemini adapter:
514
+
515
+ ```ts
516
+ llm: new GeminiLLMAdapter("gemini-2.5-flash")
517
+ ```
518
+
409
519
  ### `embedder`
410
520
 
411
521
  ```ts
@@ -416,14 +526,25 @@ Adapter used to create vectors for semantic search.
416
526
 
417
527
  Anthropic does not provide a native embedding API. Pair `AnthropicLLMAdapter` with `OpenAIEmbedAdapter` or another custom `EmbedAdapter`.
418
528
 
529
+ Gemini embeddings can be used with the included Gemini embedder:
530
+
531
+ ```ts
532
+ embedder: new GeminiEmbedAdapter("gemini-embedding-001")
533
+ ```
534
+
535
+ `GeminiEmbedAdapter` requests 1536-dimensional embeddings by default to match MemoGrafter's current `vector(1536)` database schema. If you change the schema dimension, pass the matching value as the second constructor argument.
536
+
419
537
  ### `drift`
420
538
 
421
539
  ```ts
422
540
  drift: {
423
541
  mode: "intent",
424
542
  windowSize: 5,
425
- threshold: 0.3,
543
+ driftSensitivity: "medium",
426
544
  minSegmentMessages: 3,
545
+ llmAmbiguityDetection: false,
546
+ reentryDetection: true,
547
+ reentryThreshold: 0.85,
427
548
  }
428
549
  ```
429
550
 
@@ -431,11 +552,39 @@ Controls topic boundary detection.
431
552
 
432
553
  - `mode`: `"intent"` or `"window"`.
433
554
  - `windowSize`: message window size for window mode.
434
- - `threshold`: drift sensitivity.
555
+ - `driftSensitivity`: preferred sensitivity preset, one of `"low"`, `"medium"`, or `"high"`.
556
+ - `threshold`: deprecated numeric threshold. It still works when `driftSensitivity` is not set, but MemoGrafter logs a one-time warning.
435
557
  - `minSegmentMessages`: minimum messages before a boundary.
558
+ - `llmAmbiguityDetection`: optional LLM check for borderline topic shifts. Defaults to `false`.
559
+ - `reentryDetection`: whether to link later topic returns back to earlier topic nodes. Defaults to `true`.
560
+ - `reentryThreshold`: embedding similarity threshold for reentry detection. Defaults to `0.85`.
436
561
 
437
562
  Use `"intent"` for most chatbot memory demos. In intent mode, user messages drive topic shifts.
438
563
 
564
+ Sensitivity presets resolve internally to numeric thresholds:
565
+
566
+ - `"low"`: `0.25`
567
+ - `"medium"`: `0.35`
568
+ - `"high"`: `0.50`
569
+
570
+ Use `"medium"` first. Boundaries are cut when a drift score exceeds the resolved threshold, so lower numeric thresholds split more readily and higher numeric thresholds require stronger evidence.
571
+
572
+ If both `driftSensitivity` and `threshold` are provided, `driftSensitivity` wins.
573
+
574
+ #### Reentry Detection
575
+
576
+ Reentry detection handles conversations that leave a topic and later return to it:
577
+
578
+ ```text
579
+ database choice -> authentication flow -> database connection pooling
580
+ ```
581
+
582
+ Without reentry detection, the later database discussion is just another topic node. With reentry detection, MemoGrafter creates a `reentry` edge from the later database node back to the earlier database node.
583
+
584
+ 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.
585
+
586
+ Reentry edges are written between the current rebuilt topic nodes. They do not point at deleted nodes from previous ingestion passes.
587
+
439
588
  ### `graph`
440
589
 
441
590
  ```ts
@@ -456,13 +605,33 @@ Controls graph retrieval and traversal.
456
605
  inject: {
457
606
  bufferSize: 4,
458
607
  tokenBudget: 1500,
608
+ recentWindowSize: 20,
459
609
  }
460
610
  ```
461
611
 
462
- Controls how much memory is inserted into the prompt.
612
+ Controls memory prompt sizing and the raw history window kept when chat history overflows.
463
613
 
464
614
  - `bufferSize`: nearby raw messages to include.
465
- - `tokenBudget`: approximate memory prompt budget.
615
+ - `tokenBudget`: approximate token budget used for memory prompts and agent history overflow checks. `MemoGrafterAgent` starts overflow handling at 80% of this value.
616
+ - `recentWindowSize`: number of newest raw chat messages to keep after the pinned recall block during overflow. Defaults to `20`.
617
+
618
+ 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.
619
+
620
+ ### `cache`
621
+
622
+ ```ts
623
+ cache: {
624
+ connectionString: process.env.REDIS_URL!,
625
+ ttlSeconds: 90,
626
+ }
627
+ ```
628
+
629
+ Enables an opt-in Redis cache for targeted recall. MemoGrafter creates one shared Redis client and uses it to cache only the raw `searchMemories()` result. It does not cache final prompts, filtered blocks, or `RetrievalResult`, so different `tokenBudget` values still assemble fresh output.
630
+
631
+ - `connectionString`: Redis URL.
632
+ - `ttlSeconds`: cache TTL in seconds. Defaults to `90` and is clamped between `60` and `120`.
633
+
634
+ 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.
466
635
 
467
636
  ## Queue Mode
468
637
 
@@ -531,6 +700,88 @@ const agent = new MemoGrafterAgent({
531
700
 
532
701
  Your embedding vector dimension must match the vector dimension expected by the database schema.
533
702
 
703
+ ## Storage Backends
704
+
705
+ MemoGrafter exposes storage through the `GraphStore` interface. The built-in implementation is `PostgresGraphStore`, which stores relational data, graph edges, and vector search data in PostgreSQL with `pgvector`.
706
+
707
+ Most users do not need to instantiate a store directly. `MemoGrafter` and `MemoGrafterAgent` use `PostgresGraphStore` from the `db.connectionString` config:
708
+
709
+ ```ts
710
+ const agent = new MemoGrafterAgent({
711
+ db: {
712
+ connectionString: process.env.DATABASE_URL!,
713
+ },
714
+ llm,
715
+ embedder,
716
+ });
717
+ ```
718
+
719
+ If you are extending MemoGrafter, use `GraphStore` as the contract and keep concrete storage details behind an implementation:
720
+
721
+ ```ts
722
+ import type { GraphStore } from "memo-grafter";
723
+
724
+ class MyGraphStore implements GraphStore {
725
+ // Implement the full GraphStore contract.
726
+ }
727
+ ```
728
+
729
+ Future storage implementations can use the same interface without changing pipeline or fleet code. For example, a SQLite plus vector-database implementation would implement `GraphStore` while preserving the same behavior expected by ingestion, grafting, and fleet APIs.
730
+
731
+ ## Using Pipelines Directly
732
+
733
+ `MemoGrafterAgent` is the recommended starting point, but the underlying
734
+ pipeline classes are also exported for developers who want to build custom
735
+ agent loops or integrate MemoGrafter memory primitives into an existing
736
+ orchestration framework.
737
+
738
+ Pipeline classes are exported for composability. Their constructors and
739
+ internal behavior are not covered by semver stability guarantees until v1.0.
740
+ Breaking changes to pipeline internals may occur in minor versions.
741
+
742
+ Available pipeline exports:
743
+
744
+ - `IngestPipeline`: segments messages, builds topic nodes, extracts memory nodes, and writes graph edges.
745
+ - `RetrieverPipeline`: embeds a query, searches memory nodes, and returns a structured `RetrievalResult`.
746
+ - `GrafterPipeline`: traverses the topic graph and assembles a token-budget-fitted system prompt.
747
+
748
+ Example using `RetrieverPipeline` directly:
749
+
750
+ ```ts
751
+ import {
752
+ PostgresGraphStore,
753
+ RetrieverPipeline,
754
+ OpenAIEmbedAdapter,
755
+ } from "memo-grafter";
756
+
757
+ const store = new PostgresGraphStore(process.env.DATABASE_URL!);
758
+ await store.initialize();
759
+
760
+ const embedder = new OpenAIEmbedAdapter("text-embedding-3-small");
761
+
762
+ const retriever = new RetrieverPipeline(store, embedder, {
763
+ limit: 8,
764
+ minSimilarity: 0.55,
765
+ tokenBudget: 1000,
766
+ });
767
+
768
+ const result = await retriever.run(
769
+ "deployment config and Kubernetes namespace",
770
+ sessionId,
771
+ );
772
+
773
+ console.log(result.facts);
774
+ console.log(result.systemPrompt);
775
+
776
+ await store.close();
777
+ ```
778
+
779
+ When using pipelines directly you are responsible for managing the store
780
+ connection lifecycle. Call `store.close()` during graceful shutdown.
781
+
782
+ `MemoGrafterAgent` remains the batteries-included default. Existing code
783
+ that uses `MemoGrafterAgent` does not need to change.
784
+
534
785
  ## Fleet API
535
786
 
536
787
  Fleets let you group color-scoped worker chatbots and use a conductor to graft memory across workers.
@@ -641,11 +892,31 @@ For small demos, try:
641
892
  ```ts
642
893
  drift: {
643
894
  mode: "intent",
644
- threshold: 0.3,
895
+ driftSensitivity: "medium",
645
896
  minSegmentMessages: 3,
646
897
  }
647
898
  ```
648
899
 
900
+ If segments are too coarse, try a lower resolved threshold:
901
+
902
+ ```ts
903
+ drift: {
904
+ mode: "intent",
905
+ driftSensitivity: "low",
906
+ minSegmentMessages: 2,
907
+ }
908
+ ```
909
+
910
+ If segments are too fragmented, try a higher resolved threshold:
911
+
912
+ ```ts
913
+ drift: {
914
+ mode: "intent",
915
+ driftSensitivity: "high",
916
+ minSegmentMessages: 4,
917
+ }
918
+ ```
919
+
649
920
  ### Absorb Copies Zero Nodes
650
921
 
651
922
  Inspect the source memory:
@@ -664,9 +935,27 @@ await targetAgent.absorbFromAgent(sourceAgent, {
664
935
  });
665
936
  ```
666
937
 
938
+ ### Recall Returns Zero Facts
939
+
940
+ `recall()` searches atomic memory nodes, not raw chat messages. If it returns no facts:
941
+
942
+ - Make sure ingestion has completed.
943
+ - Confirm memory nodes exist for the session.
944
+ - Try a lower `minSimilarity`.
945
+ - Try a more specific query that uses the same vocabulary as the original conversation.
946
+
947
+ Example:
948
+
949
+ ```ts
950
+ const result = await agent.recall("Japan travel preferences", {
951
+ minSimilarity: 0.3,
952
+ limit: 5,
953
+ });
954
+ ```
955
+
667
956
  ### Redis Warnings
668
957
 
669
- Redis is only required when you pass `queue` config. If you do not need background ingestion, remove the `queue` section.
958
+ Redis is only required when you pass `queue` or `cache` config. If you do not need background ingestion or recall caching, remove those sections.
670
959
 
671
960
  ### Browser Runtime Error
672
961
 
@@ -682,6 +971,7 @@ Practical notes:
682
971
  - Use PostgreSQL with `pgvector` enabled.
683
972
  - Tune `tokenBudget` to control prompt size and cost.
684
973
  - Use queue mode if ingestion becomes slow.
974
+ - Use the optional recall cache for long sessions with repeated or automatic overflow recall.
685
975
  - Store your own user/session mapping outside MemoGrafter.
686
976
  - Call `close()` during graceful shutdown.
687
977
  - Do not expose database credentials or OpenAI keys to browser code.
@@ -697,10 +987,27 @@ Main exports:
697
987
  - `WorkerAgent`
698
988
  - `ConductorAgent`
699
989
  - `AnthropicLLMAdapter`
990
+ - `GeminiLLMAdapter`
991
+ - `GeminiEmbedAdapter`
700
992
  - `OpenAILLMAdapter`
701
993
  - `OpenAIEmbedAdapter`
994
+ - `PostgresGraphStore`
995
+ - `GrafterPipeline`
996
+ - `IngestPipeline`
997
+ - `RetrieverPipeline`
998
+ - `GraphStore`
999
+ - `FleetAgentRecord`
1000
+ - `RetrievalResult`
1001
+ - `RetrieverConfig`
702
1002
  - public shared and fleet types
703
1003
 
1004
+ Useful `GraphStore` inspection methods:
1005
+
1006
+ - `getTopicNode(topicNodeId, sessionId?)`
1007
+ - `getNodesBySession(sessionId)`
1008
+ - `getSegmentsBySession(sessionId)`
1009
+ - `getEdgesByType(sessionId, type)`
1010
+
704
1011
  Common `MemoGrafterAgent` methods:
705
1012
 
706
1013
  - `initialize()`: initialize storage.
@@ -709,6 +1016,7 @@ Common `MemoGrafterAgent` methods:
709
1016
  - `getSessionId()`: read the current session ID.
710
1017
  - `getActiveNodes()`: inspect topic nodes.
711
1018
  - `getActiveSegments()`: inspect topic segments.
1019
+ - `recall(query, options?)`: retrieve structured memory by semantic query.
712
1020
  - `graft(topicIds?)`: preview memory injection.
713
1021
  - `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
714
1022
  - `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
@@ -1,11 +1,13 @@
1
- import { GraphStore } from "./store/GraphStore.js";
1
+ import { Redis } from "ioredis";
2
2
  import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
3
3
  import type { MemoGrafterFleetOptions } from "./fleet/types.js";
4
+ import type { GraphStore } from "./store/index.js";
4
5
  import type { AbsorbFromAgentOptions, EmbedAdapter, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
5
6
  export declare class MemoGrafter {
6
7
  readonly llm: LLMAdapter;
7
8
  readonly embedder: EmbedAdapter;
8
9
  readonly store: GraphStore;
10
+ readonly recallCache: Redis | null;
9
11
  private readonly ingestPipeline;
10
12
  private readonly grafterPipeline;
11
13
  private readonly ingestQueue;
@@ -1 +1 @@
1
- {"version":3,"file":"MemoGrafter.d.ts","sourceRoot":"","sources":["../src/MemoGrafter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,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,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAErC,MAAM,EAAE,iBAAiB;IA8BrC,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;IAK5B,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,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"}