@velvetmonkey/flywheel-memory 2.0.38 → 2.0.40
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/index.js +55 -6
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3506,9 +3506,11 @@ var FEEDBACK_BOOST_TIERS = [
|
|
|
3506
3506
|
];
|
|
3507
3507
|
function recordFeedback(stateDb2, entity, context, notePath, correct) {
|
|
3508
3508
|
try {
|
|
3509
|
-
|
|
3509
|
+
console.error(`[Flywheel] recordFeedback: entity="${entity}" context="${context}" notePath="${notePath}" correct=${correct}`);
|
|
3510
|
+
const result = stateDb2.db.prepare(
|
|
3510
3511
|
"INSERT INTO wikilink_feedback (entity, context, note_path, correct) VALUES (?, ?, ?, ?)"
|
|
3511
3512
|
).run(entity, context, notePath, correct ? 1 : 0);
|
|
3513
|
+
console.error(`[Flywheel] recordFeedback: inserted id=${result.lastInsertRowid}`);
|
|
3512
3514
|
} catch (e) {
|
|
3513
3515
|
console.error(`[Flywheel] recordFeedback failed for entity="${entity}": ${e}`);
|
|
3514
3516
|
throw e;
|
|
@@ -5387,6 +5389,9 @@ function getEffectiveStrictness(notePath) {
|
|
|
5387
5389
|
function getCooccurrenceIndex() {
|
|
5388
5390
|
return cooccurrenceIndex;
|
|
5389
5391
|
}
|
|
5392
|
+
function setCooccurrenceIndex(index) {
|
|
5393
|
+
cooccurrenceIndex = index;
|
|
5394
|
+
}
|
|
5390
5395
|
var entityIndex = null;
|
|
5391
5396
|
var indexReady = false;
|
|
5392
5397
|
var indexError2 = null;
|
|
@@ -5502,6 +5507,11 @@ function checkAndRefreshIfStale() {
|
|
|
5502
5507
|
console.error(`[Flywheel] Reloaded ${dbIndex._metadata.total_entities} entities`);
|
|
5503
5508
|
}
|
|
5504
5509
|
}
|
|
5510
|
+
const freshRecency = loadRecencyFromStateDb();
|
|
5511
|
+
if (freshRecency && freshRecency.lastUpdated > (recencyIndex?.lastUpdated ?? 0)) {
|
|
5512
|
+
recencyIndex = freshRecency;
|
|
5513
|
+
console.error(`[Flywheel] Refreshed recency index (${freshRecency.lastMentioned.size} entities)`);
|
|
5514
|
+
}
|
|
5505
5515
|
} catch (e) {
|
|
5506
5516
|
console.error("[Flywheel] Failed to check for stale entities:", e);
|
|
5507
5517
|
}
|
|
@@ -12164,6 +12174,13 @@ function normalizeInput(content, format) {
|
|
|
12164
12174
|
normalized = trimmed;
|
|
12165
12175
|
changes.push("Trimmed excessive blank lines");
|
|
12166
12176
|
}
|
|
12177
|
+
const multiLineWikilink = /\[\[([^\]]*\n[^\]]*)\]\]/g;
|
|
12178
|
+
if (multiLineWikilink.test(normalized)) {
|
|
12179
|
+
normalized = normalized.replace(multiLineWikilink, (_match, inner) => {
|
|
12180
|
+
return "[[" + inner.replace(/\s*\n\s*/g, " ").trim() + "]]";
|
|
12181
|
+
});
|
|
12182
|
+
changes.push("Fixed multi-line wikilinks");
|
|
12183
|
+
}
|
|
12167
12184
|
return {
|
|
12168
12185
|
content: normalized,
|
|
12169
12186
|
normalized: changes.length > 0,
|
|
@@ -15413,9 +15430,11 @@ function registerWikilinkFeedbackTools(server2, getStateDb) {
|
|
|
15413
15430
|
let result;
|
|
15414
15431
|
switch (mode) {
|
|
15415
15432
|
case "report": {
|
|
15433
|
+
console.error(`[Flywheel] wikilink_feedback report: entity="${entity}" correct=${JSON.stringify(correct)} (type: ${typeof correct})`);
|
|
15416
15434
|
if (!entity || correct === void 0) {
|
|
15417
15435
|
return {
|
|
15418
|
-
content: [{ type: "text", text: JSON.stringify({ error: "entity and correct are required for report mode" }) }]
|
|
15436
|
+
content: [{ type: "text", text: JSON.stringify({ error: "entity and correct are required for report mode" }) }],
|
|
15437
|
+
isError: true
|
|
15419
15438
|
};
|
|
15420
15439
|
}
|
|
15421
15440
|
try {
|
|
@@ -15424,7 +15443,8 @@ function registerWikilinkFeedbackTools(server2, getStateDb) {
|
|
|
15424
15443
|
return {
|
|
15425
15444
|
content: [{ type: "text", text: JSON.stringify({
|
|
15426
15445
|
error: `Failed to record feedback: ${e instanceof Error ? e.message : String(e)}`
|
|
15427
|
-
}) }]
|
|
15446
|
+
}) }],
|
|
15447
|
+
isError: true
|
|
15428
15448
|
};
|
|
15429
15449
|
}
|
|
15430
15450
|
const suppressionUpdated = updateSuppressionList(stateDb2) > 0;
|
|
@@ -17083,6 +17103,7 @@ async function main() {
|
|
|
17083
17103
|
}
|
|
17084
17104
|
}
|
|
17085
17105
|
var DEFAULT_ENTITY_EXCLUDE_FOLDERS = ["node_modules", "templates", "attachments", "tmp"];
|
|
17106
|
+
var lastCooccurrenceRebuildAt = 0;
|
|
17086
17107
|
async function updateEntitiesInStateDb() {
|
|
17087
17108
|
if (!stateDb) return;
|
|
17088
17109
|
try {
|
|
@@ -17369,6 +17390,7 @@ async function runPostIndexWork(index) {
|
|
|
17369
17390
|
const entitiesAfter = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
17370
17391
|
const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
|
|
17371
17392
|
const categoryChanges = [];
|
|
17393
|
+
const descriptionChanges = [];
|
|
17372
17394
|
if (stateDb) {
|
|
17373
17395
|
const beforeMap = new Map(entitiesBefore.map((e) => [e.name, e]));
|
|
17374
17396
|
const insertChange = stateDb.db.prepare(
|
|
@@ -17380,9 +17402,17 @@ async function runPostIndexWork(index) {
|
|
|
17380
17402
|
insertChange.run(after.name, "category", before.category, after.category);
|
|
17381
17403
|
categoryChanges.push({ entity: after.name, from: before.category, to: after.category });
|
|
17382
17404
|
}
|
|
17405
|
+
if (before) {
|
|
17406
|
+
const oldDesc = before.description ?? null;
|
|
17407
|
+
const newDesc = after.description ?? null;
|
|
17408
|
+
if (oldDesc !== newDesc) {
|
|
17409
|
+
insertChange.run(after.name, "description", oldDesc, newDesc);
|
|
17410
|
+
descriptionChanges.push({ entity: after.name, from: oldDesc, to: newDesc });
|
|
17411
|
+
}
|
|
17412
|
+
}
|
|
17383
17413
|
}
|
|
17384
17414
|
}
|
|
17385
|
-
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff, category_changes: categoryChanges });
|
|
17415
|
+
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff, category_changes: categoryChanges, description_changes: descriptionChanges });
|
|
17386
17416
|
serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
|
|
17387
17417
|
tracker.start("hub_scores", { entity_count: entitiesAfter.length });
|
|
17388
17418
|
const hubUpdated = await exportHubScores(vaultIndex, stateDb);
|
|
@@ -17414,6 +17444,24 @@ async function runPostIndexWork(index) {
|
|
|
17414
17444
|
tracker.end({ error: String(e) });
|
|
17415
17445
|
serverLog("watcher", `Recency: failed: ${e}`);
|
|
17416
17446
|
}
|
|
17447
|
+
tracker.start("cooccurrence", { entity_count: entitiesAfter.length });
|
|
17448
|
+
try {
|
|
17449
|
+
const cooccurrenceAgeMs = lastCooccurrenceRebuildAt > 0 ? Date.now() - lastCooccurrenceRebuildAt : Infinity;
|
|
17450
|
+
if (cooccurrenceAgeMs >= 60 * 60 * 1e3) {
|
|
17451
|
+
const entityNames = entitiesAfter.map((e) => e.name);
|
|
17452
|
+
const cooccurrenceIdx = await mineCooccurrences(vaultPath, entityNames);
|
|
17453
|
+
setCooccurrenceIndex(cooccurrenceIdx);
|
|
17454
|
+
lastCooccurrenceRebuildAt = Date.now();
|
|
17455
|
+
tracker.end({ rebuilt: true, associations: cooccurrenceIdx._metadata.total_associations });
|
|
17456
|
+
serverLog("watcher", `Co-occurrence: rebuilt ${cooccurrenceIdx._metadata.total_associations} associations`);
|
|
17457
|
+
} else {
|
|
17458
|
+
tracker.end({ rebuilt: false, age_ms: cooccurrenceAgeMs });
|
|
17459
|
+
serverLog("watcher", `Co-occurrence: cache valid (${Math.round(cooccurrenceAgeMs / 1e3)}s old)`);
|
|
17460
|
+
}
|
|
17461
|
+
} catch (e) {
|
|
17462
|
+
tracker.end({ error: String(e) });
|
|
17463
|
+
serverLog("watcher", `Co-occurrence: failed: ${e}`);
|
|
17464
|
+
}
|
|
17417
17465
|
if (hasEmbeddingsIndex()) {
|
|
17418
17466
|
tracker.start("note_embeddings", { files: filteredEvents.length });
|
|
17419
17467
|
let embUpdated = 0;
|
|
@@ -17521,6 +17569,7 @@ async function runPostIndexWork(index) {
|
|
|
17521
17569
|
}
|
|
17522
17570
|
}
|
|
17523
17571
|
const linkDiffs = [];
|
|
17572
|
+
const survivedLinks = [];
|
|
17524
17573
|
if (stateDb) {
|
|
17525
17574
|
const upsertHistory = stateDb.db.prepare(`
|
|
17526
17575
|
INSERT INTO note_link_history (note_path, target) VALUES (?, ?)
|
|
@@ -17536,7 +17585,6 @@ async function runPostIndexWork(index) {
|
|
|
17536
17585
|
const getEdgeCount = stateDb.db.prepare(
|
|
17537
17586
|
"SELECT edits_survived FROM note_link_history WHERE note_path=? AND target=?"
|
|
17538
17587
|
);
|
|
17539
|
-
const survivedLinks2 = [];
|
|
17540
17588
|
for (const entry of forwardLinkResults) {
|
|
17541
17589
|
const currentSet = /* @__PURE__ */ new Set([
|
|
17542
17590
|
...entry.resolved.map((n) => n.toLowerCase()),
|
|
@@ -17552,12 +17600,13 @@ async function runPostIndexWork(index) {
|
|
|
17552
17600
|
linkDiffs.push({ file: entry.file, ...diff });
|
|
17553
17601
|
}
|
|
17554
17602
|
updateStoredNoteLinks(stateDb, entry.file, currentSet);
|
|
17603
|
+
if (diff.removed.length === 0) continue;
|
|
17555
17604
|
for (const link of currentSet) {
|
|
17556
17605
|
if (!previousSet.has(link)) continue;
|
|
17557
17606
|
upsertHistory.run(entry.file, link);
|
|
17558
17607
|
const countRow = getEdgeCount.get(entry.file, link);
|
|
17559
17608
|
if (countRow) {
|
|
17560
|
-
|
|
17609
|
+
survivedLinks.push({ entity: link, file: entry.file, count: countRow.edits_survived });
|
|
17561
17610
|
}
|
|
17562
17611
|
const hit = checkThreshold.get(entry.file, link);
|
|
17563
17612
|
if (hit) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.40",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. 42 tools for search, backlinks, graph queries, mutations, and hybrid semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
55
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
55
|
+
"@velvetmonkey/vault-core": "^2.0.40",
|
|
56
56
|
"better-sqlite3": "^11.0.0",
|
|
57
57
|
"chokidar": "^4.0.0",
|
|
58
58
|
"gray-matter": "^4.0.3",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=18.0.0"
|
|
76
76
|
},
|
|
77
|
-
"license": "
|
|
77
|
+
"license": "Apache-2.0",
|
|
78
78
|
"files": [
|
|
79
79
|
"dist",
|
|
80
80
|
"README.md",
|