@modusensus/dsh-mneme 0.6.7 → 0.6.9
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/.github/workflows/test.yml +32 -0
- package/.release-notes-v0.6.9.md +13 -0
- package/CHANGELOG.md +89 -0
- package/LICENSE +21 -21
- package/README.md +219 -463
- package/SECURITY.md +544 -0
- package/docs/devlog/2026-08-14-dsh-mneme-dev-log.md +247 -0
- package/docs/devlog/2026-08-15-dsh-mneme-audit-stress-dev-log.md +145 -0
- package/docs/devlog/2026-08-15-dsh-mneme-pipeline-dev-log.md +56 -0
- package/docs/devlog/2026-08-15-dsh-mneme-reflection-dev-log.md +77 -0
- package/docs/devlog/2026-08-15-dsh-mneme-review-fixes-dev-log.md +64 -0
- package/docs/devlog/2026-08-15-dsh-mneme-semantic-dev-log.md +90 -0
- package/dsh-mneme/CHANGELOG.md +248 -0
- package/dsh-mneme/LICENSE +21 -0
- package/dsh-mneme/README.md +465 -0
- package/{cordis.patch.yml → dsh-mneme/cordis.patch.yml} +15 -15
- package/dsh-mneme/docs/AGENT_MEMORY_RESEARCH.md +183 -0
- package/dsh-mneme/docs/ENTITIES.md +245 -0
- package/dsh-mneme/docs/LOCAL_MODEL.md +141 -0
- package/dsh-mneme/docs/MIGRATION.md +127 -0
- package/dsh-mneme/docs/SEMANTIC.md +256 -0
- package/dsh-mneme/docs/SLEEP.md +163 -0
- package/{lib → dsh-mneme/lib}/api.js +783 -783
- package/{lib → dsh-mneme/lib}/client.js +1757 -1757
- package/{src → dsh-mneme/lib}/commands.js +64 -64
- package/{lib → dsh-mneme/lib}/config.js +298 -288
- package/{lib → dsh-mneme/lib}/dream/clustering.js +118 -118
- package/{src → dsh-mneme/lib}/dream/decisions.js +488 -439
- package/{lib → dsh-mneme/lib}/dream/sleep.js +561 -554
- package/{src → dsh-mneme/lib}/dream/tag-extractor.js +156 -156
- package/{lib → dsh-mneme/lib}/dream.js +958 -929
- package/{src → dsh-mneme/lib}/embedding.js +154 -154
- package/{src → dsh-mneme/lib}/entities/extractor.js +242 -242
- package/{src → dsh-mneme/lib}/hot-memory.js +53 -53
- package/{lib → dsh-mneme/lib}/index.js +361 -361
- package/{src → dsh-mneme/lib}/inject.js +208 -208
- package/{lib → dsh-mneme/lib}/local-embedder.js +282 -282
- package/{lib → dsh-mneme/lib}/mirror.js +170 -170
- package/{lib → dsh-mneme/lib}/parser/tag.js +59 -59
- package/{lib → dsh-mneme/lib}/parser/wiki-link.js +38 -38
- package/{src → dsh-mneme/lib}/quality-filter.js +123 -123
- package/{lib → dsh-mneme/lib}/reranker.js +218 -218
- package/{lib → dsh-mneme/lib}/search/adaptive.js +22 -22
- package/{lib → dsh-mneme/lib}/search/bm25.js +96 -96
- package/{src → dsh-mneme/lib}/search/tag-boost.js +61 -61
- package/{src → dsh-mneme/lib}/service.js +1726 -1726
- package/{lib → dsh-mneme/lib}/settings.js +172 -172
- package/{lib → dsh-mneme/lib}/store.js +2238 -2238
- package/{lib → dsh-mneme/lib}/summarize.js +236 -236
- package/{lib → dsh-mneme/lib}/tools.js +290 -290
- package/{lib → dsh-mneme/lib}/vector-index.js +116 -116
- package/dsh-mneme/package-lock.json +1936 -0
- package/dsh-mneme/package.json +80 -0
- package/{scripts → dsh-mneme/scripts}/benchmark-embed.js +201 -201
- package/{scripts → dsh-mneme/scripts}/benchmark-recall.js +133 -133
- package/{scripts → dsh-mneme/scripts}/benchmark-rerank.js +166 -166
- package/{scripts → dsh-mneme/scripts}/e2e-dsh.js +218 -218
- package/{scripts → dsh-mneme/scripts}/stress-dsh.js +255 -255
- package/{scripts → dsh-mneme/scripts}/sync-lib.js +52 -52
- package/{src → dsh-mneme/src}/api.js +783 -783
- package/{lib → dsh-mneme/src}/commands.js +64 -64
- package/{src → dsh-mneme/src}/config.js +298 -288
- package/{src → dsh-mneme/src}/dream/clustering.js +118 -118
- package/{lib → dsh-mneme/src}/dream/decisions.js +488 -439
- package/{src → dsh-mneme/src}/dream/sleep.js +561 -554
- package/{lib → dsh-mneme/src}/dream/tag-extractor.js +156 -156
- package/{src → dsh-mneme/src}/dream.js +958 -929
- package/{lib → dsh-mneme/src}/embedding.js +154 -154
- package/{lib → dsh-mneme/src}/entities/extractor.js +242 -242
- package/{lib → dsh-mneme/src}/hot-memory.js +53 -53
- package/{src → dsh-mneme/src}/index.js +361 -361
- package/{lib → dsh-mneme/src}/inject.js +208 -208
- package/{src → dsh-mneme/src}/local-embedder.js +282 -282
- package/{src → dsh-mneme/src}/mirror.js +170 -170
- package/{src → dsh-mneme/src}/parser/tag.js +59 -59
- package/{src → dsh-mneme/src}/parser/wiki-link.js +38 -38
- package/{lib → dsh-mneme/src}/quality-filter.js +123 -123
- package/{src → dsh-mneme/src}/reranker.js +218 -218
- package/{src → dsh-mneme/src}/search/adaptive.js +22 -22
- package/{src → dsh-mneme/src}/search/bm25.js +96 -96
- package/{lib → dsh-mneme/src}/search/tag-boost.js +61 -61
- package/{lib → dsh-mneme/src}/service.js +1726 -1726
- package/{src → dsh-mneme/src}/settings.js +172 -172
- package/{src → dsh-mneme/src}/store.js +2238 -2238
- package/{src → dsh-mneme/src}/summarize.js +236 -236
- package/{src → dsh-mneme/src}/tools.js +290 -290
- package/{src → dsh-mneme/src}/vector-index.js +116 -116
- package/{test → dsh-mneme/test}/api.test.js +594 -594
- package/{test → dsh-mneme/test}/audit.test.js +448 -448
- package/{test → dsh-mneme/test}/benchmark.test.js +35 -35
- package/{test → dsh-mneme/test}/boundary-v0625.test.js +82 -82
- package/{test → dsh-mneme/test}/client.test.js +368 -368
- package/{test → dsh-mneme/test}/clustering.test.js +100 -100
- package/{test → dsh-mneme/test}/commands.test.js +69 -69
- package/{test → dsh-mneme/test}/config.test.js +50 -50
- package/{test → dsh-mneme/test}/conflict-freeze.test.js +290 -290
- package/{test → dsh-mneme/test}/directory.test.js +134 -134
- package/{test → dsh-mneme/test}/dream.test.js +1060 -901
- package/{test → dsh-mneme/test}/entities.test.js +522 -522
- package/{test → dsh-mneme/test}/epistemic.test.js +298 -298
- package/{test → dsh-mneme/test}/fnew-0112.test.js +311 -311
- package/{test → dsh-mneme/test}/fnew-03.test.js +422 -422
- package/{test → dsh-mneme/test}/graph-api.test.js +175 -175
- package/{test → dsh-mneme/test}/helpers/dream-mock.js +82 -82
- package/{test → dsh-mneme/test}/hot-memory.test.js +174 -174
- package/{test → dsh-mneme/test}/inject.test.js +103 -103
- package/{test → dsh-mneme/test}/llm-audit.test.js +279 -279
- package/{test → dsh-mneme/test}/local-embedder.test.js +227 -227
- package/{test → dsh-mneme/test}/mirror-dirty.test.js +424 -424
- package/{test → dsh-mneme/test}/mirror-edit-digest.test.js +187 -187
- package/{test → dsh-mneme/test}/mirror-generation.test.js +499 -499
- package/{test → dsh-mneme/test}/mirror.test.js +249 -249
- package/{test → dsh-mneme/test}/normalize-decisions.test.js +120 -120
- package/{test → dsh-mneme/test}/peer-blockers.test.js +190 -190
- package/{test → dsh-mneme/test}/policy-epoch.test.js +259 -259
- package/{test → dsh-mneme/test}/provenance.test.js +103 -103
- package/{test → dsh-mneme/test}/quality-filter.test.js +118 -118
- package/{test → dsh-mneme/test}/reasoning-effort.test.js +199 -199
- package/{test → dsh-mneme/test}/recall-evals.test.js +235 -235
- package/{test → dsh-mneme/test}/recall-layer.test.js +315 -315
- package/{test → dsh-mneme/test}/receipt-chain.test.js +451 -451
- package/{test → dsh-mneme/test}/reflection.test.js +226 -226
- package/{test → dsh-mneme/test}/reranker.test.js +240 -240
- package/{test → dsh-mneme/test}/search-fusion.test.js +90 -90
- package/{test → dsh-mneme/test}/semantic.test.js +124 -124
- package/{test → dsh-mneme/test}/service-search.test.js +199 -199
- package/{test → dsh-mneme/test}/service.test.js +435 -435
- package/{test → dsh-mneme/test}/settings.test.js +118 -118
- package/{test → dsh-mneme/test}/sleep.test.js +365 -365
- package/{test → dsh-mneme/test}/store.test.js +436 -436
- package/{test → dsh-mneme/test}/stress.test.js +209 -209
- package/{test → dsh-mneme/test}/summarize.test.js +191 -191
- package/{test → dsh-mneme/test}/tag-boost.test.js +125 -125
- package/{test → dsh-mneme/test}/tag.test.js +312 -312
- package/{test → dsh-mneme/test}/tools.test.js +285 -285
- package/{test → dsh-mneme/test}/vector-index.test.js +221 -221
- package/{test → dsh-mneme/test}/wiki-link.test.js +332 -332
- package/package.json +18 -40
- package//346/250/252/345/271/205.png +0 -0
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
// Thin vector-index layer over the store's embedding column. Keeps the
|
|
2
|
-
// current model fingerprint + stats in a dedicated `vector_meta` table so the
|
|
3
|
-
// index can detect drift (embeddings generated by a different model) and the
|
|
4
|
-
// Web panel can report index health. The heavy lifting (cosine search) lives
|
|
5
|
-
// in store.searchVector; this module owns the index lifecycle.
|
|
6
|
-
|
|
7
|
-
const META_SCHEMA = `
|
|
8
|
-
CREATE TABLE IF NOT EXISTS vector_meta (
|
|
9
|
-
key TEXT PRIMARY KEY,
|
|
10
|
-
value TEXT NOT NULL
|
|
11
|
-
);
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
export function createVectorIndex({ store, logger }) {
|
|
15
|
-
const db = store.db;
|
|
16
|
-
db.exec(META_SCHEMA);
|
|
17
|
-
|
|
18
|
-
function getMeta(key) {
|
|
19
|
-
const row = db.prepare("SELECT value FROM vector_meta WHERE key = ?").get(key);
|
|
20
|
-
return row?.value ?? undefined;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function setMeta(key, value) {
|
|
24
|
-
db.prepare(
|
|
25
|
-
`INSERT INTO vector_meta (key, value) VALUES (?, ?)
|
|
26
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
27
|
-
).run(key, String(value));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
/** Record the model that generated the current vectors. */
|
|
32
|
-
markModel(modelHash, dimension) {
|
|
33
|
-
setMeta("model_hash", modelHash);
|
|
34
|
-
setMeta("dimension", String(dimension ?? ""));
|
|
35
|
-
setMeta("updated_at", new Date().toISOString());
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
modelHash() {
|
|
39
|
-
return getMeta("model_hash");
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
dimension() {
|
|
43
|
-
const raw = getMeta("dimension");
|
|
44
|
-
return raw && Number.isInteger(Number(raw)) ? Number(raw) : undefined;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/** Store (or clear with null vector) one memory's embedding. */
|
|
48
|
-
saveEmbedding(id, vector) {
|
|
49
|
-
store.setEmbedding(id, vector);
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
/** Drop one memory's embedding. */
|
|
53
|
-
deleteEmbedding(id) {
|
|
54
|
-
store.setEmbedding(id, null);
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
/** Read one memory's cached vector; undefined when none/parse failure. */
|
|
58
|
-
getEmbedding(id) {
|
|
59
|
-
const row = db.prepare("SELECT embedding FROM memories WHERE id = ?").get(id);
|
|
60
|
-
if (!row?.embedding) return undefined;
|
|
61
|
-
try {
|
|
62
|
-
const v = JSON.parse(row.embedding);
|
|
63
|
-
return Array.isArray(v) && v.length ? v : undefined;
|
|
64
|
-
} catch {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
/** Cosine search over embedded rows; results carry a 0..1 score. */
|
|
70
|
-
search(queryVector, { limit = 20, threshold = 0.65 } = {}) {
|
|
71
|
-
return store.searchVector(queryVector, { limit, threshold });
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
/** Re-embed every row missing an embedding. Returns indexed count. */
|
|
75
|
-
async rebuildIndex(embedder, { limit = 1000 } = {}) {
|
|
76
|
-
// The loop needs a single-text embedder. Native embedSingle is preferred;
|
|
77
|
-
// embed-only OpenAI-compatible clients (issue #10) are accepted too via
|
|
78
|
-
// their `embed` single-text interface, so /vector-reindex no longer
|
|
79
|
-
// silently returns 0 for them. An embedder exposing neither is ignored.
|
|
80
|
-
let embedOne = null;
|
|
81
|
-
if (embedder && typeof embedder.embedSingle === "function") {
|
|
82
|
-
embedOne = (text) => embedder.embedSingle(text);
|
|
83
|
-
} else if (embedder && typeof embedder.embed === "function") {
|
|
84
|
-
embedOne = (text) => Promise.resolve(embedder.embed(text));
|
|
85
|
-
}
|
|
86
|
-
if (!embedOne) return { indexed: 0, skipped: 0 };
|
|
87
|
-
const rows = store.needsEmbedding(limit);
|
|
88
|
-
let indexed = 0;
|
|
89
|
-
for (const row of rows) {
|
|
90
|
-
try {
|
|
91
|
-
const text = [row.title, row.content].filter(Boolean).join("\n");
|
|
92
|
-
const vector = await embedOne(text);
|
|
93
|
-
if (vector && vector.length) {
|
|
94
|
-
store.setEmbedding(row.id, vector);
|
|
95
|
-
indexed++;
|
|
96
|
-
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
logger?.warn?.(`[dsh-mneme] rebuild embed failed for ${row.id}: ${String(error)}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (indexed > 0 && embedder.modelHash) this.markModel(embedder.modelHash, embedder.dimension);
|
|
102
|
-
return { indexed, skipped: rows.length - indexed };
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
/** Index stats for the Web panel / debugging. */
|
|
106
|
-
getStats() {
|
|
107
|
-
return {
|
|
108
|
-
modelHash: getMeta("model_hash"),
|
|
109
|
-
dimension: this.dimension(),
|
|
110
|
-
updatedAt: getMeta("updated_at"),
|
|
111
|
-
embeddedCount: store.embeddedCount(),
|
|
112
|
-
totalCount: store.count()
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
}
|
|
1
|
+
// Thin vector-index layer over the store's embedding column. Keeps the
|
|
2
|
+
// current model fingerprint + stats in a dedicated `vector_meta` table so the
|
|
3
|
+
// index can detect drift (embeddings generated by a different model) and the
|
|
4
|
+
// Web panel can report index health. The heavy lifting (cosine search) lives
|
|
5
|
+
// in store.searchVector; this module owns the index lifecycle.
|
|
6
|
+
|
|
7
|
+
const META_SCHEMA = `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS vector_meta (
|
|
9
|
+
key TEXT PRIMARY KEY,
|
|
10
|
+
value TEXT NOT NULL
|
|
11
|
+
);
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
export function createVectorIndex({ store, logger }) {
|
|
15
|
+
const db = store.db;
|
|
16
|
+
db.exec(META_SCHEMA);
|
|
17
|
+
|
|
18
|
+
function getMeta(key) {
|
|
19
|
+
const row = db.prepare("SELECT value FROM vector_meta WHERE key = ?").get(key);
|
|
20
|
+
return row?.value ?? undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function setMeta(key, value) {
|
|
24
|
+
db.prepare(
|
|
25
|
+
`INSERT INTO vector_meta (key, value) VALUES (?, ?)
|
|
26
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
|
|
27
|
+
).run(key, String(value));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
/** Record the model that generated the current vectors. */
|
|
32
|
+
markModel(modelHash, dimension) {
|
|
33
|
+
setMeta("model_hash", modelHash);
|
|
34
|
+
setMeta("dimension", String(dimension ?? ""));
|
|
35
|
+
setMeta("updated_at", new Date().toISOString());
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
modelHash() {
|
|
39
|
+
return getMeta("model_hash");
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
dimension() {
|
|
43
|
+
const raw = getMeta("dimension");
|
|
44
|
+
return raw && Number.isInteger(Number(raw)) ? Number(raw) : undefined;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/** Store (or clear with null vector) one memory's embedding. */
|
|
48
|
+
saveEmbedding(id, vector) {
|
|
49
|
+
store.setEmbedding(id, vector);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** Drop one memory's embedding. */
|
|
53
|
+
deleteEmbedding(id) {
|
|
54
|
+
store.setEmbedding(id, null);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/** Read one memory's cached vector; undefined when none/parse failure. */
|
|
58
|
+
getEmbedding(id) {
|
|
59
|
+
const row = db.prepare("SELECT embedding FROM memories WHERE id = ?").get(id);
|
|
60
|
+
if (!row?.embedding) return undefined;
|
|
61
|
+
try {
|
|
62
|
+
const v = JSON.parse(row.embedding);
|
|
63
|
+
return Array.isArray(v) && v.length ? v : undefined;
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/** Cosine search over embedded rows; results carry a 0..1 score. */
|
|
70
|
+
search(queryVector, { limit = 20, threshold = 0.65 } = {}) {
|
|
71
|
+
return store.searchVector(queryVector, { limit, threshold });
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/** Re-embed every row missing an embedding. Returns indexed count. */
|
|
75
|
+
async rebuildIndex(embedder, { limit = 1000 } = {}) {
|
|
76
|
+
// The loop needs a single-text embedder. Native embedSingle is preferred;
|
|
77
|
+
// embed-only OpenAI-compatible clients (issue #10) are accepted too via
|
|
78
|
+
// their `embed` single-text interface, so /vector-reindex no longer
|
|
79
|
+
// silently returns 0 for them. An embedder exposing neither is ignored.
|
|
80
|
+
let embedOne = null;
|
|
81
|
+
if (embedder && typeof embedder.embedSingle === "function") {
|
|
82
|
+
embedOne = (text) => embedder.embedSingle(text);
|
|
83
|
+
} else if (embedder && typeof embedder.embed === "function") {
|
|
84
|
+
embedOne = (text) => Promise.resolve(embedder.embed(text));
|
|
85
|
+
}
|
|
86
|
+
if (!embedOne) return { indexed: 0, skipped: 0 };
|
|
87
|
+
const rows = store.needsEmbedding(limit);
|
|
88
|
+
let indexed = 0;
|
|
89
|
+
for (const row of rows) {
|
|
90
|
+
try {
|
|
91
|
+
const text = [row.title, row.content].filter(Boolean).join("\n");
|
|
92
|
+
const vector = await embedOne(text);
|
|
93
|
+
if (vector && vector.length) {
|
|
94
|
+
store.setEmbedding(row.id, vector);
|
|
95
|
+
indexed++;
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logger?.warn?.(`[dsh-mneme] rebuild embed failed for ${row.id}: ${String(error)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (indexed > 0 && embedder.modelHash) this.markModel(embedder.modelHash, embedder.dimension);
|
|
102
|
+
return { indexed, skipped: rows.length - indexed };
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/** Index stats for the Web panel / debugging. */
|
|
106
|
+
getStats() {
|
|
107
|
+
return {
|
|
108
|
+
modelHash: getMeta("model_hash"),
|
|
109
|
+
dimension: this.dimension(),
|
|
110
|
+
updatedAt: getMeta("updated_at"),
|
|
111
|
+
embeddedCount: store.embeddedCount(),
|
|
112
|
+
totalCount: store.count()
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|