@velvetmonkey/flywheel-memory 2.0.39 → 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.
Files changed (2) hide show
  1. package/dist/index.js +43 -3
  2. package/package.json +2 -2
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
- stateDb2.db.prepare(
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 {
@@ -17423,6 +17444,24 @@ async function runPostIndexWork(index) {
17423
17444
  tracker.end({ error: String(e) });
17424
17445
  serverLog("watcher", `Recency: failed: ${e}`);
17425
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
+ }
17426
17465
  if (hasEmbeddingsIndex()) {
17427
17466
  tracker.start("note_embeddings", { files: filteredEvents.length });
17428
17467
  let embUpdated = 0;
@@ -17561,6 +17600,7 @@ async function runPostIndexWork(index) {
17561
17600
  linkDiffs.push({ file: entry.file, ...diff });
17562
17601
  }
17563
17602
  updateStoredNoteLinks(stateDb, entry.file, currentSet);
17603
+ if (diff.removed.length === 0) continue;
17564
17604
  for (const link of currentSet) {
17565
17605
  if (!previousSet.has(link)) continue;
17566
17606
  upsertHistory.run(entry.file, link);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.39",
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.39",
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",