linksee-memory 0.12.0 → 0.12.1
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 +1007 -1004
- package/dist/db/migrate.js +28 -28
- package/dist/db/schema.sql +461 -461
- package/dist/lib/drift-anchors.js +3 -3
- package/dist/lib/drift-detection.js +20 -20
- package/dist/lib/drift-view.js +16 -16
- package/dist/lib/guard.js +28 -28
- package/dist/lib/map-import.js +17 -17
- package/dist/lib/truth-engine.js +13 -13
- package/dist/mcp/prompts.js +58 -58
- package/dist/mcp/resources.js +7 -7
- package/dist/mcp/sampling.js +20 -20
- package/dist/mcp/server.js +130 -130
- package/package.json +96 -95
package/dist/mcp/server.js
CHANGED
|
@@ -479,15 +479,15 @@ function appendAxisFilters(sql, params, filters) {
|
|
|
479
479
|
return sql;
|
|
480
480
|
}
|
|
481
481
|
function runFtsQuery(query, layer, limit, axis) {
|
|
482
|
-
let sql = `
|
|
483
|
-
SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
|
|
484
|
-
m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
|
|
485
|
-
m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
|
|
486
|
-
bm25(memories_fts) as bm25_score
|
|
487
|
-
FROM memories_fts
|
|
488
|
-
JOIN memories m ON m.id = memories_fts.rowid
|
|
489
|
-
JOIN entities e ON e.id = m.entity_id
|
|
490
|
-
WHERE memories_fts MATCH ?
|
|
482
|
+
let sql = `
|
|
483
|
+
SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
|
|
484
|
+
m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
|
|
485
|
+
m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
|
|
486
|
+
bm25(memories_fts) as bm25_score
|
|
487
|
+
FROM memories_fts
|
|
488
|
+
JOIN memories m ON m.id = memories_fts.rowid
|
|
489
|
+
JOIN entities e ON e.id = m.entity_id
|
|
490
|
+
WHERE memories_fts MATCH ?
|
|
491
491
|
`;
|
|
492
492
|
const params = [query];
|
|
493
493
|
if (layer) {
|
|
@@ -501,14 +501,14 @@ function runFtsQuery(query, layer, limit, axis) {
|
|
|
501
501
|
return db.prepare(sql).all(...params);
|
|
502
502
|
}
|
|
503
503
|
function runLikeQuery(query, entityName, layer, limit, axis) {
|
|
504
|
-
let sql = `
|
|
505
|
-
SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
|
|
506
|
-
m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
|
|
507
|
-
m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
|
|
508
|
-
0 as bm25_score
|
|
509
|
-
FROM memories m
|
|
510
|
-
JOIN entities e ON e.id = m.entity_id
|
|
511
|
-
WHERE 1=1
|
|
504
|
+
let sql = `
|
|
505
|
+
SELECT m.id, m.entity_id, e.name as entity_name, e.kind as entity_kind, e.momentum_score,
|
|
506
|
+
m.layer, m.content, m.importance, m.created_at, m.last_accessed_at, m.access_count,
|
|
507
|
+
m.altitude as _altitude, m.mem_type as _mem_type, m.mem_state as _mem_state, m.thread_id as _thread_id,
|
|
508
|
+
0 as bm25_score
|
|
509
|
+
FROM memories m
|
|
510
|
+
JOIN entities e ON e.id = m.entity_id
|
|
511
|
+
WHERE 1=1
|
|
512
512
|
`;
|
|
513
513
|
const params = [];
|
|
514
514
|
if (entityName) {
|
|
@@ -839,8 +839,8 @@ function handleForget(args) {
|
|
|
839
839
|
}
|
|
840
840
|
// Auto-sweep — also respect pin (importance >= 0.9) as protection
|
|
841
841
|
const rows = db
|
|
842
|
-
.prepare(`SELECT id, layer, importance, access_count, last_accessed_at, protected, altitude
|
|
843
|
-
FROM memories
|
|
842
|
+
.prepare(`SELECT id, layer, importance, access_count, last_accessed_at, protected, altitude
|
|
843
|
+
FROM memories
|
|
844
844
|
WHERE protected = 0 AND importance < 0.9`)
|
|
845
845
|
.all();
|
|
846
846
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -889,17 +889,17 @@ function handleConsolidate(args) {
|
|
|
889
889
|
// read-only audit: count candidates using the same rules.
|
|
890
890
|
const now = Math.floor(Date.now() / 1000);
|
|
891
891
|
const ageCutoff = now - (args.min_age_days ?? 7) * 86400;
|
|
892
|
-
const candidates = db.prepare(`
|
|
893
|
-
SELECT m.entity_id, e.name as entity_name, m.layer, COUNT(*) as c
|
|
894
|
-
FROM memories m
|
|
895
|
-
JOIN entities e ON e.id = m.entity_id
|
|
896
|
-
WHERE m.protected = 0
|
|
897
|
-
AND m.importance < 0.9
|
|
898
|
-
AND m.layer IN ('context', 'emotion', 'implementation')
|
|
899
|
-
AND m.created_at <= ?
|
|
900
|
-
GROUP BY m.entity_id, m.layer
|
|
901
|
-
HAVING c >= 2
|
|
902
|
-
ORDER BY c DESC
|
|
892
|
+
const candidates = db.prepare(`
|
|
893
|
+
SELECT m.entity_id, e.name as entity_name, m.layer, COUNT(*) as c
|
|
894
|
+
FROM memories m
|
|
895
|
+
JOIN entities e ON e.id = m.entity_id
|
|
896
|
+
WHERE m.protected = 0
|
|
897
|
+
AND m.importance < 0.9
|
|
898
|
+
AND m.layer IN ('context', 'emotion', 'implementation')
|
|
899
|
+
AND m.created_at <= ?
|
|
900
|
+
GROUP BY m.entity_id, m.layer
|
|
901
|
+
HAVING c >= 2
|
|
902
|
+
ORDER BY c DESC
|
|
903
903
|
`).all(ageCutoff);
|
|
904
904
|
const totalReplaced = candidates.reduce((s, c) => s + c.c, 0);
|
|
905
905
|
return JSON.stringify({
|
|
@@ -973,19 +973,19 @@ function handleListEntities(args) {
|
|
|
973
973
|
const minMemories = Math.max(1, Number(args?.min_memories ?? 1));
|
|
974
974
|
const limit = Math.max(1, Math.min(200, Number(args?.limit ?? 30)));
|
|
975
975
|
const offset = Math.max(0, Number(args?.offset ?? 0));
|
|
976
|
-
let sql = `
|
|
977
|
-
SELECT e.id, e.name, e.kind, e.canonical_key, e.momentum_score,
|
|
978
|
-
e.updated_at, e.created_at,
|
|
979
|
-
COUNT(m.id) as memory_count,
|
|
980
|
-
MAX(m.last_accessed_at) as last_memory_access,
|
|
981
|
-
SUM(CASE WHEN m.layer = 'goal' THEN 1 ELSE 0 END) as goal_count,
|
|
982
|
-
SUM(CASE WHEN m.layer = 'caveat' THEN 1 ELSE 0 END) as caveat_count,
|
|
983
|
-
SUM(CASE WHEN m.layer = 'learning' THEN 1 ELSE 0 END) as learning_count,
|
|
984
|
-
SUM(CASE WHEN m.layer = 'implementation' THEN 1 ELSE 0 END) as impl_count,
|
|
985
|
-
SUM(CASE WHEN m.importance >= 0.9 THEN 1 ELSE 0 END) as pinned_count
|
|
986
|
-
FROM entities e
|
|
987
|
-
LEFT JOIN memories m ON m.entity_id = e.id
|
|
988
|
-
WHERE 1=1
|
|
976
|
+
let sql = `
|
|
977
|
+
SELECT e.id, e.name, e.kind, e.canonical_key, e.momentum_score,
|
|
978
|
+
e.updated_at, e.created_at,
|
|
979
|
+
COUNT(m.id) as memory_count,
|
|
980
|
+
MAX(m.last_accessed_at) as last_memory_access,
|
|
981
|
+
SUM(CASE WHEN m.layer = 'goal' THEN 1 ELSE 0 END) as goal_count,
|
|
982
|
+
SUM(CASE WHEN m.layer = 'caveat' THEN 1 ELSE 0 END) as caveat_count,
|
|
983
|
+
SUM(CASE WHEN m.layer = 'learning' THEN 1 ELSE 0 END) as learning_count,
|
|
984
|
+
SUM(CASE WHEN m.layer = 'implementation' THEN 1 ELSE 0 END) as impl_count,
|
|
985
|
+
SUM(CASE WHEN m.importance >= 0.9 THEN 1 ELSE 0 END) as pinned_count
|
|
986
|
+
FROM entities e
|
|
987
|
+
LEFT JOIN memories m ON m.entity_id = e.id
|
|
988
|
+
WHERE 1=1
|
|
989
989
|
`;
|
|
990
990
|
const params = [];
|
|
991
991
|
if (kind) {
|
|
@@ -1037,29 +1037,29 @@ function handleRecallFile(args) {
|
|
|
1037
1037
|
return JSON.stringify({ ok: true, count: 0, note: 'No edits found for that path substring.' });
|
|
1038
1038
|
}
|
|
1039
1039
|
// Daily breakdown
|
|
1040
|
-
const daily = db.prepare(`
|
|
1041
|
-
SELECT DATE(occurred_at, 'unixepoch') as day, operation, COUNT(*) as edits
|
|
1042
|
-
FROM session_file_edits WHERE file_path LIKE ?
|
|
1043
|
-
GROUP BY day, operation ORDER BY day
|
|
1040
|
+
const daily = db.prepare(`
|
|
1041
|
+
SELECT DATE(occurred_at, 'unixepoch') as day, operation, COUNT(*) as edits
|
|
1042
|
+
FROM session_file_edits WHERE file_path LIKE ?
|
|
1043
|
+
GROUP BY day, operation ORDER BY day
|
|
1044
1044
|
`).all(`%${sub}%`);
|
|
1045
1045
|
// Distinct context_snippets (intents) — deduped, ordered by recency
|
|
1046
|
-
const intents = db.prepare(`
|
|
1047
|
-
SELECT DISTINCT context_snippet, MAX(occurred_at) as last_at, COUNT(*) as freq
|
|
1048
|
-
FROM session_file_edits
|
|
1049
|
-
WHERE file_path LIKE ? AND context_snippet IS NOT NULL AND LENGTH(context_snippet) > 20
|
|
1050
|
-
GROUP BY context_snippet
|
|
1051
|
-
ORDER BY last_at DESC
|
|
1052
|
-
LIMIT ?
|
|
1046
|
+
const intents = db.prepare(`
|
|
1047
|
+
SELECT DISTINCT context_snippet, MAX(occurred_at) as last_at, COUNT(*) as freq
|
|
1048
|
+
FROM session_file_edits
|
|
1049
|
+
WHERE file_path LIKE ? AND context_snippet IS NOT NULL AND LENGTH(context_snippet) > 20
|
|
1050
|
+
GROUP BY context_snippet
|
|
1051
|
+
ORDER BY last_at DESC
|
|
1052
|
+
LIMIT ?
|
|
1053
1053
|
`).all(`%${sub}%`, maxIntents);
|
|
1054
1054
|
// Linked memories
|
|
1055
|
-
const memories = db.prepare(`
|
|
1056
|
-
SELECT DISTINCT m.id, m.layer, m.content, m.importance, e.name as entity_name
|
|
1057
|
-
FROM session_file_edits sfe
|
|
1058
|
-
JOIN memories m ON m.id = sfe.memory_id
|
|
1059
|
-
JOIN entities e ON e.id = m.entity_id
|
|
1060
|
-
WHERE sfe.file_path LIKE ?
|
|
1061
|
-
ORDER BY m.importance DESC
|
|
1062
|
-
LIMIT 20
|
|
1055
|
+
const memories = db.prepare(`
|
|
1056
|
+
SELECT DISTINCT m.id, m.layer, m.content, m.importance, e.name as entity_name
|
|
1057
|
+
FROM session_file_edits sfe
|
|
1058
|
+
JOIN memories m ON m.id = sfe.memory_id
|
|
1059
|
+
JOIN entities e ON e.id = m.entity_id
|
|
1060
|
+
WHERE sfe.file_path LIKE ?
|
|
1061
|
+
ORDER BY m.importance DESC
|
|
1062
|
+
LIMIT 20
|
|
1063
1063
|
`).all(`%${sub}%`);
|
|
1064
1064
|
// Distinct file paths matched (the substring may match multiple files)
|
|
1065
1065
|
const paths = db.prepare(`SELECT file_path, COUNT(*) as edits FROM session_file_edits WHERE file_path LIKE ? GROUP BY file_path ORDER BY edits DESC`).all(`%${sub}%`);
|
|
@@ -1126,10 +1126,10 @@ async function handleConsolidateWithSampling(args) {
|
|
|
1126
1126
|
const ageCutoff = Math.floor(Date.now() / 1000) - (typeof args?.min_age_days === 'number' ? args.min_age_days : 7) * 86400;
|
|
1127
1127
|
const snapshot = new Map();
|
|
1128
1128
|
const candidateRows = db
|
|
1129
|
-
.prepare(`SELECT m.id, m.content, e.name as entity_name
|
|
1130
|
-
FROM memories m JOIN entities e ON e.id = m.entity_id
|
|
1131
|
-
WHERE m.protected = 0
|
|
1132
|
-
AND m.layer IN ('context','emotion','implementation')
|
|
1129
|
+
.prepare(`SELECT m.id, m.content, e.name as entity_name
|
|
1130
|
+
FROM memories m JOIN entities e ON e.id = m.entity_id
|
|
1131
|
+
WHERE m.protected = 0
|
|
1132
|
+
AND m.layer IN ('context','emotion','implementation')
|
|
1133
1133
|
AND m.created_at <= ?`)
|
|
1134
1134
|
.all(ageCutoff);
|
|
1135
1135
|
for (const r of candidateRows)
|
|
@@ -1352,8 +1352,8 @@ function resolveProjectFromRecentEdits() {
|
|
|
1352
1352
|
if (projects.length <= 1)
|
|
1353
1353
|
return { project: projects[0] };
|
|
1354
1354
|
const rows = db
|
|
1355
|
-
.prepare(`SELECT file_path FROM session_file_edits
|
|
1356
|
-
WHERE occurred_at > unixepoch() - 86400
|
|
1355
|
+
.prepare(`SELECT file_path FROM session_file_edits
|
|
1356
|
+
WHERE occurred_at > unixepoch() - 86400
|
|
1357
1357
|
ORDER BY occurred_at DESC LIMIT 50`)
|
|
1358
1358
|
.all();
|
|
1359
1359
|
if (rows.length === 0)
|
|
@@ -1504,25 +1504,25 @@ function handleFlagProposals(args) {
|
|
|
1504
1504
|
const sessionContext = args.session_context ?? 'conversation session';
|
|
1505
1505
|
const PROPOSAL_TAG = 'orphaned_proposal';
|
|
1506
1506
|
const now = Math.floor(Date.now() / 1000);
|
|
1507
|
-
const insertAnchor = db.prepare(`
|
|
1508
|
-
INSERT INTO drift_anchors (
|
|
1509
|
-
kind, statement, rationale, domain, decision_mode,
|
|
1510
|
-
confidence, lifecycle, status, owner,
|
|
1511
|
-
evidence_refs, created_at, updated_at
|
|
1512
|
-
) VALUES (
|
|
1513
|
-
'decision', ?, ?, ?, 'hypothesis',
|
|
1514
|
-
?, 'active', 'active', 'agent',
|
|
1515
|
-
?, ?, ?
|
|
1516
|
-
)
|
|
1507
|
+
const insertAnchor = db.prepare(`
|
|
1508
|
+
INSERT INTO drift_anchors (
|
|
1509
|
+
kind, statement, rationale, domain, decision_mode,
|
|
1510
|
+
confidence, lifecycle, status, owner,
|
|
1511
|
+
evidence_refs, created_at, updated_at
|
|
1512
|
+
) VALUES (
|
|
1513
|
+
'decision', ?, ?, ?, 'hypothesis',
|
|
1514
|
+
?, 'active', 'active', 'agent',
|
|
1515
|
+
?, ?, ?
|
|
1516
|
+
)
|
|
1517
1517
|
`);
|
|
1518
|
-
const insertCandidate = db.prepare(`
|
|
1519
|
-
INSERT INTO memory_write_candidates (
|
|
1520
|
-
scope, candidate_type, target_node_id, rationale,
|
|
1521
|
-
confidence, evidence_refs, status, created_at
|
|
1522
|
-
) VALUES (
|
|
1523
|
-
'orphaned_proposal', 'update_node', ?, ?,
|
|
1524
|
-
?, ?, 'pending_review', ?
|
|
1525
|
-
)
|
|
1518
|
+
const insertCandidate = db.prepare(`
|
|
1519
|
+
INSERT INTO memory_write_candidates (
|
|
1520
|
+
scope, candidate_type, target_node_id, rationale,
|
|
1521
|
+
confidence, evidence_refs, status, created_at
|
|
1522
|
+
) VALUES (
|
|
1523
|
+
'orphaned_proposal', 'update_node', ?, ?,
|
|
1524
|
+
?, ?, 'pending_review', ?
|
|
1525
|
+
)
|
|
1526
1526
|
`);
|
|
1527
1527
|
const results = [];
|
|
1528
1528
|
const txn = db.transaction(() => {
|
|
@@ -1566,13 +1566,13 @@ function handleFlagProposals(args) {
|
|
|
1566
1566
|
function handleDream(args) {
|
|
1567
1567
|
const domainFilter = args?.domain;
|
|
1568
1568
|
// 1. Fetch active North Star anchor(s)
|
|
1569
|
-
const northStars = db.prepare(`
|
|
1570
|
-
SELECT id, statement, rationale, domain, confidence, lifecycle,
|
|
1571
|
-
datetime(review_after, 'unixepoch') as review_date,
|
|
1572
|
-
datetime(created_at, 'unixepoch') as declared_at
|
|
1573
|
-
FROM drift_anchors
|
|
1574
|
-
WHERE node_type = 'north_star' AND status = 'active'
|
|
1575
|
-
ORDER BY created_at DESC
|
|
1569
|
+
const northStars = db.prepare(`
|
|
1570
|
+
SELECT id, statement, rationale, domain, confidence, lifecycle,
|
|
1571
|
+
datetime(review_after, 'unixepoch') as review_date,
|
|
1572
|
+
datetime(created_at, 'unixepoch') as declared_at
|
|
1573
|
+
FROM drift_anchors
|
|
1574
|
+
WHERE node_type = 'north_star' AND status = 'active'
|
|
1575
|
+
ORDER BY created_at DESC
|
|
1576
1576
|
`).all();
|
|
1577
1577
|
// Re-injection friction — the active-observability loop closing back into reflection.
|
|
1578
1578
|
// "Re-surfaced N×, yet still contradicted in reality" is the machine evidence behind #15443.
|
|
@@ -1589,18 +1589,18 @@ function handleDream(args) {
|
|
|
1589
1589
|
// why-strings so the existing backlog is drainable without a backfill write.
|
|
1590
1590
|
let distillQueue = [];
|
|
1591
1591
|
try {
|
|
1592
|
-
const rows = db.prepare(`
|
|
1593
|
-
SELECT m.id, m.layer, m.content, datetime(m.created_at, 'unixepoch') AS created, e.name AS entity
|
|
1594
|
-
FROM memories m JOIN entities e ON e.id = m.entity_id
|
|
1595
|
-
WHERE m.layer IN ('learning', 'caveat')
|
|
1596
|
-
AND json_valid(m.content)
|
|
1597
|
-
-- settle window: the Stop hook extracts every turn, so the newest rows belong to a
|
|
1598
|
-
-- session still in motion. Don't ask the agent to distill the conversation it is in.
|
|
1599
|
-
AND m.created_at < unixepoch() - 1800
|
|
1600
|
-
AND (json_extract(m.content, '$.needs_distill') = 1
|
|
1601
|
-
OR json_extract(m.content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
|
|
1602
|
-
OR json_extract(m.content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')
|
|
1603
|
-
ORDER BY m.created_at DESC LIMIT 8
|
|
1592
|
+
const rows = db.prepare(`
|
|
1593
|
+
SELECT m.id, m.layer, m.content, datetime(m.created_at, 'unixepoch') AS created, e.name AS entity
|
|
1594
|
+
FROM memories m JOIN entities e ON e.id = m.entity_id
|
|
1595
|
+
WHERE m.layer IN ('learning', 'caveat')
|
|
1596
|
+
AND json_valid(m.content)
|
|
1597
|
+
-- settle window: the Stop hook extracts every turn, so the newest rows belong to a
|
|
1598
|
+
-- session still in motion. Don't ask the agent to distill the conversation it is in.
|
|
1599
|
+
AND m.created_at < unixepoch() - 1800
|
|
1600
|
+
AND (json_extract(m.content, '$.needs_distill') = 1
|
|
1601
|
+
OR json_extract(m.content, '$.why') = 'Decision detected by pattern match — may need agent enrichment'
|
|
1602
|
+
OR json_extract(m.content, '$.why') = 'User-stated warning/prohibition — auto-extracted by caveat pattern match')
|
|
1603
|
+
ORDER BY m.created_at DESC LIMIT 8
|
|
1604
1604
|
`).all();
|
|
1605
1605
|
distillQueue = rows.map((r) => {
|
|
1606
1606
|
let c = {};
|
|
@@ -1638,26 +1638,26 @@ function handleDream(args) {
|
|
|
1638
1638
|
}
|
|
1639
1639
|
// 2. Fetch pending orphaned proposals
|
|
1640
1640
|
const candidateQuery = domainFilter
|
|
1641
|
-
? `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
|
|
1642
|
-
mc.rationale as candidate_rationale, mc.confidence,
|
|
1643
|
-
mc.evidence_refs, mc.status,
|
|
1644
|
-
datetime(mc.created_at, 'unixepoch') as flagged_at,
|
|
1645
|
-
da.statement, da.rationale as anchor_rationale, da.domain,
|
|
1646
|
-
da.evidence_refs as anchor_evidence
|
|
1647
|
-
FROM memory_write_candidates mc
|
|
1648
|
-
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1649
|
-
WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
|
|
1650
|
-
AND da.domain = ?
|
|
1641
|
+
? `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
|
|
1642
|
+
mc.rationale as candidate_rationale, mc.confidence,
|
|
1643
|
+
mc.evidence_refs, mc.status,
|
|
1644
|
+
datetime(mc.created_at, 'unixepoch') as flagged_at,
|
|
1645
|
+
da.statement, da.rationale as anchor_rationale, da.domain,
|
|
1646
|
+
da.evidence_refs as anchor_evidence
|
|
1647
|
+
FROM memory_write_candidates mc
|
|
1648
|
+
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1649
|
+
WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
|
|
1650
|
+
AND da.domain = ?
|
|
1651
1651
|
ORDER BY mc.created_at DESC`
|
|
1652
|
-
: `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
|
|
1653
|
-
mc.rationale as candidate_rationale, mc.confidence,
|
|
1654
|
-
mc.evidence_refs, mc.status,
|
|
1655
|
-
datetime(mc.created_at, 'unixepoch') as flagged_at,
|
|
1656
|
-
da.statement, da.rationale as anchor_rationale, da.domain,
|
|
1657
|
-
da.evidence_refs as anchor_evidence
|
|
1658
|
-
FROM memory_write_candidates mc
|
|
1659
|
-
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1660
|
-
WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
|
|
1652
|
+
: `SELECT mc.id as candidate_id, mc.target_node_id as anchor_id,
|
|
1653
|
+
mc.rationale as candidate_rationale, mc.confidence,
|
|
1654
|
+
mc.evidence_refs, mc.status,
|
|
1655
|
+
datetime(mc.created_at, 'unixepoch') as flagged_at,
|
|
1656
|
+
da.statement, da.rationale as anchor_rationale, da.domain,
|
|
1657
|
+
da.evidence_refs as anchor_evidence
|
|
1658
|
+
FROM memory_write_candidates mc
|
|
1659
|
+
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1660
|
+
WHERE mc.scope = 'orphaned_proposal' AND mc.status = 'pending_review'
|
|
1661
1661
|
ORDER BY mc.created_at DESC`;
|
|
1662
1662
|
const candidates = domainFilter
|
|
1663
1663
|
? db.prepare(candidateQuery).all(domainFilter)
|
|
@@ -1737,12 +1737,12 @@ function handleResolveProposal(args) {
|
|
|
1737
1737
|
throw new Error('verdict must be "surface" or "dismiss"');
|
|
1738
1738
|
}
|
|
1739
1739
|
// Verify candidate exists and is pending
|
|
1740
|
-
const candidate = db.prepare(`
|
|
1741
|
-
SELECT mc.id, mc.target_node_id, mc.status, mc.evidence_refs,
|
|
1742
|
-
da.statement
|
|
1743
|
-
FROM memory_write_candidates mc
|
|
1744
|
-
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1745
|
-
WHERE mc.id = ? AND mc.scope = 'orphaned_proposal'
|
|
1740
|
+
const candidate = db.prepare(`
|
|
1741
|
+
SELECT mc.id, mc.target_node_id, mc.status, mc.evidence_refs,
|
|
1742
|
+
da.statement
|
|
1743
|
+
FROM memory_write_candidates mc
|
|
1744
|
+
JOIN drift_anchors da ON mc.target_node_id = da.id
|
|
1745
|
+
WHERE mc.id = ? AND mc.scope = 'orphaned_proposal'
|
|
1746
1746
|
`).get(candidateId);
|
|
1747
1747
|
if (!candidate) {
|
|
1748
1748
|
throw new Error(`Candidate #${candidateId} not found or not an orphaned proposal`);
|
package/package.json
CHANGED
|
@@ -1,95 +1,96 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "linksee-memory",
|
|
3
|
-
"version": "0.12.
|
|
4
|
-
"mcpName": "io.github.michielinksee/linksee-memory",
|
|
5
|
-
"description": "Local-first agent memory MCP — cross-agent brain with drift detection, 6-layer structured memory + token-saving file diff cache",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"linksee-memory": "dist/mcp/server.js",
|
|
9
|
-
"linksee-memory-import": "dist/bin/import-sessions.js",
|
|
10
|
-
"linksee-memory-sync": "dist/bin/sync-session.js",
|
|
11
|
-
"linksee-memory-install-skill": "dist/bin/install-skill.js",
|
|
12
|
-
"linksee-memory-stats": "dist/bin/stats.js",
|
|
13
|
-
"linksee-memory-setup": "dist/bin/setup.js",
|
|
14
|
-
"linksee-memory-declare": "dist/bin/declare-anchor.js",
|
|
15
|
-
"linksee-memory-detect": "dist/bin/detect-drift.js",
|
|
16
|
-
"linksee-memory-guard": "dist/bin/guard-hook.js",
|
|
17
|
-
"linksee-memory-map": "dist/bin/map-import.js",
|
|
18
|
-
"linksee-memory-export": "dist/bin/export-report.js"
|
|
19
|
-
},
|
|
20
|
-
"main": "./dist/mcp/server.js",
|
|
21
|
-
"files": [
|
|
22
|
-
"dist/**/*.js",
|
|
23
|
-
"dist/**/*.d.ts",
|
|
24
|
-
"dist/db/schema.sql",
|
|
25
|
-
"dist/skill/SKILL.md",
|
|
26
|
-
"README.md",
|
|
27
|
-
"LICENSE"
|
|
28
|
-
],
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "tsc && node -e \"require('fs').copyFileSync('src/db/schema.sql','dist/db/schema.sql'); require('fs').mkdirSync('dist/skill',{recursive:true}); require('fs').copyFileSync('src/skill/SKILL.md','dist/skill/SKILL.md')\"",
|
|
31
|
-
"start": "node dist/mcp/server.js",
|
|
32
|
-
"dev": "tsx src/mcp/server.ts",
|
|
33
|
-
"migrate": "node dist/db/migrate.js",
|
|
34
|
-
"migrate:dev": "tsx src/db/migrate.ts",
|
|
35
|
-
"prepublishOnly": "node scripts/release-gate.mjs && npm run build"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"agent
|
|
43
|
-
"
|
|
44
|
-
"claude
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"@
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"@types/
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "linksee-memory",
|
|
3
|
+
"version": "0.12.1",
|
|
4
|
+
"mcpName": "io.github.michielinksee/linksee-memory",
|
|
5
|
+
"description": "Local-first agent memory MCP — cross-agent brain with drift detection, 6-layer structured memory + token-saving file diff cache",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"linksee-memory": "dist/mcp/server.js",
|
|
9
|
+
"linksee-memory-import": "dist/bin/import-sessions.js",
|
|
10
|
+
"linksee-memory-sync": "dist/bin/sync-session.js",
|
|
11
|
+
"linksee-memory-install-skill": "dist/bin/install-skill.js",
|
|
12
|
+
"linksee-memory-stats": "dist/bin/stats.js",
|
|
13
|
+
"linksee-memory-setup": "dist/bin/setup.js",
|
|
14
|
+
"linksee-memory-declare": "dist/bin/declare-anchor.js",
|
|
15
|
+
"linksee-memory-detect": "dist/bin/detect-drift.js",
|
|
16
|
+
"linksee-memory-guard": "dist/bin/guard-hook.js",
|
|
17
|
+
"linksee-memory-map": "dist/bin/map-import.js",
|
|
18
|
+
"linksee-memory-export": "dist/bin/export-report.js"
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/mcp/server.js",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/**/*.js",
|
|
23
|
+
"dist/**/*.d.ts",
|
|
24
|
+
"dist/db/schema.sql",
|
|
25
|
+
"dist/skill/SKILL.md",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc && node -e \"require('fs').copyFileSync('src/db/schema.sql','dist/db/schema.sql'); require('fs').mkdirSync('dist/skill',{recursive:true}); require('fs').copyFileSync('src/skill/SKILL.md','dist/skill/SKILL.md')\"",
|
|
31
|
+
"start": "node dist/mcp/server.js",
|
|
32
|
+
"dev": "tsx src/mcp/server.ts",
|
|
33
|
+
"migrate": "node dist/db/migrate.js",
|
|
34
|
+
"migrate:dev": "tsx src/db/migrate.ts",
|
|
35
|
+
"prepublishOnly": "node scripts/release-gate.mjs && npm run build",
|
|
36
|
+
"version": "node scripts/sync-version.mjs"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"mcp",
|
|
40
|
+
"model-context-protocol",
|
|
41
|
+
"memory",
|
|
42
|
+
"agent",
|
|
43
|
+
"agent-memory",
|
|
44
|
+
"claude",
|
|
45
|
+
"claude-code",
|
|
46
|
+
"cursor",
|
|
47
|
+
"chatgpt",
|
|
48
|
+
"sqlite",
|
|
49
|
+
"local-first",
|
|
50
|
+
"token-savings",
|
|
51
|
+
"cross-agent"
|
|
52
|
+
],
|
|
53
|
+
"author": "Synapse Arrows PTE. LTD.",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"homepage": "https://github.com/michielinksee/linksee-memory",
|
|
56
|
+
"repository": {
|
|
57
|
+
"type": "git",
|
|
58
|
+
"url": "git+https://github.com/michielinksee/linksee-memory.git"
|
|
59
|
+
},
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/michielinksee/linksee-memory/issues"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@babel/parser": "^7.25.0",
|
|
65
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
66
|
+
"better-sqlite3": "^12.9.0",
|
|
67
|
+
"sqlite-vec": "^0.1.6",
|
|
68
|
+
"yaml": "^2.9.0"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
72
|
+
"@types/node": "^22.7.0",
|
|
73
|
+
"tsx": "^4.19.0",
|
|
74
|
+
"typescript": "^5.6.0"
|
|
75
|
+
},
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": ">=20"
|
|
78
|
+
},
|
|
79
|
+
"overrides": {
|
|
80
|
+
"ip-address": "^10.2.0",
|
|
81
|
+
"hono": "^4.12.21",
|
|
82
|
+
"qs": "^6.15.2",
|
|
83
|
+
"esbuild": "^0.28.1"
|
|
84
|
+
},
|
|
85
|
+
"pnpm": {
|
|
86
|
+
"onlyBuiltDependencies": [
|
|
87
|
+
"better-sqlite3"
|
|
88
|
+
],
|
|
89
|
+
"overrides": {
|
|
90
|
+
"ip-address": "^10.2.0",
|
|
91
|
+
"hono": "^4.12.21",
|
|
92
|
+
"qs": "^6.15.2",
|
|
93
|
+
"esbuild": "^0.28.1"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|