nlm-memory 0.15.0 → 0.16.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.
- package/dist/cli/nlm.js +8 -1
- package/dist/cli/nlm.js.map +1 -1
- package/dist/core/hook/pointer-block.d.ts +7 -1
- package/dist/core/hook/pointer-block.js +15 -3
- package/dist/core/hook/pointer-block.js.map +1 -1
- package/dist/core/recall/recall-service.d.ts +7 -0
- package/dist/core/recall/recall-service.js +31 -0
- package/dist/core/recall/recall-service.js.map +1 -1
- package/dist/core/recall/related-exemplars.d.ts +14 -0
- package/dist/core/recall/related-exemplars.js +25 -0
- package/dist/core/recall/related-exemplars.js.map +1 -0
- package/dist/hook/prompt-recall-hook.d.ts +2 -1
- package/dist/hook/prompt-recall-hook.js +1 -1
- package/dist/hook/prompt-recall-hook.js.map +1 -1
- package/dist/hook/recall-over-http.d.ts +5 -1
- package/dist/hook/recall-over-http.js +14 -5
- package/dist/hook/recall-over-http.js.map +1 -1
- package/dist/http/app.js +12 -2
- package/dist/http/app.js.map +1 -1
- package/dist/shared/types.d.ts +28 -0
- package/nlm/index.js +24 -11
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/scripts/prompt-recall-hook.mjs +24 -11
- package/plugin/scripts/session-start-hook.mjs +12 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nlm-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Local-first persistent memory for AI coding agents. Recalls relevant prior sessions on every prompt; learns which sessions actually get cited. Owner-edited timeline beats append-only memory layers.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Edward Chalupa",
|
|
@@ -64,8 +64,8 @@ function recordSurfaced(conversationId, ids) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// src/core/hook/pointer-block.ts
|
|
67
|
-
function formatPointerBlock(hits, facts = []) {
|
|
68
|
-
if (hits.length === 0 && facts.length === 0) return "";
|
|
67
|
+
function formatPointerBlock(hits, facts = [], exemplars = []) {
|
|
68
|
+
if (hits.length === 0 && facts.length === 0 && exemplars.length === 0) return "";
|
|
69
69
|
const out = [];
|
|
70
70
|
if (hits.length > 0) {
|
|
71
71
|
out.push("## Possibly-relevant prior sessions (nlm-memory)");
|
|
@@ -86,9 +86,16 @@ function formatPointerBlock(hits, facts = []) {
|
|
|
86
86
|
out.push(`- ${f.subject} ${f.predicate}: ${f.value}${tag}`);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (exemplars.length > 0) {
|
|
90
|
+
if (out.length > 0) out.push("");
|
|
91
|
+
out.push("## Related code exemplars (nlm-memory)");
|
|
92
|
+
for (const e of exemplars) {
|
|
93
|
+
const langPart = e.lang ? `${e.lang} \xB7 ` : "";
|
|
94
|
+
out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext.slice(0, 120)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const tools = exemplars.length > 0 ? "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved), recall_code (pull the full code for a related exemplar)." : "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved).";
|
|
98
|
+
out.push(tools);
|
|
92
99
|
return out.join("\n");
|
|
93
100
|
}
|
|
94
101
|
|
|
@@ -298,9 +305,9 @@ var RECALL_LIMIT = 5;
|
|
|
298
305
|
var RECALL_TIMEOUT_MS = 2e3;
|
|
299
306
|
async function recallOverHttp(prompt, runtime, conversationId) {
|
|
300
307
|
const query = extractRecallQuery(prompt);
|
|
301
|
-
if (query === null) return { hits: [], facts: [] };
|
|
308
|
+
if (query === null) return { hits: [], facts: [], exemplars: [] };
|
|
302
309
|
const portValue = process.env["NLM_PORT"] ?? "3940";
|
|
303
|
-
const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
|
|
310
|
+
const url = `http://localhost:${portValue}/api/recall?q=${encodeURIComponent(query)}&mode=keyword&limit=${RECALL_LIMIT}&withFacts=true&withExemplars=true` + (conversationId ? `&conversation_id=${encodeURIComponent(conversationId)}` : "");
|
|
304
311
|
const controller = new AbortController();
|
|
305
312
|
const timer = setTimeout(() => controller.abort(), RECALL_TIMEOUT_MS);
|
|
306
313
|
try {
|
|
@@ -310,12 +317,12 @@ async function recallOverHttp(prompt, runtime, conversationId) {
|
|
|
310
317
|
headers: hookAuthHeaders(extra),
|
|
311
318
|
signal: controller.signal
|
|
312
319
|
});
|
|
313
|
-
if (!res.ok) return { hits: [], facts: [] };
|
|
320
|
+
if (!res.ok) return { hits: [], facts: [], exemplars: [] };
|
|
314
321
|
let body;
|
|
315
322
|
try {
|
|
316
323
|
body = await res.json();
|
|
317
324
|
} catch {
|
|
318
|
-
return { hits: [], facts: [] };
|
|
325
|
+
return { hits: [], facts: [], exemplars: [] };
|
|
319
326
|
}
|
|
320
327
|
const hits = (body.results ?? []).map((r) => ({
|
|
321
328
|
id: r.id,
|
|
@@ -330,7 +337,13 @@ async function recallOverHttp(prompt, runtime, conversationId) {
|
|
|
330
337
|
value: f.value,
|
|
331
338
|
corroborationCount: f.corroborationCount
|
|
332
339
|
}));
|
|
333
|
-
|
|
340
|
+
const exemplars = (body.relatedExemplars ?? []).map((e) => ({
|
|
341
|
+
outcome: e.outcome,
|
|
342
|
+
lang: e.lang,
|
|
343
|
+
repo: e.repo,
|
|
344
|
+
taskContext: e.taskContext
|
|
345
|
+
}));
|
|
346
|
+
return { hits, facts, exemplars };
|
|
334
347
|
} finally {
|
|
335
348
|
clearTimeout(timer);
|
|
336
349
|
}
|
|
@@ -380,7 +393,7 @@ async function runHook(input, deps) {
|
|
|
380
393
|
perFireCap: PER_FIRE_CAP,
|
|
381
394
|
perConversationCap: PER_CONVERSATION_CAP
|
|
382
395
|
});
|
|
383
|
-
const block = formatPointerBlock(selected, fetched.facts);
|
|
396
|
+
const block = formatPointerBlock(selected, fetched.facts, fetched.exemplars);
|
|
384
397
|
const estTokens = Math.ceil(block.length / 4);
|
|
385
398
|
appendHookLog({
|
|
386
399
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -53,8 +53,8 @@ function recordSurfaced(conversationId, ids) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// src/core/hook/pointer-block.ts
|
|
56
|
-
function formatPointerBlock(hits, facts = []) {
|
|
57
|
-
if (hits.length === 0 && facts.length === 0) return "";
|
|
56
|
+
function formatPointerBlock(hits, facts = [], exemplars = []) {
|
|
57
|
+
if (hits.length === 0 && facts.length === 0 && exemplars.length === 0) return "";
|
|
58
58
|
const out = [];
|
|
59
59
|
if (hits.length > 0) {
|
|
60
60
|
out.push("## Possibly-relevant prior sessions (nlm-memory)");
|
|
@@ -75,9 +75,16 @@ function formatPointerBlock(hits, facts = []) {
|
|
|
75
75
|
out.push(`- ${f.subject} ${f.predicate}: ${f.value}${tag}`);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
if (exemplars.length > 0) {
|
|
79
|
+
if (out.length > 0) out.push("");
|
|
80
|
+
out.push("## Related code exemplars (nlm-memory)");
|
|
81
|
+
for (const e of exemplars) {
|
|
82
|
+
const langPart = e.lang ? `${e.lang} \xB7 ` : "";
|
|
83
|
+
out.push(`- [${e.outcome}] ${langPart}${e.repo} - ${e.taskContext.slice(0, 120)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const tools = exemplars.length > 0 ? "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved), recall_code (pull the full code for a related exemplar)." : "NLM tools: recall_sessions (search), get_session (full transcript), recall_facts (prior decisions), get_fact_history (how a decision evolved).";
|
|
87
|
+
out.push(tools);
|
|
81
88
|
return out.join("\n");
|
|
82
89
|
}
|
|
83
90
|
|