memo-grafter 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/.env.example +1 -0
  2. package/README.md +187 -95
  3. package/USER_GUIDE.md +226 -9
  4. package/dist/MemoGrafter.d.ts +1 -1
  5. package/dist/MemoGrafter.d.ts.map +1 -1
  6. package/dist/MemoGrafter.js +16 -5
  7. package/dist/MemoGrafter.js.map +1 -1
  8. package/dist/MemoGrafterAgent.d.ts +7 -1
  9. package/dist/MemoGrafterAgent.d.ts.map +1 -1
  10. package/dist/MemoGrafterAgent.js +45 -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 +4 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/pipeline/GrafterPipeline.d.ts +1 -2
  26. package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
  27. package/dist/pipeline/GrafterPipeline.js +3 -5
  28. package/dist/pipeline/GrafterPipeline.js.map +1 -1
  29. package/dist/pipeline/IngestPipeline.d.ts +7 -3
  30. package/dist/pipeline/IngestPipeline.d.ts.map +1 -1
  31. package/dist/pipeline/IngestPipeline.js +31 -5
  32. package/dist/pipeline/IngestPipeline.js.map +1 -1
  33. package/dist/pipeline/RetrieverPipeline.d.ts +11 -0
  34. package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -0
  35. package/dist/pipeline/RetrieverPipeline.js +73 -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 +69 -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 +11 -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 +2 -0
  123. package/dist/utils/text/tokenCount.d.ts.map +1 -0
  124. package/dist/utils/text/tokenCount.js +4 -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
