nlm-memory 0.7.0 → 0.9.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 (40) hide show
  1. package/dist/cli/nlm.js +36 -0
  2. package/dist/cli/nlm.js.map +1 -1
  3. package/dist/core/hook/pointer-block.d.ts +1 -0
  4. package/dist/core/hook/pointer-block.js +7 -1
  5. package/dist/core/hook/pointer-block.js.map +1 -1
  6. package/dist/core/hook/query-extract.d.ts +5 -0
  7. package/dist/core/hook/query-extract.js +36 -0
  8. package/dist/core/hook/query-extract.js.map +1 -0
  9. package/dist/core/hook/select.d.ts +1 -0
  10. package/dist/core/hook/select.js.map +1 -1
  11. package/dist/core/recall/citation-log.d.ts +1 -0
  12. package/dist/core/recall/citation-log.js +37 -0
  13. package/dist/core/recall/citation-log.js.map +1 -1
  14. package/dist/core/recall/precision.d.ts +16 -0
  15. package/dist/core/recall/precision.js +46 -0
  16. package/dist/core/recall/precision.js.map +1 -0
  17. package/dist/core/recall/query-log.d.ts +4 -0
  18. package/dist/core/recall/query-log.js +39 -0
  19. package/dist/core/recall/query-log.js.map +1 -1
  20. package/dist/core/recall/recall-service.js +20 -1
  21. package/dist/core/recall/recall-service.js.map +1 -1
  22. package/dist/core/recall/reranker.d.ts +7 -0
  23. package/dist/core/recall/reranker.js +29 -0
  24. package/dist/core/recall/reranker.js.map +1 -0
  25. package/dist/core/storage/pg-session-store.js +21 -0
  26. package/dist/core/storage/pg-session-store.js.map +1 -1
  27. package/dist/core/storage/sqlite-session-store.js +11 -0
  28. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  29. package/dist/core/storage/supersedence-log.d.ts +5 -0
  30. package/dist/core/storage/supersedence-log.js +34 -1
  31. package/dist/core/storage/supersedence-log.js.map +1 -1
  32. package/dist/hook/recall-over-http.js +6 -1
  33. package/dist/hook/recall-over-http.js.map +1 -1
  34. package/dist/hook/session-start-hook.js +1 -0
  35. package/dist/hook/session-start-hook.js.map +1 -1
  36. package/dist/mcp/server.js +13 -2
  37. package/dist/mcp/server.js.map +1 -1
  38. package/nlm/index.js +151 -3
  39. package/package.json +1 -1
  40. package/plugin/scripts/prompt-recall-hook.mjs +151 -3
package/nlm/index.js CHANGED
@@ -48,12 +48,154 @@ function hookAuthHeaders(extra = {}) {
48
48
  return { ...extra, authorization: `Bearer ${token}` };
49
49
  }
50
50
 
