@velvetmonkey/flywheel-memory 2.0.152 → 2.0.153

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 +39 -4
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -8104,7 +8104,7 @@ function computeHubScores(index) {
8104
8104
  }
8105
8105
  }
8106
8106
  const N = nodes.length;
8107
- if (N === 0) return /* @__PURE__ */ new Map();
8107
+ if (N === 0) return { scores: /* @__PURE__ */ new Map(), edgeCount: 0 };
8108
8108
  for (const note of index.notes.values()) {
8109
8109
  const fromIdx = nodeIdx.get(normalizeTarget(note.path));
8110
8110
  if (fromIdx === void 0) continue;
@@ -8120,6 +8120,11 @@ function computeHubScores(index) {
8120
8120
  }
8121
8121
  }
8122
8122
  }
8123
+ const edgeCount = adj.reduce((sum, a) => sum + a.length, 0);
8124
+ if (edgeCount === 0) {
8125
+ console.error(`[Flywheel] Hub scores: 0 edges in graph of ${N} nodes, skipping eigenvector`);
8126
+ return { scores: /* @__PURE__ */ new Map(), edgeCount: 0 };
8127
+ }
8123
8128
  let scores = new Float64Array(N).fill(1 / N);
8124
8129
  for (let iter = 0; iter < EIGEN_ITERATIONS; iter++) {
8125
8130
  const next = new Float64Array(N);
@@ -8147,7 +8152,21 @@ function computeHubScores(index) {
8147
8152
  result.set(nodes[i], scaled);
8148
8153
  }
8149
8154
  }
8150
- return result;
8155
+ if (result.size < N * 0.1 && edgeCount > 0) {
8156
+ console.error(`[Flywheel] Hub scores: eigenvector too sparse (${result.size}/${N}), falling back to degree centrality`);
8157
+ result.clear();
8158
+ let maxDegree = 0;
8159
+ for (let i = 0; i < N; i++) {
8160
+ if (adj[i].length > maxDegree) maxDegree = adj[i].length;
8161
+ }
8162
+ if (maxDegree > 0) {
8163
+ for (let i = 0; i < N; i++) {
8164
+ const scaled = Math.round(adj[i].length / maxDegree * 100);
8165
+ if (scaled > 0) result.set(nodes[i], scaled);
8166
+ }
8167
+ }
8168
+ }
8169
+ return { scores: result, edgeCount };
8151
8170
  }
8152
8171
  function updateHubScoresInDb(stateDb2, hubScores) {
8153
8172
  const updateStmt = stateDb2.db.prepare(`
@@ -8170,8 +8189,8 @@ async function exportHubScores(vaultIndex2, stateDb2) {
8170
8189
  console.error("[Flywheel] No StateDb available, skipping hub score export");
8171
8190
  return 0;
8172
8191
  }
8173
- const hubScores = computeHubScores(vaultIndex2);
8174
- console.error(`[Flywheel] Computed hub scores for ${hubScores.size} notes`);
8192
+ const { scores: hubScores, edgeCount } = computeHubScores(vaultIndex2);
8193
+ console.error(`[Flywheel] Computed hub scores for ${hubScores.size} notes (${edgeCount} edges in graph)`);
8175
8194
  try {
8176
8195
  const updated = updateHubScoresInDb(stateDb2, hubScores);
8177
8196
  console.error(`[Flywheel] Updated ${updated} hub scores in StateDb`);
@@ -8888,6 +8907,7 @@ var PipelineRunner = class {
8888
8907
  await runStep("tag_scan", tracker, { files: p.events.length }, () => this.tagScan());
8889
8908
  await runStep("retrieval_cooccurrence", tracker, {}, () => this.retrievalCooccurrence());
8890
8909
  await runStep("integrity_check", tracker, {}, () => this.integrityCheck());
8910
+ await runStep("maintenance", tracker, {}, () => this.maintenance());
8891
8911
  const duration = Date.now() - this.batchStart;
8892
8912
  if (p.sd) {
8893
8913
  recordIndexEvent(p.sd, {
@@ -9690,6 +9710,21 @@ var PipelineRunner = class {
9690
9710
  return { integrity: "failed", detail: result.detail };
9691
9711
  }
9692
9712
  }
9713
+ // ── Maintenance: periodic incremental vacuum ─────────────────────
9714
+ async maintenance() {
9715
+ const { p } = this;
9716
+ if (!p.sd) return { skipped: true, reason: "no statedb" };
9717
+ const VACUUM_INTERVAL_MS = 60 * 60 * 1e3;
9718
+ const lastRow = p.sd.getMetadataValue.get("last_incremental_vacuum");
9719
+ const lastVacuum = lastRow ? parseInt(lastRow.value, 10) : 0;
9720
+ if (Date.now() - lastVacuum < VACUUM_INTERVAL_MS) {
9721
+ return { skipped: true, reason: "vacuumed recently" };
9722
+ }
9723
+ p.sd.db.pragma("incremental_vacuum");
9724
+ p.sd.setMetadataValue.run("last_incremental_vacuum", String(Date.now()));
9725
+ serverLog("watcher", "Incremental vacuum completed");
9726
+ return { vacuumed: true };
9727
+ }
9693
9728
  };
9694
9729
 
9695
9730
  // src/core/read/logging.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.152",
3
+ "version": "2.0.153",
4
4
  "description": "MCP tools that search, write, and auto-link your Obsidian vault — and learn from your edits.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,7 +54,7 @@
54
54
  "dependencies": {
55
55
  "@huggingface/transformers": "^3.8.1",
56
56
  "@modelcontextprotocol/sdk": "^1.25.1",
57
- "@velvetmonkey/vault-core": "^2.0.152",
57
+ "@velvetmonkey/vault-core": "^2.0.153",
58
58
  "better-sqlite3": "^12.0.0",
59
59
  "chokidar": "^4.0.0",
60
60
  "gray-matter": "^4.0.3",