@@ -1,95 +1,187 @@
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 conversations into topic-based memory, retrieves the relevant parts 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 tasks, or decide goals for an agent.
9
-
10
- ## What You Can Build
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.
17
-
18
- ## How It Works
19
-
20
- At a high level:
21
-
22
- ```text
23
- chat messages
24
- -> topic segments
25
- -> memory nodes
26
- -> graph links
27
- -> relevant memory injection
28
- -> optional memory grafting
29
- ```
30
-
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.
32
-
33
- ## Install
34
-
35
- ```bash
36
- npm install memo-grafter
37
- ```
38
-
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
- ```
45
-
46
- ## Minimal Example
47
-
48
- ```ts
49
- import "dotenv/config";
50
-
51
- import {
52
- MemoGrafterAgent,
53
- OpenAIEmbedAdapter,
54
- OpenAILLMAdapter,
55
- } from "memo-grafter";
56
-
57
- const agent = new MemoGrafterAgent({
58
- db: { connectionString: process.env.DATABASE_URL! },
59
- llm: new OpenAILLMAdapter("gpt-4o"),
60
- embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
61
- });
62
-
63
- await agent.initialize();
64
-
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
73
-
74
- Memory grafting is the core idea behind the name: one chatbot can build memory, and another chatbot can absorb only the useful parts.
75
-
76
- ```ts
77
- await writingBot.absorbFromAgent(travelBot, {
78
- prompt: "Japan travel preferences",
79
- limit: 3,
80
- });
81
- ```
82
-
83
- ## Learn More
84
-
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
- ```
92
-
93
- ## License
94
-
95
- 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 conversations into topic-based memory, retrieves the relevant parts 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 tasks, or decide goals for an agent.
9
+
10
+ ## What You Can Build
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.
17
+
18
+ ## How It Works
19
+
20
+ At a high level:
21
+
22
+ ```text
23
+ chat messages
24
+ -> topic segments
25
+ -> memory nodes
26
+ -> graph links
27
+ -> relevant memory injection
28
+ -> optional memory grafting
29
+ ```
30
+
31
+ MemoGrafter stores conversation turns, detects topic shifts, summarizes segments into memory nodes, links related nodes, and injects relevant memory into future LLM calls. Drift detection uses embedding distance, sharp message pivots, short-message dampening, and structural phrases like "by the way" or "going back to" to split conversations into useful topic segments. Memory grafting lets one chatbot or session copy selected memory from another.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install memo-grafter
37
+ ```
38
+
39
+ MemoGrafter runs server-side on Node.js. The built-in storage implementation is `PostgresGraphStore`, which requires PostgreSQL with `pgvector`. Included provider adapters require their matching API keys.
40
+
41
+ ```bash
42
+ DATABASE_URL=postgres://postgres:postgres@localhost:5432/memo_grafter
43
+ ANTHROPIC_API_KEY=sk-ant-...
44
+ GEMINI_API_KEY=...
45
+ OPENAI_API_KEY=sk-...
46
+ ```
47
+
48
+ ## Minimal Example
49
+
50
+ ```ts
51
+ import "dotenv/config";
52
+
53
+ import {
54
+ MemoGrafterAgent,
55
+ OpenAIEmbedAdapter,
56
+ OpenAILLMAdapter,
57
+ } from "memo-grafter";
58
+
59
+ const agent = new MemoGrafterAgent({
60
+ db: { connectionString: process.env.DATABASE_URL! },
61
+ llm: new OpenAILLMAdapter("gpt-4o"),
62
+ embedder: new OpenAIEmbedAdapter("text-embedding-3-small"),
63
+ });
64
+
65
+ await agent.initialize();
66
+
67
+ console.log(await agent.invoke("I am planning a Japan trip."));
68
+ console.log(await agent.invoke("I like quiet towns, bookstores, and local cafes."));
69
+ console.log(await agent.invoke("What do you remember about my travel preferences?"));
70
+
71
+ const recall = await agent.recall("travel preferences", {
72
+ limit: 5,
73
+ minSimilarity: 0.6,
74
+ });
75
+
76
+ console.log(recall.facts);
77
+ console.log(recall.systemPrompt);
78
+
79
+ await agent.close();
80
+ ```
81
+
82
+ ## Adapters
83
+
84
+ MemoGrafter includes provider adapters such as `OpenAILLMAdapter`, `OpenAIEmbedAdapter`, `AnthropicLLMAdapter`, `GeminiLLMAdapter`, and `GeminiEmbedAdapter`. You can also bring any provider by implementing the public adapter interfaces:
85
+
86
+ ```ts
87
+ import {
88
+ type EmbedAdapter,
89
+ type LLMAdapter,
90
+ type Message,
91
+ } from "memo-grafter";
92
+
93
+ class MyLLMAdapter implements LLMAdapter {
94
+ async complete(messages: Message[], system?: string): Promise<string> {
95
+ // Call your model provider here.
96
+ return "Assistant response";
97
+ }
98
+ }
99
+
100
+ class MyEmbedAdapter implements EmbedAdapter {
101
+ async embed(text: string): Promise<number[]> {
102
+ // Return an embedding vector matching your storage schema.
103
+ return [];
104
+ }
105
+ }
106
+ ```
107
+
108
+ ## Storage
109
+
110
+ MemoGrafter uses a public `GraphStore` interface internally. The default implementation is `PostgresGraphStore`, backed by PostgreSQL and `pgvector`, and this is what `MemoGrafter` and `MemoGrafterAgent` construct from the `db.connectionString` config today.
111
+
112
+ ```ts
113
+ import {
114
+ PostgresGraphStore,
115
+ type GraphStore,
116
+ } from "memo-grafter";
117
+
118
+ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
119
+ ```
120
+
121
+ The interface boundary keeps the Postgres implementation isolated and gives future storage backends a clear contract to implement.
122
+
123
+ ## Targeted Recall
124
+
125
+ Use `agent.recall()` when you want structured memory back without asking the LLM to answer yet. It searches atomic memory nodes by meaning, filters stale memories, groups them with their parent topic summaries, and returns a prompt block you can inspect or pass to your own model call.
126
+
127
+ ```ts
128
+ const result = await agent.recall("deployment config", {
129
+ limit: 8,
130
+ minSimilarity: 0.55,
131
+ tokenBudget: 1000,
132
+ });
133
+
134
+ console.log(result.facts);
135
+ console.log(result.nodes);
136
+ console.log(result.systemPrompt);
137
+ console.log(result.tokenCount);
138
+ ```
139
+
140
+ `recall()` is side-effect free. It does not call `invoke()`, does not mutate chat history, and does not inject anything automatically.
141
+
142
+ ## Memory Grafting
143
+
144
+ Memory grafting is the core idea behind the name: one chatbot can build memory, and another chatbot can absorb only the useful parts.
145
+
146
+ ```ts
147
+ await writingBot.absorbFromAgent(travelBot, {
148
+ prompt: "Japan travel preferences",
149
+ limit: 3,
150
+ });
151
+ ```
152
+
153
+ ## Drift And Reentry
154
+
155
+ Use `driftSensitivity` for developer-friendly topic segmentation:
156
+
157
+ ```ts
158
+ const agent = new MemoGrafterAgent({
159
+ db: { connectionString: process.env.DATABASE_URL! },
160
+ llm,
161
+ embedder,
162
+ drift: {
163
+ mode: "intent",
164
+ driftSensitivity: "medium",
165
+ minSegmentMessages: 3,
166
+ reentryDetection: true,
167
+ },
168
+ });
169
+ ```
170
+
171
+ Sensitivity presets are `"low"`, `"medium"`, and `"high"`. The older numeric `threshold` option is still accepted for compatibility, but `driftSensitivity` is preferred.
172
+
173
+ When a conversation returns to an earlier topic, MemoGrafter can create a `reentry` edge between the new topic node and the earlier related topic node. For example, a chat can move from database decisions to authentication, then back to database pooling; the later database segment is linked back to the original database topic instead of becoming an isolated duplicate.
174
+
175
+ ## Learn More
176
+
177
+ 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.
178
+
179
+ This repository also includes a runnable demo:
180
+
181
+ ```text
182
+ examples/chatbot-memory-demo
183
+ ```
184
+
185
+ ## License
186
+
187
+ MIT
package/USER_GUIDE.md CHANGED
@@ -12,11 +12,12 @@ 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
+ - A Gemini API key only if using the included Gemini adapters.
20
21
  - Redis only if enabling queue mode.