51
+ // src/core/hook/query-extract.ts
52
+ var STOPWORDS = /* @__PURE__ */ new Set([
53
+ "a",
54
+ "an",
55
+ "the",
56
+ "is",
57
+ "are",
58
+ "was",
59
+ "were",
60
+ "be",
61
+ "been",
62
+ "being",
63
+ "have",
64
+ "has",
65
+ "had",
66
+ "do",
67
+ "does",
68
+ "did",
69
+ "will",
70
+ "would",
71
+ "could",
72
+ "should",
73
+ "may",
74
+ "might",
75
+ "shall",
76
+ "can",
77
+ "dare",
78
+ "ought",
79
+ "yes",
80
+ "no",
81
+ "not",
82
+ "please",
83
+ "thank",
84
+ "thanks",
85
+ "ok",
86
+ "okay",
87
+ "i",
88
+ "me",
89
+ "my",
90
+ "we",
91
+ "us",
92
+ "our",
93
+ "you",
94
+ "your",
95
+ "it",
96
+ "its",
97
+ "this",
98
+ "that",
99
+ "these",
100
+ "those",
101
+ "and",
102
+ "or",
103
+ "but",
104
+ "if",
105
+ "so",
106
+ "to",
107
+ "of",
108
+ "in",
109
+ "on",
110
+ "at",
111
+ "by",
112
+ "for",
113
+ "from",
114
+ "with",
115
+ "about",
116
+ "into",
117
+ "through",
118
+ "during",
119
+ "before",
120
+ "after",
121
+ "above",
122
+ "below",
123
+ "up",
124
+ "down",
125
+ "out",
126
+ "off",
127
+ "over",
128
+ "under",
129
+ "again",
130
+ "further",
131
+ "then",
132
+ "once",
133
+ "here",
134
+ "there",
135
+ "when",
136
+ "where",
137
+ "why",
138
+ "how",
139
+ "all",
140
+ "both",
141
+ "each",
142
+ "few",
143
+ "more",
144
+ "most",
145
+ "other",
146
+ "some",
147
+ "such",
148
+ "than",
149
+ "too",
150
+ "very",
151
+ "just",
152
+ "now",
153
+ "also",
154
+ "get",
155
+ "let",
156
+ "what",
157
+ "which",
158
+ "who",
159
+ "whom",
160
+ "whose",
161
+ "any",
162
+ "much",
163
+ "many",
164
+ "sounds",
165
+ "good",
166
+ "great",
167
+ "sure",
168
+ "right",
169
+ "well",
170
+ "done",
171
+ "nice",
172
+ "cool",
173
+ "perfect",
174
+ "exactly",
175
+ "proceed",
176
+ "continue",
177
+ "go",
178
+ "ahead",
179
+ "next",
180
+ "help"
181
+ ]);
182
+ var MIN_CONTENT_WORDS = 2;
183
+ var MIN_WORD_LEN = 3;
184
+ function extractRecallQuery(prompt) {
185
+ const tokens = prompt.trim().split(/\s+/).map((t) => t.replace(/^[^\w-]+|[^\w-]+$/g, "")).filter((t) => t.length >= MIN_WORD_LEN);
186
+ const contentWords = tokens.filter((t) => !STOPWORDS.has(t.toLowerCase()));
187
+ if (contentWords.length < MIN_CONTENT_WORDS) return null;
188
+ return contentWords.join(" ");
189
+ }
190
+
51
191
  // src/hook/recall-over-http.ts
52
192
  var RECALL_LIMIT = 5;
53
193
  var RECALL_TIMEOUT_MS = 2e3;
54
194
  async function recallOverHttp(prompt, runtime) {
195
+ const query = extractRecallQuery(prompt);
196
+ if (query === null) return { hits: [], facts: [] };
55
197
  const portValue = process.env["NLM_PORT"] ?? "3940";
56
- const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(prompt)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true`;
198
+ const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true`;
57
199
  const controller = new AbortController();
58
200
  const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
59
201
  try {
@@ -74,7 +216,8 @@ async function recallOverHttp(prompt, runtime) {
74
216
  id: r.id,
75
217
  label: r.label,
76
218
  startedAt: r.startedAt,
77
- matchScore: r.matchScore
219
+ matchScore: r.matchScore,
220
+ ...r.summary !== void 0 ? { summary: r.summary } : {}
78
221
  }));
