@lotargo/memory_plugin 1.6.6 → 1.6.8

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 (48) hide show
  1. package/CHANGELOG.md +148 -101
  2. package/README.md +436 -304
  3. package/mcp-server/cli/direct_commands.js +39 -0
  4. package/mcp-server/cli.js +16 -5
  5. package/mcp-server/cli_boot.js +4 -1
  6. package/mcp-server/client_cli.js +73 -0
  7. package/mcp-server/client_paths.js +44 -0
  8. package/mcp-server/client_registration.js +38 -0
  9. package/mcp-server/codex_config.js +86 -8
  10. package/mcp-server/db/database.js +14 -21
  11. package/mcp-server/db/migrations.js +66 -77
  12. package/mcp-server/db/rag_blob_transport.js +143 -0
  13. package/mcp-server/db/rag_sync.js +284 -0
  14. package/mcp-server/db/sync_queue.js +219 -307
  15. package/mcp-server/dev_link.js +142 -0
  16. package/mcp-server/fact_format.js +44 -12
  17. package/mcp-server/index.js +17 -7
  18. package/mcp-server/ingest/exporter.js +44 -38
  19. package/mcp-server/ingest/normalizer.js +1 -1
  20. package/mcp-server/ingest/pipeline.js +260 -248
  21. package/mcp-server/persona_migration.js +39 -0
  22. package/mcp-server/prompt_manager.js +162 -55
  23. package/mcp-server/retrieval/retriever.js +99 -64
  24. package/mcp-server/setup.js +150 -100
  25. package/mcp-server/storage/blob_store.js +53 -1
  26. package/mcp-server/tools/core/knowledge_read_core.js +163 -0
  27. package/mcp-server/tools/core/memory_core.js +24 -4
  28. package/mcp-server/tools/core/memory_routing.js +10 -0
  29. package/mcp-server/tools/core/note_core.js +53 -0
  30. package/mcp-server/tools/core/rag_query_core.js +169 -0
  31. package/mcp-server/tools/index.js +11 -9
  32. package/mcp-server/tools/memory_tools.js +4 -1
  33. package/mcp-server/tools/note_tools.js +35 -0
  34. package/mcp-server/tools/rag_tools.js +211 -364
  35. package/mcp-server/uninstall.js +627 -0
  36. package/opencode-plugin/index.js +80 -12
  37. package/opencode-plugin/main.js +136 -0
  38. package/package.json +25 -5
  39. package/skills/using-memory/SKILL.md +28 -19
  40. package/mcp-server/benchmarks/fetch_real_corpus.js +0 -351
  41. package/mcp-server/benchmarks/gpu_profile_benchmark.js +0 -170
  42. package/mcp-server/benchmarks/policy_dominance_test.js +0 -221
  43. package/mcp-server/benchmarks/quality_evaluator.js +0 -598
  44. package/mcp-server/benchmarks/raw_corpus_data.js +0 -613
  45. package/mcp-server/benchmarks/run_benchmarks.js +0 -366
  46. package/mcp-server/benchmarks/stress_ingestion.js +0 -195
  47. package/mcp-server/benchmarks/table_code_retrieval.js +0 -453
  48. package/mcp-server/benchmarks/test_dual_layer.js +0 -141