21
22
 
22
23
  MemoGrafter is server-side only. Do not run it in browser code.
@@ -51,16 +52,19 @@ 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
 
66
+ `GEMINI_API_KEY` is required only when using `GeminiLLMAdapter` or `GeminiEmbedAdapter`.
67
+
64
68
  `REDIS_URL` is optional and only needed when you pass `queue` config.
65
69
 
66
70
  Enable `pgvector` in PostgreSQL:
@@ -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
 
@@ -140,6 +146,14 @@ export interface Message {
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
 
@@ -226,6 +260,40 @@ On every call, `invoke()`:
226
260
 
227
261
  On the first turn there may be no memory to inject. Later turns can use memory created from earlier turns.
228
262
 
263
+ ### Targeted Recall
264
+
265
+ Use `recall()` when you want to retrieve structured memory by meaning without asking the LLM to produce an answer.
266
+
267
+ ```ts
268
+ const result = await agent.recall("deployment config", {
269
+ limit: 8,
270
+ minSimilarity: 0.55,
271
+ tokenBudget: 1000,
272
+ });
273
+
274
+ console.log(result.facts);
275
+ console.log(result.nodes);
276
+ console.log(result.systemPrompt);
277
+ console.log(result.tokenCount);
278
+ ```
279
+
280
+ `recall()` returns a `RetrievalResult`:
281
+
282
+ - `facts`: matching memory nodes with a `similarity` score.
283
+ - `nodes`: parent topic nodes for the included facts.
284
+ - `systemPrompt`: a formatted memory block that can be passed to an LLM if you choose.
285
+ - `tokenCount`: approximate token count for the included fact blocks.
286
+
287
+ Options:
288
+
289
+ - `limit`: max memory nodes to fetch before filtering. Defaults to `10`.
290
+ - `minSimilarity`: cosine similarity floor. Defaults to `0.6`.
291
+ - `tokenBudget`: max approximate tokens for included fact blocks. Defaults to `1200`.
292
+
293
+ `recall()` is side-effect free. It does not call `invoke()`, does not trigger a new LLM completion, does not mutate local history, and does not inject the result automatically. Your application decides whether to display the memories, add `result.systemPrompt` to a model call, or ignore the result.
294
+
295
+ 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.
296
+
229
297
  ## Inspecting Memory
230
298
 
231
299
  Read active topic nodes:
@@ -368,8 +436,11 @@ const agent = new MemoGrafterAgent({
368
436
  drift: {
369
437
  mode: "intent",
370
438
  windowSize: 5,
371
- threshold: 0.3,
439
+ driftSensitivity: "medium",
372
440
  minSegmentMessages: 3,
441
+ llmAmbiguityDetection: false,
442
+ reentryDetection: true,
443
+ reentryThreshold: 0.85,
373
444
  },
374
445
  graph: {
375
446
  topK: 5,
@@ -390,7 +461,27 @@ db: {
390
461
  }
391
462
  ```
392
463
 
393
- PostgreSQL connection string.
464
+ PostgreSQL connection string used by the built-in `PostgresGraphStore`.
465
+
466
+ MemoGrafter currently constructs `PostgresGraphStore` from this config internally. Advanced users can also import the storage contract directly:
467
+
468
+ ```ts
469
+ import {
470
+ PostgresGraphStore,
471
+ type GraphStore,
472
+ } from "memo-grafter";
473
+
474
+ const store: GraphStore = new PostgresGraphStore(process.env.DATABASE_URL!);
475
+ ```
476
+
477
+ `GraphStore` is the public storage interface. `PostgresGraphStore` is the default PostgreSQL and pgvector implementation.
478
+
479
+ Useful store inspection methods include:
480
+
481
+ - `getNodesBySession(sessionId)`: read topic nodes for a session.
482
+ - `getTopicNode(topicNodeId, sessionId?)`: read one topic node by ID.
483
+ - `getSegmentsBySession(sessionId)`: read topic segments for a session.
484
+ - `getEdgesByType(sessionId, type)`: inspect graph edges such as `"reentry"`, `"semantic"`, `"temporal"`, or `"grafted"`.
394
485
 
395
486
  ### `llm`
396
487
 
@@ -406,6 +497,12 @@ Anthropic models can be used with the included Anthropic adapter:
406
497
  llm: new AnthropicLLMAdapter("claude-sonnet-4-5")
407
498
  ```
408
499
 
500
+ Gemini models can be used with the included Gemini adapter:
501
+
502
+ ```ts
503
+ llm: new GeminiLLMAdapter("gemini-2.5-flash")
504
+ ```
505
+
409
506
  ### `embedder`
410
507
 
411
508
  ```ts
@@ -416,14 +513,25 @@ Adapter used to create vectors for semantic search.
416
513
 
417
514
  Anthropic does not provide a native embedding API. Pair `AnthropicLLMAdapter` with `OpenAIEmbedAdapter` or another custom `EmbedAdapter`.
418
515
 
516
+ Gemini embeddings can be used with the included Gemini embedder:
517
+
518
+ ```ts
519
+ embedder: new GeminiEmbedAdapter("gemini-embedding-001")
520
+ ```
521
+
522
+ `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.
523
+
419
524
  ### `drift`
420
525
 
421
526
  ```ts
422
527
  drift: {
423
528
  mode: "intent",
424
529
  windowSize: 5,
425
- threshold: 0.3,
530
+ driftSensitivity: "medium",
426
531
  minSegmentMessages: 3,
532
+ llmAmbiguityDetection: false,
533
+ reentryDetection: true,
534
+ reentryThreshold: 0.85,
427
535
  }
428
536
  ```
429
537
 
@@ -431,11 +539,39 @@ Controls topic boundary detection.
431
539
 
432
540
  - `mode`: `"intent"` or `"window"`.
433
541
  - `windowSize`: message window size for window mode.
434
- - `threshold`: drift sensitivity.
542
+ - `driftSensitivity`: preferred sensitivity preset, one of `"low"`, `"medium"`, or `"high"`.
543
+ - `threshold`: deprecated numeric threshold. It still works when `driftSensitivity` is not set, but MemoGrafter logs a one-time warning.
435
544
  - `minSegmentMessages`: minimum messages before a boundary.
545
+ - `llmAmbiguityDetection`: optional LLM check for borderline topic shifts. Defaults to `false`.
546
+ - `reentryDetection`: whether to link later topic returns back to earlier topic nodes. Defaults to `true`.
547
+ - `reentryThreshold`: embedding similarity threshold for reentry detection. Defaults to `0.85`.
436
548
 
437
549
  Use `"intent"` for most chatbot memory demos. In intent mode, user messages drive topic shifts.
438
550
 
551
+ Sensitivity presets resolve internally to numeric thresholds:
552
+
553
+ - `"low"`: `0.25`
554
+ - `"medium"`: `0.35`
555
+ - `"high"`: `0.50`
556
+
557
+ 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.
558
+
559
+ If both `driftSensitivity` and `threshold` are provided, `driftSensitivity` wins.
560
+
561
+ #### Reentry Detection
562
+
563
+ Reentry detection handles conversations that leave a topic and later return to it:
564
+
565
+ ```text
566
+ database choice -> authentication flow -> database connection pooling
567
+ ```
568
+
569
+ 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.
570
+
571
+ 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.
572
+
573
+ Reentry edges are written between the current rebuilt topic nodes. They do not point at deleted nodes from previous ingestion passes.
574
+
439
575
  ### `graph`
440
576
 
441
577
  ```ts
@@ -531,6 +667,34 @@ const agent = new MemoGrafterAgent({
531
667
 
532
668
  Your embedding vector dimension must match the vector dimension expected by the database schema.
533
669
 
670
+ ## Storage Backends
671
+
672
+ 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`.
673
+
674
+ Most users do not need to instantiate a store directly. `MemoGrafter` and `MemoGrafterAgent` use `PostgresGraphStore` from the `db.connectionString` config:
675
+
676
+ ```ts
677
+ const agent = new MemoGrafterAgent({
678
+ db: {
679
+ connectionString: process.env.DATABASE_URL!,
680
+ },
681
+ llm,
682
+ embedder,
683
+ });
684
+ ```
685
+
686
+ If you are extending MemoGrafter, use `GraphStore` as the contract and keep concrete storage details behind an implementation:
687
+
688
+ ```ts
689
+ import type { GraphStore } from "memo-grafter";
690
+
691
+ class MyGraphStore implements GraphStore {
692
+ // Implement the full GraphStore contract.
693
+ }
694
+ ```
695
+
696
+ 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.
697
+
534
698
  ## Fleet API
535
699
 
536
700
  Fleets let you group color-scoped worker chatbots and use a conductor to graft memory across workers.
@@ -641,11 +805,31 @@ For small demos, try:
641
805
  ```ts
642
806
  drift: {
643
807
  mode: "intent",
644
- threshold: 0.3,
808
+ driftSensitivity: "medium",
645
809
  minSegmentMessages: 3,
646
810
  }
647
811
  ```
648
812
 
813
+ If segments are too coarse, try a lower resolved threshold:
814
+
815
+ ```ts
816
+ drift: {
817
+ mode: "intent",
818
+ driftSensitivity: "low",
819
+ minSegmentMessages: 2,
820
+ }
821
+ ```
822
+
823
+ If segments are too fragmented, try a higher resolved threshold:
824
+
825
+ ```ts
826
+ drift: {
827
+ mode: "intent",
828
+ driftSensitivity: "high",
829
+ minSegmentMessages: 4,
830
+ }
831
+ ```
832
+
649
833
  ### Absorb Copies Zero Nodes
650
834
 
651
835
  Inspect the source memory:
@@ -664,6 +848,24 @@ await targetAgent.absorbFromAgent(sourceAgent, {
664
848
  });
665
849
  ```
666
850
 
851
+ ### Recall Returns Zero Facts
852
+
853
+ `recall()` searches atomic memory nodes, not raw chat messages. If it returns no facts:
854
+
855
+ - Make sure ingestion has completed.
856
+ - Confirm memory nodes exist for the session.
857
+ - Try a lower `minSimilarity`.
858
+ - Try a more specific query that uses the same vocabulary as the original conversation.
859
+
860
+ Example:
861
+
862
+ ```ts
863
+ const result = await agent.recall("Japan travel preferences", {
864
+ minSimilarity: 0.3,
865
+ limit: 5,
866
+ });
867
+ ```
868
+
667
869
  ### Redis Warnings
668
870
 
669
871
  Redis is only required when you pass `queue` config. If you do not need background ingestion, remove the `queue` section.
@@ -697,10 +899,24 @@ Main exports:
697
899
  - `WorkerAgent`
698
900
  - `ConductorAgent`
699
901
  - `AnthropicLLMAdapter`
902
+ - `GeminiLLMAdapter`
903
+ - `GeminiEmbedAdapter`
700
904
  - `OpenAILLMAdapter`
701
905
  - `OpenAIEmbedAdapter`
906
+ - `PostgresGraphStore`
907
+ - `GraphStore`
908
+ - `FleetAgentRecord`
909
+ - `RetrievalResult`
910
+ - `RetrieverConfig`
702
911
  - public shared and fleet types
703
912
 
913
+ Useful `GraphStore` inspection methods:
914
+
915
+ - `getTopicNode(topicNodeId, sessionId?)`
916
+ - `getNodesBySession(sessionId)`
917
+ - `getSegmentsBySession(sessionId)`
918
+ - `getEdgesByType(sessionId, type)`
919
+
704
920
  Common `MemoGrafterAgent` methods:
705
921
 
706
922
  - `initialize()`: initialize storage.
@@ -709,6 +925,7 @@ Common `MemoGrafterAgent` methods:
709
925
  - `getSessionId()`: read the current session ID.
710
926
  - `getActiveNodes()`: inspect topic nodes.
711
927
  - `getActiveSegments()`: inspect topic segments.
928
+ - `recall(query, options?)`: retrieve structured memory by semantic query.
712
929
  - `graft(topicIds?)`: preview memory injection.
713
930
  - `ingestGraftedNodes(nodes)`: copy provided nodes into this agent.
714
931
  - `absorbFromAgent(sourceAgent, options)`: select and copy memory from another agent.
@@ -1,6 +1,6 @@
1
- import { GraphStore } from "./store/GraphStore.js";
2
1
  import { MemoGrafterFleet } from "./fleet/MemoGrafterFleet.js";
3
2
  import type { MemoGrafterFleetOptions } from "./fleet/types.js";
3
+ import type { GraphStore } from "./store/index.js";
4
4
  import type { AbsorbFromAgentOptions, EmbedAdapter, InjectionResult, LLMAdapter, MemoGrafterConfig, Message, TopicNode, TopicSegment } from "./types.js";
5
5
  export declare class MemoGrafter {
6
6
  readonly llm: LLMAdapter;