linksee-memory 0.7.1 → 0.8.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/README.md +193 -34
- package/dist/bin/declare-anchor.d.ts +2 -0
- package/dist/bin/declare-anchor.js +146 -0
- package/dist/bin/detect-drift.d.ts +2 -0
- package/dist/bin/detect-drift.js +91 -0
- package/dist/db/migrate.js +52 -28
- package/dist/db/schema.sql +348 -232
- package/dist/lib/consolidate.d.ts +2 -0
- package/dist/lib/consolidate.js +8 -0
- package/dist/lib/drift-anchors.d.ts +78 -0
- package/dist/lib/drift-anchors.js +224 -0
- package/dist/lib/drift-detection.d.ts +62 -0
- package/dist/lib/drift-detection.js +416 -0
- package/dist/lib/drift-view.d.ts +62 -0
- package/dist/lib/drift-view.js +120 -0
- package/dist/lib/edge-detection.d.ts +22 -0
- package/dist/lib/edge-detection.js +178 -0
- package/dist/lib/session-extractor.js +49 -0
- package/dist/lib/truth-engine.d.ts +84 -0
- package/dist/lib/truth-engine.js +417 -0
- package/dist/mcp/server.js +292 -57
- package/package.json +5 -3
package/dist/db/migrate.js
CHANGED
|
@@ -33,11 +33,11 @@ export function runMigrations(db) {
|
|
|
33
33
|
// v3 → v4: rebuild memories_fts with trigram tokenizer for JP/CJK support.
|
|
34
34
|
// Only runs when upgrading an existing DB from schema v1-3.
|
|
35
35
|
if (currentVersion > 0 && currentVersion < 4) {
|
|
36
|
-
db.exec(`
|
|
37
|
-
DROP TRIGGER IF EXISTS trg_memories_fts_ai;
|
|
38
|
-
DROP TRIGGER IF EXISTS trg_memories_fts_ad;
|
|
39
|
-
DROP TRIGGER IF EXISTS trg_memories_fts_au;
|
|
40
|
-
DROP TABLE IF EXISTS memories_fts;
|
|
36
|
+
db.exec(`
|
|
37
|
+
DROP TRIGGER IF EXISTS trg_memories_fts_ai;
|
|
38
|
+
DROP TRIGGER IF EXISTS trg_memories_fts_ad;
|
|
39
|
+
DROP TRIGGER IF EXISTS trg_memories_fts_au;
|
|
40
|
+
DROP TABLE IF EXISTS memories_fts;
|
|
41
41
|
`);
|
|
42
42
|
}
|
|
43
43
|
// v4 → v5: add normalized_name column BEFORE schema.sql runs,
|
|
@@ -81,11 +81,35 @@ export function runMigrations(db) {
|
|
|
81
81
|
db.exec('ALTER TABLE memories ADD COLUMN thread_id TEXT');
|
|
82
82
|
}
|
|
83
83
|
// Backfill thread_id from content JSON session_id for existing memories
|
|
84
|
-
db.exec(`
|
|
85
|
-
UPDATE memories SET thread_id = json_extract(content, '$.session_id')
|
|
86
|
-
WHERE thread_id IS NULL AND json_valid(content) AND json_extract(content, '$.session_id') IS NOT NULL
|
|
84
|
+
db.exec(`
|
|
85
|
+
UPDATE memories SET thread_id = json_extract(content, '$.session_id')
|
|
86
|
+
WHERE thread_id IS NULL AND json_valid(content) AND json_extract(content, '$.session_id') IS NOT NULL
|
|
87
87
|
`);
|
|
88
88
|
}
|
|
89
|
+
// v8 → v9: ProjectCoreNode — extend drift_anchors into the Current Truth Map node.
|
|
90
|
+
// ADDITIVE columns only (ADD COLUMN with safe defaults) — NO CHECK rebuild, so the existing
|
|
91
|
+
// detector/dashboard (which read the original columns + status active/retired) are unaffected.
|
|
92
|
+
// `lifecycle` carries the rich state; `status` stays the coarse scan-gate. New tables
|
|
93
|
+
// (reality_events, memory_write_candidates) are created by db.exec(sql) below (CREATE IF NOT EXISTS).
|
|
94
|
+
if (currentVersion > 0 && currentVersion < 9) {
|
|
95
|
+
const have = new Set(db.prepare('PRAGMA table_info(drift_anchors)').all().map((c) => c.name));
|
|
96
|
+
const addCol = (name, ddl) => {
|
|
97
|
+
if (!have.has(name))
|
|
98
|
+
db.exec(`ALTER TABLE drift_anchors ADD COLUMN ${ddl}`);
|
|
99
|
+
};
|
|
100
|
+
addCol('node_type', 'node_type TEXT');
|
|
101
|
+
addCol('domain', 'domain TEXT');
|
|
102
|
+
addCol('decision_mode', 'decision_mode TEXT');
|
|
103
|
+
addCol('confidence', 'confidence REAL NOT NULL DEFAULT 0.8');
|
|
104
|
+
addCol('lifecycle', "lifecycle TEXT NOT NULL DEFAULT 'active'");
|
|
105
|
+
addCol('validity_scope', "validity_scope TEXT NOT NULL DEFAULT '{}'");
|
|
106
|
+
addCol('card_policy', "card_policy TEXT NOT NULL DEFAULT '{}'");
|
|
107
|
+
addCol('reality_manifestations', "reality_manifestations TEXT NOT NULL DEFAULT '[]'");
|
|
108
|
+
addCol('evidence_refs', "evidence_refs TEXT NOT NULL DEFAULT '[]'");
|
|
109
|
+
addCol('review_after', 'review_after INTEGER');
|
|
110
|
+
addCol('last_confirmed_at', 'last_confirmed_at INTEGER');
|
|
111
|
+
addCol('owner', 'owner TEXT');
|
|
112
|
+
}
|
|
89
113
|
db.exec(sql);
|
|
90
114
|
if (currentVersion > 0 && currentVersion < 4) {
|
|
91
115
|
db.exec(`INSERT INTO memories_fts(rowid, content) SELECT id, content FROM memories;`);
|
|
@@ -124,12 +148,12 @@ function migrateV5EntityNormalization(db) {
|
|
|
124
148
|
}
|
|
125
149
|
})();
|
|
126
150
|
// 4. Auto-merge duplicate entities (same kind + normalized_name)
|
|
127
|
-
const dupes = db.prepare(`
|
|
128
|
-
SELECT kind, normalized_name, GROUP_CONCAT(id) as ids
|
|
129
|
-
FROM entities
|
|
130
|
-
WHERE normalized_name IS NOT NULL
|
|
131
|
-
GROUP BY kind, normalized_name
|
|
132
|
-
HAVING COUNT(*) > 1
|
|
151
|
+
const dupes = db.prepare(`
|
|
152
|
+
SELECT kind, normalized_name, GROUP_CONCAT(id) as ids
|
|
153
|
+
FROM entities
|
|
154
|
+
WHERE normalized_name IS NOT NULL
|
|
155
|
+
GROUP BY kind, normalized_name
|
|
156
|
+
HAVING COUNT(*) > 1
|
|
133
157
|
`).all();
|
|
134
158
|
if (dupes.length > 0) {
|
|
135
159
|
console.log(`[linksee-memory] v5 migration: merging ${dupes.length} duplicate entity clusters`);
|
|
@@ -150,12 +174,12 @@ function mergeEntityCluster(db, ids) {
|
|
|
150
174
|
if (ids.length < 2)
|
|
151
175
|
return;
|
|
152
176
|
// Score each entity: prefer most memories, then has canonical_key, then lowest id
|
|
153
|
-
const rows = db.prepare(`
|
|
154
|
-
SELECT e.id, e.name, e.canonical_key, COUNT(m.id) as mem_count
|
|
155
|
-
FROM entities e LEFT JOIN memories m ON m.entity_id = e.id
|
|
156
|
-
WHERE e.id IN (${ids.map(() => '?').join(',')})
|
|
157
|
-
GROUP BY e.id
|
|
158
|
-
ORDER BY mem_count DESC, (e.canonical_key IS NOT NULL) DESC, e.id ASC
|
|
177
|
+
const rows = db.prepare(`
|
|
178
|
+
SELECT e.id, e.name, e.canonical_key, COUNT(m.id) as mem_count
|
|
179
|
+
FROM entities e LEFT JOIN memories m ON m.entity_id = e.id
|
|
180
|
+
WHERE e.id IN (${ids.map(() => '?').join(',')})
|
|
181
|
+
GROUP BY e.id
|
|
182
|
+
ORDER BY mem_count DESC, (e.canonical_key IS NOT NULL) DESC, e.id ASC
|
|
159
183
|
`).all(...ids);
|
|
160
184
|
const keep = rows[0];
|
|
161
185
|
const mergeIds = rows.slice(1).map(r => r.id);
|
|
@@ -176,16 +200,16 @@ function mergeEntityCluster(db, ids) {
|
|
|
176
200
|
db.prepare('UPDATE events SET entity_id = ? WHERE entity_id = ?').run(keep.id, mid);
|
|
177
201
|
// Reassign edges (both directions)
|
|
178
202
|
// Handle UNIQUE constraint: delete duplicates first
|
|
179
|
-
db.prepare(`
|
|
180
|
-
DELETE FROM edges WHERE from_id = ? AND EXISTS (
|
|
181
|
-
SELECT 1 FROM edges e2 WHERE e2.from_id = ? AND e2.to_id = edges.to_id AND e2.relation = edges.relation
|
|
182
|
-
)
|
|
203
|
+
db.prepare(`
|
|
204
|
+
DELETE FROM edges WHERE from_id = ? AND EXISTS (
|
|
205
|
+
SELECT 1 FROM edges e2 WHERE e2.from_id = ? AND e2.to_id = edges.to_id AND e2.relation = edges.relation
|
|
206
|
+
)
|
|
183
207
|
`).run(mid, keep.id);
|
|
184
208
|
db.prepare('UPDATE edges SET from_id = ? WHERE from_id = ?').run(keep.id, mid);
|
|
185
|
-
db.prepare(`
|
|
186
|
-
DELETE FROM edges WHERE to_id = ? AND EXISTS (
|
|
187
|
-
SELECT 1 FROM edges e2 WHERE e2.to_id = ? AND e2.from_id = edges.from_id AND e2.relation = edges.relation
|
|
188
|
-
)
|
|
209
|
+
db.prepare(`
|
|
210
|
+
DELETE FROM edges WHERE to_id = ? AND EXISTS (
|
|
211
|
+
SELECT 1 FROM edges e2 WHERE e2.to_id = ? AND e2.from_id = edges.from_id AND e2.relation = edges.relation
|
|
212
|
+
)
|
|
189
213
|
`).run(mid, keep.id);
|
|
190
214
|
db.prepare('UPDATE edges SET to_id = ? WHERE to_id = ?').run(keep.id, mid);
|
|
191
215
|
// Reassign consolidations
|