@@ -1,221 +0,0 @@
1
- import { tmpdir } from "node:os";
2
- import { join } from "node:path";
3
- import { rmSync, existsSync } from "node:fs";
4
- import { getDatabase } from "../db/database.js";
5
- import { ingestDocument } from "../ingest/pipeline.js";
6
- import { embedText } from "../ml/model_manager.js";
7
- import { hybridQuery } from "../retrieval/retriever.js";
8
- import { RAW_CORPUS, POLICY_DOMINANCE_QUERIES } from "./raw_corpus_data.js";
9
-
10
- const K = 5;
11
-
12
- function chunkTypeLabel(retrievalPolicy) {
13
- if (retrievalPolicy === "table_summary") return "TABLE";
14
- if (retrievalPolicy === "code_signature") return "CODE";
15
- return "MICRO";
16
- }
17
-
18
- async function ingestCorpus(db, blobDir) {
19
- let totalSections = 0;
20
- let totalMicro = 0;
21
-
22
- for (const doc of RAW_CORPUS) {
23
- const res = await ingestDocument({
24
- content: doc.content,
25
- type: "text",
26
- title: doc.title,
27
- path: `benchmark://${doc.id}`,
28
- generateEmbeddings: true,
29
- customDb: db,
30
- customBlobDir: blobDir,
31
- });
32
- totalSections += res.sectionsCount;
33
- totalMicro += res.microChunksCount;
34
- if (global.gc) global.gc();
35
- }
36
-
37
- const policyRow = await db.prepare(
38
- `SELECT COUNT(*) as cnt FROM micro_chunks WHERE retrieval_policy IN ('table_summary', 'code_signature')`
39
- ).get();
40
- const totalPolicy = policyRow?.cnt || 0;
41
-
42
- return { totalSections, totalMicro, totalPolicy };
43
- }
44
-
45
- async function runMode(db, query, mode, alpha = 0.5) {
46
- const hits = await hybridQuery({
47
- query: query.query,
48
- limit: K,
49
- customDb: db,
50
- fusionAlgorithm: mode,
51
- alpha,
52
- generateEmbeddings: true,
53
- includeGraphContext: false,
54
- });
55
- return hits;
56
- }
57
-
58
- function analyzeChunkTypes(hits) {
59
- let policyCount = 0;
60
- let microCount = 0;
61
- const types = [];
62
- for (const hit of hits) {
63
- const isPolicy = hit.retrieval_policy === "table_summary" || hit.retrieval_policy === "code_signature";
64
- if (isPolicy) {
65
- policyCount++;
66
- types.push(chunkTypeLabel(hit.retrieval_policy));
67
- } else {
68
- microCount++;
69
- types.push("MICRO");
70
- }
71
- }
72
- return { policyCount, microCount, types };
73
- }
74
-
75
- function renderResultsTable(results) {
76
- const wQ = 42;
77
- const wMode = 12;
78
- const wTypes = 32;
79
- const wWinner = 8;
80
-
81
- const sep = `├${"─".repeat(wQ + 2)}┼${"─".repeat(wMode + 2)}┼${"─".repeat(wTypes + 2)}┼${"─".repeat(wWinner + 2)}┤`;
82
- const top = `┌${"─".repeat(wQ + 2)}┬${"─".repeat(wMode + 2)}┬${"─".repeat(wTypes + 2)}┬${"─".repeat(wWinner + 2)}┐`;
83
- const bot = `└${"─".repeat(wQ + 2)}┴${"─".repeat(wMode + 2)}┴${"─".repeat(wTypes + 2)}┴${"─".repeat(wWinner + 2)}┘`;
84
-
85
- console.log(`\x1b[36m${top}\x1b[0m`);
86
- console.log(`\x1b[36m│\x1b[0m \x1b[1m${"Query".padEnd(wQ)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Mode".padEnd(wMode)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Chunk Types (top-5)".padEnd(wTypes)}\x1b[0m \x1b[36m│\x1b[0m \x1b[1m${"Winner".padEnd(wWinner)}\x1b[0m \x1b[36m│\x1b[0m`);
87
- console.log(`\x1b[36m${sep}\x1b[0m`);
88
-
89
- for (const r of results) {
90
- const qShort = r.query.length > wQ ? r.query.substring(0, wQ - 3) + "..." : r.query;
91
- const typesStr = r.types.join(", ") || "(none)";
92
- const winner = r.policyCount > r.microCount ? "\x1b[33mPOLICY\x1b[0m" : r.microCount > 0 ? "\x1b[32mMICRO\x1b[0m" : "—";
93
- console.log(
94
- `\x1b[36m│\x1b[0m ${qShort.padEnd(wQ)} \x1b[36m│\x1b[0m ${r.mode.padEnd(wMode)} \x1b[36m│\x1b[0m ${typesStr.padEnd(wTypes)} \x1b[36m│\x1b[0m ${winner.padEnd(wWinner)} \x1b[36m│\x1b[0m`
95
- );
96
- }
97
- console.log(`\x1b[36m${bot}\x1b[0m`);
98
- }
99
-
100
- async function main() {
101
- const line = "─".repeat(60);
102
- console.log(`\x1b[36m╭${line}╮\x1b[0m`);
103
- console.log(`\x1b[36m│\x1b[0m \x1b[1m\x1b[37mPOLICY DOMINANCE BENCHMARK\x1b[0m`.padEnd(72) + `\x1b[36m│\x1b[0m`);
104
- console.log(`\x1b[36m│\x1b[0m \x1b[90mRaw docs: tables + code + text | chunk-type distribution\x1b[0m`.padEnd(72) + `\x1b[36m│\x1b[0m`);
105
- console.log(`\x1b[36m╰${line}╯\x1b[0m`);
106
-
107
- const TEST_DIR = join(tmpdir(), `memory_policy_bench_${Date.now()}`);
108
- const TEST_DB_PATH = join(TEST_DIR, "bench_memory.sqlite");
109
- const TEST_BLOB_DIR = join(TEST_DIR, "blobs");
110
-
111
- const db = await getDatabase(TEST_DB_PATH);
112
-
113
- console.log("\n[INGEST] Processing raw corpus...");
114
- const ingestMeta = await ingestCorpus(db, TEST_BLOB_DIR);
115
- console.log(` Sections: ${ingestMeta.totalSections}, Micro-chunks: ${ingestMeta.totalMicro}, Policy chunks: ${ingestMeta.totalPolicy}`);
116
-
117
- const modes = ["bm25_only", "vector_only", "rrf", "rsf"];
118
- const allResults = [];
119
-
120
- // Per-mode aggregation
121
- const modeStats = {};
122
- for (const mode of modes) modeStats[mode] = { policy: 0, micro: 0, total: 0 };
123
-
124
- // Per-category aggregation
125
- const catStats = {};
126
-
127
- console.log("\n[EVAL] Running queries across modes...");
128
- for (const q of POLICY_DOMINANCE_QUERIES) {
129
- const cat = q.expectedDocIds[0].split("_")[0];
130
- if (!catStats[cat]) catStats[cat] = { policy: 0, micro: 0, total: 0 };
131
-
132
- for (const mode of modes) {
133
- const hits = await runMode(db, q, mode);
134
- const analysis = analyzeChunkTypes(hits);
135
-
136
- allResults.push({
137
- query: q.query,
138
- mode,
139
- types: analysis.types,
140
- policyCount: analysis.policyCount,
141
- microCount: analysis.microCount,
142
- expectedWinner: q.expectedWinner,
143
- queryType: q.query_type,
144
- });
145
-
146
- modeStats[mode].policy += analysis.policyCount;
147
- modeStats[mode].micro += analysis.microCount;
148
- modeStats[mode].total += hits.length;
149
-
150
- catStats[cat].policy += analysis.policyCount;
151
- catStats[cat].micro += analysis.microCount;
152
- catStats[cat].total += hits.length;
153
- }
154
- }
155
-
156
- // Render detailed table
157
- console.log("\n[RESULTS] Chunk-type distribution per query per mode:");
158
- renderResultsTable(allResults);
159
-
160
- // Render mode summary
161
- console.log("\n[SUMMARY] Aggregate chunk-type ratio per mode:");
162
- console.log("┌────────────┬─────────┬─────────┬──────────┐");
163
- console.log("│ Mode │ Policy │ Micro │ Policy % │");
164
- console.log("├────────────┼─────────┼─────────┼──────────┤");
165
- for (const mode of modes) {
166
- const s = modeStats[mode];
167
- const pct = s.total > 0 ? ((s.policy / s.total) * 100).toFixed(1) : "0.0";
168
- console.log(`│ ${mode.padEnd(10)} │ ${String(s.policy).padStart(7)} │ ${String(s.micro).padStart(7)} │ ${pct.padStart(7)}% │`);
169
- }
170
- console.log("└────────────┴─────────┴─────────┴──────────┘");
171
-
172
- // Render category summary
173
- console.log("\n[CATEGORY] Chunk-type ratio by document category:");
174
- console.log("┌──────────────────┬─────────┬─────────┬──────────┐");
175
- console.log("│ Category │ Policy │ Micro │ Policy % │");
176
- console.log("├──────────────────┼─────────┼─────────┼──────────┤");
177
- for (const [cat, s] of Object.entries(catStats)) {
178
- const pct = s.total > 0 ? ((s.policy / s.total) * 100).toFixed(1) : "0.0";
179
- console.log(`│ ${cat.padEnd(16)} │ ${String(s.policy).padStart(7)} │ ${String(s.micro).padStart(7)} │ ${pct.padStart(7)}% │`);
180
- }
181
- console.log("└──────────────────┴─────────┴─────────┴──────────┘");
182
-
183
- // Diagnosis
184
- console.log("\n[DIAGNOSIS]");
185
- const allPolicy = Object.values(modeStats).reduce((a, m) => a + m.policy, 0);
186
- const allMicro = Object.values(modeStats).reduce((a, m) => a + m.micro, 0);
187
- const allTotal = allPolicy + allMicro;
188
- const globalPct = allTotal > 0 ? ((allPolicy / allTotal) * 100).toFixed(1) : "0.0";
189
- console.log(` Global policy dominance: ${globalPct}% (${allPolicy}/${allTotal} chunks)`);
190
-
191
- if (Number(globalPct) > 80) {
192
- console.log(" ⚠️ Policy chunks dominate — benchmark confirms the issue.");
193
- console.log(" → Consider: separate policy/micro slots (Option A) or per-source dedup (Option B)");
194
- } else if (Number(globalPct) < 40) {
195
- console.log(" ✅ Micro chunks dominate — policy expansion is conservative.");
196
- } else {
197
- console.log(" ✅ Balanced distribution — both chunk types compete fairly.");
198
- }
199
-
200
- // Check if modes differ
201
- const modePcts = modes.map(m => {
202
- const s = modeStats[m];
203
- return s.total > 0 ? (s.policy / s.total) : 0;
204
- });
205
- const modeSpread = Math.max(...modePcts) - Math.min(...modePcts);
206
- console.log(` Mode spread: ${(modeSpread * 100).toFixed(1)}% (max policy% - min policy% across modes)`);
207
- if (modeSpread < 0.1) {
208
- console.log(" ⚠️ All modes produce similar distributions — modes are not differentiated.");
209
- } else {
210
- console.log(" ✅ Modes produce different distributions — benchmark can distinguish them.");
211
- }
212
-
213
- // Cleanup
214
- try { db.close(); } catch {}
215
- if (existsSync(TEST_DIR)) rmSync(TEST_DIR, { recursive: true, force: true });
216
- }
217
-
218
- main().catch(err => {
219
- console.error("Benchmark failed:", err);
220
- process.exit(1);
221
- });