79
222
  const facts = (body.relatedFacts ?? []).map((f) => ({
80
223
  subject: f.subject,
@@ -158,7 +301,12 @@ function formatPointerBlock(hits, facts = []) {
158
301
  if (hits.length > 0) {
159
302
  out.push("## Possibly-relevant prior sessions (nlm-memory)");
160
303
  for (const h of hits) {
161
- out.push(`- ${h.id} \xB7 ${h.label} (${h.startedAt.slice(0, 10)})`);
304
+ const datePart = h.startedAt.slice(0, 10);
305
+ if (h.summary) {
306
+ out.push(`- ${h.id} \xB7 ${h.label} (${datePart}) \u2014 ${h.summary.slice(0, 120)}`);
307
+ } else {
308
+ out.push(`- ${h.id} \xB7 ${h.label} (${datePart})`);
309
+ }
162
310
  }
163
311
  }
164
312
  if (facts.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlm-memory",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Local-first non-linear memory operating system for AI operators.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -70,7 +70,12 @@ function formatPointerBlock(hits, facts = []) {
70
70
  if (hits.length > 0) {
71
71
  out.push("## Possibly-relevant prior sessions (nlm-memory)");
72
72
  for (const h of hits) {
73
- out.push(`- ${h.id} \xB7 ${h.label} (${h.startedAt.slice(0, 10)})`);
73
+ const datePart = h.startedAt.slice(0, 10);
74
+ if (h.summary) {
75
+ out.push(`- ${h.id} \xB7 ${h.label} (${datePart}) \u2014 ${h.summary.slice(0, 120)}`);
76
+ } else {
77
+ out.push(`- ${h.id} \xB7 ${h.label} (${datePart})`);
78
+ }
74
79
  }
75
80
  }
76
81
  if (facts.length > 0) {
@@ -148,12 +153,154 @@ function hookAuthHeaders(extra = {}) {
148
153
  return { ...extra, authorization: `Bearer ${token}` };
149
154
  }
150
155
 
156
+ // src/core/hook/query-extract.ts
157
+ var STOPWORDS = /* @__PURE__ */ new Set([
158
+ "a",
159
+ "an",
160
+ "the",
161
+ "is",
162
+ "are",
163
+ "was",
164
+ "were",
165
+ "be",
166
+ "been",
167
+ "being",
168
+ "have",
169
+ "has",
170
+ "had",
171
+ "do",
172
+ "does",
173
+ "did",
174
+ "will",
175
+ "would",
176
+ "could",
177
+ "should",
178
+ "may",
179
+ "might",
180
+ "shall",
181
+ "can",
182
+ "dare",
183
+ "ought",
184
+ "yes",
185
+ "no",
186
+ "not",
187
+ "please",
188
+ "thank",
189
+ "thanks",
190
+ "ok",
191
+ "okay",
192
+ "i",
193
+ "me",
194
+ "my",
195
+ "we",
196
+ "us",
197
+ "our",
198
+ "you",
199
+ "your",
200
+ "it",
201
+ "its",
202
+ "this",
203
+ "that",
204
+ "these",
205
+ "those",
206
+ "and",
207
+ "or",
208
+ "but",
209
+ "if",
210
+ "so",
211
+ "to",
212
+ "of",
213
+ "in",
214
+ "on",
215
+ "at",
216
+ "by",
217
+ "for",
218
+ "from",
219
+ "with",
220
+ "about",
221
+ "into",
222
+ "through",
223
+ "during",
224
+ "before",
225
+ "after",
226
+ "above",
227
+ "below",
228
+ "up",
229
+ "down",
230
+ "out",
231
+ "off",
232
+ "over",
233
+ "under",
234
+ "again",
235
+ "further",
236
+ "then",
237
+ "once",
238
+ "here",
239
+ "there",
240
+ "when",
241
+ "where",
242
+ "why",
243
+ "how",
244
+ "all",
245
+ "both",
246
+ "each",
247
+ "few",
248
+ "more",
249
+ "most",
250
+ "other",
251
+ "some",
252
+ "such",
253
+ "than",
254
+ "too",
255
+ "very",
256
+ "just",
257
+ "now",
258
+ "also",
259
+ "get",
260
+ "let",
261
+ "what",
262
+ "which",
263
+ "who",
264
+ "whom",
265
+ "whose",
266
+ "any",
267
+ "much",
268
+ "many",
269
+ "sounds",
270
+ "good",
271
+ "great",
272
+ "sure",
273
+ "right",
274
+ "well",
275
+ "done",
276
+ "nice",
277
+ "cool",
278
+ "perfect",
279
+ "exactly",
280
+ "proceed",
281
+ "continue",
282
+ "go",
283
+ "ahead",
284
+ "next",
285
+ "help"
286
+ ]);
287
+ var MIN_CONTENT_WORDS = 2;
288
+ var MIN_WORD_LEN = 3;
289
+ function extractRecallQuery(prompt) {
290
+ const tokens = prompt.trim().split(/\s+/).map((t) => t.replace(/^[^\w-]+|[^\w-]+$/g, "")).filter((t) => t.length >= MIN_WORD_LEN);
291
+ const contentWords = tokens.filter((t) => !STOPWORDS.has(t.toLowerCase()));
292
+ if (contentWords.length < MIN_CONTENT_WORDS) return null;
293
+ return contentWords.join(" ");
294
+ }
295
+
151
296
  // src/hook/recall-over-http.ts
152
297
  var RECALL_LIMIT = 5;
153
298
  var RECALL_TIMEOUT_MS = 2e3;
154
299
  async function recallOverHttp(prompt, runtime) {
300
+ const query = extractRecallQuery(prompt);
301
+ if (query === null) return { hits: [], facts: [] };
155
302
  const portValue = process.env["NLM_PORT"] ?? "3940";
156
- const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(prompt)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true`;
303
+ const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true`;
157
304
  const controller = new AbortController();
158
305
  const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
159
306
  try {
@@ -174,7 +321,8 @@ async function recallOverHttp(prompt, runtime) {
174
321
  id: r.id,
175
322
  label: r.label,
176
323
  startedAt: r.startedAt,
177
- matchScore: r.matchScore
324
+ matchScore: r.matchScore,
325
+ ...r.summary !== void 0 ? { summary: r.summary } : {}
178
326
  }));
179
327
  const facts = (body.relatedFacts ?? []).map((f) => ({
180
328
  subject: f.subject,