@velvetmonkey/flywheel-memory 2.0.155 → 2.0.156
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 +212 -49
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6516,6 +6516,7 @@ import {
|
|
|
6516
6516
|
loadFlywheelConfigFromDb,
|
|
6517
6517
|
saveFlywheelConfigToDb
|
|
6518
6518
|
} from "@velvetmonkey/vault-core";
|
|
6519
|
+
var DEFAULT_ENTITY_EXCLUDE_FOLDERS = ["node_modules", "templates", "attachments", "tmp"];
|
|
6519
6520
|
var DEFAULT_CONFIG = {
|
|
6520
6521
|
exclude: [],
|
|
6521
6522
|
exclude_entity_folders: [],
|
|
@@ -7238,6 +7239,7 @@ function addNoteToIndex(index, note) {
|
|
|
7238
7239
|
async function upsertNote(index, vaultPath2, notePath) {
|
|
7239
7240
|
try {
|
|
7240
7241
|
const existed = index.notes.has(notePath);
|
|
7242
|
+
const oldNote = existed ? index.notes.get(notePath) : void 0;
|
|
7241
7243
|
let releasedKeys = [];
|
|
7242
7244
|
if (existed) {
|
|
7243
7245
|
releasedKeys = removeNoteFromIndex(index, notePath);
|
|
@@ -7255,10 +7257,12 @@ async function upsertNote(index, vaultPath2, notePath) {
|
|
|
7255
7257
|
if (releasedKeys.length > 0) {
|
|
7256
7258
|
reconcileReleasedKeys(index, releasedKeys);
|
|
7257
7259
|
}
|
|
7260
|
+
const entityFieldChanged = !oldNote || (oldNote.title !== note.title || JSON.stringify(oldNote.aliases) !== JSON.stringify(note.aliases) || oldNote.frontmatter.type !== note.frontmatter.type);
|
|
7258
7261
|
return {
|
|
7259
7262
|
success: true,
|
|
7260
7263
|
action: existed ? "updated" : "added",
|
|
7261
|
-
path: notePath
|
|
7264
|
+
path: notePath,
|
|
7265
|
+
entityFieldChanged
|
|
7262
7266
|
};
|
|
7263
7267
|
} catch (error) {
|
|
7264
7268
|
return {
|
|
@@ -7300,7 +7304,8 @@ async function processBatch(index, vaultPath2, batch, options = {}) {
|
|
|
7300
7304
|
successful: 0,
|
|
7301
7305
|
failed: 0,
|
|
7302
7306
|
results: [],
|
|
7303
|
-
durationMs: 0
|
|
7307
|
+
durationMs: 0,
|
|
7308
|
+
hasEntityRelevantChanges: false
|
|
7304
7309
|
};
|
|
7305
7310
|
}
|
|
7306
7311
|
console.error(`[flywheel] Processing ${total} file events`);
|
|
@@ -7355,7 +7360,8 @@ async function processBatch(index, vaultPath2, batch, options = {}) {
|
|
|
7355
7360
|
successful,
|
|
7356
7361
|
failed,
|
|
7357
7362
|
results,
|
|
7358
|
-
durationMs
|
|
7363
|
+
durationMs,
|
|
7364
|
+
hasEntityRelevantChanges: results.some((r) => r.entityFieldChanged)
|
|
7359
7365
|
};
|
|
7360
7366
|
}
|
|
7361
7367
|
|
|
@@ -8427,6 +8433,7 @@ var PipelineRunner = class {
|
|
|
8427
8433
|
entitiesAfter = [];
|
|
8428
8434
|
entitiesBefore = [];
|
|
8429
8435
|
hubBefore = /* @__PURE__ */ new Map();
|
|
8436
|
+
hasEntityRelevantChanges = false;
|
|
8430
8437
|
forwardLinkResults = [];
|
|
8431
8438
|
linkDiffs = [];
|
|
8432
8439
|
survivedLinks = [];
|
|
@@ -8498,6 +8505,7 @@ var PipelineRunner = class {
|
|
|
8498
8505
|
if (!vaultIndex2) {
|
|
8499
8506
|
const rebuilt = await p.buildVaultIndex(p.vp);
|
|
8500
8507
|
p.updateVaultIndex(rebuilt);
|
|
8508
|
+
this.hasEntityRelevantChanges = true;
|
|
8501
8509
|
serverLog("watcher", `Index rebuilt (full): ${rebuilt.notes.size} notes, ${rebuilt.entities.size} entities`);
|
|
8502
8510
|
} else {
|
|
8503
8511
|
const absoluteBatch = {
|
|
@@ -8505,6 +8513,7 @@ var PipelineRunner = class {
|
|
|
8505
8513
|
events: p.events.map((e) => ({ ...e, path: path15.join(p.vp, e.path) }))
|
|
8506
8514
|
};
|
|
8507
8515
|
const batchResult = await processBatch(vaultIndex2, p.vp, absoluteBatch);
|
|
8516
|
+
this.hasEntityRelevantChanges = batchResult.hasEntityRelevantChanges;
|
|
8508
8517
|
serverLog("watcher", `Incremental: ${batchResult.successful}/${batchResult.total} files in ${batchResult.durationMs}ms`);
|
|
8509
8518
|
}
|
|
8510
8519
|
p.updateIndexState("ready");
|
|
@@ -8551,7 +8560,7 @@ var PipelineRunner = class {
|
|
|
8551
8560
|
for (const r of rows) this.hubBefore.set(r.name, r.hub_score);
|
|
8552
8561
|
}
|
|
8553
8562
|
const entityScanAgeMs = p.ctx.lastEntityScanAt > 0 ? Date.now() - p.ctx.lastEntityScanAt : Infinity;
|
|
8554
|
-
if (entityScanAgeMs < 5 * 60 * 1e3) {
|
|
8563
|
+
if (entityScanAgeMs < 5 * 60 * 1e3 && !this.hasEntityRelevantChanges) {
|
|
8555
8564
|
tracker.start("entity_scan", {});
|
|
8556
8565
|
tracker.skip("entity_scan", `cache valid (${Math.round(entityScanAgeMs / 1e3)}s old)`);
|
|
8557
8566
|
this.entitiesBefore = p.sd ? getAllEntitiesFromDb(p.sd) : [];
|
|
@@ -9329,7 +9338,7 @@ async function flushLogs() {
|
|
|
9329
9338
|
|
|
9330
9339
|
// src/index.ts
|
|
9331
9340
|
init_embeddings();
|
|
9332
|
-
import { openStateDb, scanVaultEntities as scanVaultEntities4, getAllEntitiesFromDb as
|
|
9341
|
+
import { openStateDb, scanVaultEntities as scanVaultEntities4, getAllEntitiesFromDb as getAllEntitiesFromDb6, loadContentHashes, saveContentHashBatch, renameContentHash, checkDbIntegrity as checkDbIntegrity2, safeBackupAsync as safeBackupAsync2, preserveCorruptedDb, deleteStateDbFiles, attemptSalvage } from "@velvetmonkey/vault-core";
|
|
9333
9342
|
|
|
9334
9343
|
// src/core/write/memory.ts
|
|
9335
9344
|
init_wikilinkFeedback();
|
|
@@ -10723,6 +10732,20 @@ import { z } from "zod";
|
|
|
10723
10732
|
// src/core/read/constants.ts
|
|
10724
10733
|
var MAX_LIMIT = 200;
|
|
10725
10734
|
|
|
10735
|
+
// src/core/read/indexGuard.ts
|
|
10736
|
+
function requireIndex() {
|
|
10737
|
+
const state2 = getIndexState();
|
|
10738
|
+
if (state2 === "building") {
|
|
10739
|
+
const { parsed, total } = getIndexProgress();
|
|
10740
|
+
const progress = total > 0 ? ` (${parsed}/${total} files)` : "";
|
|
10741
|
+
throw new Error(`Index building${progress}... try again shortly`);
|
|
10742
|
+
}
|
|
10743
|
+
if (state2 === "error") {
|
|
10744
|
+
const error = getIndexError();
|
|
10745
|
+
throw new Error(`Index failed to build: ${error?.message || "unknown error"}`);
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
|
|
10726
10749
|
// src/tools/read/query.ts
|
|
10727
10750
|
init_embeddings();
|
|
10728
10751
|
import {
|
|
@@ -11747,6 +11770,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
|
11747
11770
|
consumer: z.enum(["llm", "human"]).default("llm").describe('Output format: "llm" applies sandwich ordering and strips scoring fields for context efficiency. "human" preserves score order and all scoring metadata for UI display.')
|
|
11748
11771
|
},
|
|
11749
11772
|
async ({ query, where, has_tag, has_any_tag, has_all_tags, include_children, folder, title_contains, modified_after, modified_before, sort_by, order, prefix, limit: requestedLimit, detail_count: requestedDetailCount, context_note, consumer }) => {
|
|
11773
|
+
requireIndex();
|
|
11750
11774
|
const limit = Math.min(requestedLimit ?? 10, MAX_LIMIT);
|
|
11751
11775
|
const detailN = requestedDetailCount ?? 5;
|
|
11752
11776
|
const index = getIndex();
|
|
@@ -12466,20 +12490,6 @@ function detectCycles(index, maxLength = 10, limit = 20) {
|
|
|
12466
12490
|
return cycles;
|
|
12467
12491
|
}
|
|
12468
12492
|
|
|
12469
|
-
// src/core/read/indexGuard.ts
|
|
12470
|
-
function requireIndex() {
|
|
12471
|
-
const state2 = getIndexState();
|
|
12472
|
-
if (state2 === "building") {
|
|
12473
|
-
const { parsed, total } = getIndexProgress();
|
|
12474
|
-
const progress = total > 0 ? ` (${parsed}/${total} files)` : "";
|
|
12475
|
-
throw new Error(`Index building${progress}... try again shortly`);
|
|
12476
|
-
}
|
|
12477
|
-
if (state2 === "error") {
|
|
12478
|
-
const error = getIndexError();
|
|
12479
|
-
throw new Error(`Index failed to build: ${error?.message || "unknown error"}`);
|
|
12480
|
-
}
|
|
12481
|
-
}
|
|
12482
|
-
|
|
12483
12493
|
// src/tools/read/graph.ts
|
|
12484
12494
|
async function getContext(vaultPath2, sourcePath, line, contextLines = 1) {
|
|
12485
12495
|
try {
|
|
@@ -14656,7 +14666,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
14656
14666
|
import * as fs15 from "fs";
|
|
14657
14667
|
import * as path18 from "path";
|
|
14658
14668
|
import { z as z6 } from "zod";
|
|
14659
|
-
import { scanVaultEntities as scanVaultEntities2, getEntityIndexFromDb as getEntityIndexFromDb2 } from "@velvetmonkey/vault-core";
|
|
14669
|
+
import { scanVaultEntities as scanVaultEntities2, getEntityIndexFromDb as getEntityIndexFromDb2, getAllEntitiesFromDb as getAllEntitiesFromDb3 } from "@velvetmonkey/vault-core";
|
|
14660
14670
|
|
|
14661
14671
|
// src/core/read/aliasSuggestions.ts
|
|
14662
14672
|
import { STOPWORDS_EN as STOPWORDS_EN3 } from "@velvetmonkey/vault-core";
|
|
@@ -14726,6 +14736,10 @@ function suggestEntityAliases(stateDb2, folder) {
|
|
|
14726
14736
|
// src/tools/read/system.ts
|
|
14727
14737
|
init_edgeWeights();
|
|
14728
14738
|
init_embeddings();
|
|
14739
|
+
init_wikilinks();
|
|
14740
|
+
init_wikilinkFeedback();
|
|
14741
|
+
init_recency();
|
|
14742
|
+
init_cooccurrence();
|
|
14729
14743
|
function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfig, getStateDb3) {
|
|
14730
14744
|
const RefreshIndexOutputSchema = {
|
|
14731
14745
|
success: z6.boolean().describe("Whether the refresh succeeded"),
|
|
@@ -14733,7 +14747,15 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
14733
14747
|
entities_count: z6.number().describe("Number of entities (titles + aliases)"),
|
|
14734
14748
|
fts5_notes: z6.number().describe("Number of notes in FTS5 search index"),
|
|
14735
14749
|
edges_recomputed: z6.number().optional().describe("Number of edges with recomputed weights"),
|
|
14750
|
+
hub_scores: z6.number().optional().describe("Number of hub scores exported"),
|
|
14751
|
+
graph_snapshot: z6.boolean().optional().describe("Whether graph topology snapshot was recorded"),
|
|
14752
|
+
suppression_list: z6.boolean().optional().describe("Whether wikilink suppression list was updated"),
|
|
14753
|
+
task_cache: z6.boolean().optional().describe("Whether task cache was refreshed"),
|
|
14736
14754
|
embeddings_refreshed: z6.number().optional().describe("Number of note embeddings updated"),
|
|
14755
|
+
entity_embeddings_refreshed: z6.number().optional().describe("Number of entity embeddings updated"),
|
|
14756
|
+
recency_rebuilt: z6.boolean().optional().describe("Whether recency index was rebuilt"),
|
|
14757
|
+
cooccurrence_associations: z6.number().optional().describe("Number of co-occurrence associations rebuilt"),
|
|
14758
|
+
index_cached: z6.boolean().optional().describe("Whether vault index cache was saved"),
|
|
14737
14759
|
duration_ms: z6.number().describe("Time taken to rebuild index")
|
|
14738
14760
|
};
|
|
14739
14761
|
server2.registerTool(
|
|
@@ -14747,90 +14769,224 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
14747
14769
|
async () => {
|
|
14748
14770
|
const vaultPath2 = getVaultPath();
|
|
14749
14771
|
const startTime = Date.now();
|
|
14772
|
+
const tracker = createStepTracker();
|
|
14750
14773
|
setIndexState("building");
|
|
14751
14774
|
setIndexError(null);
|
|
14752
14775
|
try {
|
|
14776
|
+
tracker.start("vault_index", {});
|
|
14753
14777
|
const newIndex = await buildVaultIndex(vaultPath2);
|
|
14754
14778
|
setIndex(newIndex);
|
|
14755
14779
|
setIndexState("ready");
|
|
14780
|
+
tracker.end({ notes: newIndex.notes.size, entities: newIndex.entities.size });
|
|
14756
14781
|
const stateDb2 = getStateDb3?.();
|
|
14757
14782
|
if (stateDb2) {
|
|
14783
|
+
tracker.start("entity_sync", {});
|
|
14758
14784
|
try {
|
|
14759
14785
|
const config = loadConfig(stateDb2);
|
|
14786
|
+
const excludeFolders = config.exclude_entity_folders?.length ? config.exclude_entity_folders : DEFAULT_ENTITY_EXCLUDE_FOLDERS;
|
|
14760
14787
|
const entityIndex2 = await scanVaultEntities2(vaultPath2, {
|
|
14761
|
-
excludeFolders
|
|
14762
|
-
"daily-notes",
|
|
14763
|
-
"daily",
|
|
14764
|
-
"weekly",
|
|
14765
|
-
"weekly-notes",
|
|
14766
|
-
"monthly",
|
|
14767
|
-
"monthly-notes",
|
|
14768
|
-
"quarterly",
|
|
14769
|
-
"yearly-notes",
|
|
14770
|
-
"periodic",
|
|
14771
|
-
"journal",
|
|
14772
|
-
"inbox",
|
|
14773
|
-
"templates",
|
|
14774
|
-
"attachments",
|
|
14775
|
-
"tmp",
|
|
14776
|
-
"clippings",
|
|
14777
|
-
"readwise",
|
|
14778
|
-
"articles",
|
|
14779
|
-
"bookmarks",
|
|
14780
|
-
"web-clips"
|
|
14781
|
-
],
|
|
14788
|
+
excludeFolders,
|
|
14782
14789
|
customCategories: config.custom_categories
|
|
14783
14790
|
});
|
|
14784
14791
|
stateDb2.replaceAllEntities(entityIndex2);
|
|
14792
|
+
tracker.end({ entities: entityIndex2._metadata.total_entities });
|
|
14785
14793
|
console.error(`[Flywheel] Updated ${entityIndex2._metadata.total_entities} entities in StateDb`);
|
|
14786
14794
|
} catch (e) {
|
|
14795
|
+
tracker.end({ error: String(e) });
|
|
14787
14796
|
console.error("[Flywheel] Failed to update entities:", e);
|
|
14788
14797
|
}
|
|
14789
14798
|
}
|
|
14799
|
+
let flywheelConfig2;
|
|
14790
14800
|
if (setConfig) {
|
|
14801
|
+
tracker.start("config_merge", {});
|
|
14791
14802
|
const existing = loadConfig(stateDb2);
|
|
14792
14803
|
const inferred = inferConfig(newIndex, vaultPath2);
|
|
14793
14804
|
if (stateDb2) {
|
|
14794
14805
|
saveConfig(stateDb2, inferred, existing);
|
|
14795
14806
|
}
|
|
14796
|
-
|
|
14807
|
+
flywheelConfig2 = loadConfig(stateDb2);
|
|
14808
|
+
setConfig(flywheelConfig2);
|
|
14809
|
+
tracker.end({});
|
|
14797
14810
|
}
|
|
14798
14811
|
let fts5Notes = 0;
|
|
14812
|
+
tracker.start("fts5_rebuild", {});
|
|
14799
14813
|
try {
|
|
14800
14814
|
const ftsState = await buildFTS5Index(vaultPath2);
|
|
14801
14815
|
fts5Notes = ftsState.noteCount;
|
|
14816
|
+
tracker.end({ notes: fts5Notes });
|
|
14802
14817
|
console.error(`[Flywheel] FTS5 index rebuilt: ${fts5Notes} notes`);
|
|
14803
14818
|
} catch (err) {
|
|
14819
|
+
tracker.end({ error: String(err) });
|
|
14804
14820
|
console.error("[Flywheel] FTS5 rebuild failed:", err);
|
|
14805
14821
|
}
|
|
14806
14822
|
let edgesRecomputed = 0;
|
|
14807
14823
|
if (stateDb2) {
|
|
14824
|
+
tracker.start("edge_weights", {});
|
|
14808
14825
|
try {
|
|
14809
14826
|
const edgeResult = recomputeEdgeWeights(stateDb2);
|
|
14810
14827
|
edgesRecomputed = edgeResult.edges_updated;
|
|
14828
|
+
tracker.end({ edges: edgeResult.edges_updated, duration_ms: edgeResult.duration_ms });
|
|
14811
14829
|
console.error(`[Flywheel] Edge weights: ${edgeResult.edges_updated} edges in ${edgeResult.duration_ms}ms`);
|
|
14812
14830
|
} catch (err) {
|
|
14831
|
+
tracker.end({ error: String(err) });
|
|
14813
14832
|
console.error("[Flywheel] Edge weight recompute failed:", err);
|
|
14814
14833
|
}
|
|
14815
14834
|
}
|
|
14835
|
+
tracker.start("entity_index_init", {});
|
|
14836
|
+
try {
|
|
14837
|
+
await initializeEntityIndex(vaultPath2);
|
|
14838
|
+
tracker.end({});
|
|
14839
|
+
console.error("[Flywheel] Entity index initialized");
|
|
14840
|
+
} catch (err) {
|
|
14841
|
+
tracker.end({ error: String(err) });
|
|
14842
|
+
console.error("[Flywheel] Entity index init failed:", err);
|
|
14843
|
+
}
|
|
14844
|
+
let hubScoresExported = 0;
|
|
14845
|
+
tracker.start("hub_scores", {});
|
|
14846
|
+
try {
|
|
14847
|
+
hubScoresExported = await exportHubScores(newIndex, stateDb2);
|
|
14848
|
+
tracker.end({ exported: hubScoresExported });
|
|
14849
|
+
if (hubScoresExported > 0) {
|
|
14850
|
+
console.error(`[Flywheel] Hub scores: ${hubScoresExported} entities`);
|
|
14851
|
+
}
|
|
14852
|
+
} catch (err) {
|
|
14853
|
+
tracker.end({ error: String(err) });
|
|
14854
|
+
console.error("[Flywheel] Hub score export failed:", err);
|
|
14855
|
+
}
|
|
14856
|
+
let graphSnapshotRecorded = false;
|
|
14857
|
+
if (stateDb2) {
|
|
14858
|
+
tracker.start("graph_snapshot", {});
|
|
14859
|
+
try {
|
|
14860
|
+
const graphMetrics = computeGraphMetrics(newIndex);
|
|
14861
|
+
recordGraphSnapshot(stateDb2, graphMetrics);
|
|
14862
|
+
graphSnapshotRecorded = true;
|
|
14863
|
+
tracker.end({ recorded: true });
|
|
14864
|
+
} catch (err) {
|
|
14865
|
+
tracker.end({ error: String(err) });
|
|
14866
|
+
console.error("[Flywheel] Graph snapshot failed:", err);
|
|
14867
|
+
}
|
|
14868
|
+
}
|
|
14869
|
+
let suppressionUpdated = false;
|
|
14870
|
+
if (stateDb2) {
|
|
14871
|
+
tracker.start("suppression_list", {});
|
|
14872
|
+
try {
|
|
14873
|
+
updateSuppressionList(stateDb2);
|
|
14874
|
+
suppressionUpdated = true;
|
|
14875
|
+
tracker.end({ updated: true });
|
|
14876
|
+
} catch (err) {
|
|
14877
|
+
tracker.end({ error: String(err) });
|
|
14878
|
+
console.error("[Flywheel] Suppression list update failed:", err);
|
|
14879
|
+
}
|
|
14880
|
+
}
|
|
14881
|
+
let recencyRebuilt = false;
|
|
14882
|
+
if (stateDb2) {
|
|
14883
|
+
tracker.start("recency", {});
|
|
14884
|
+
try {
|
|
14885
|
+
const entities = getAllEntitiesFromDb3(stateDb2).map((e) => ({
|
|
14886
|
+
name: e.name,
|
|
14887
|
+
path: e.path,
|
|
14888
|
+
aliases: e.aliases
|
|
14889
|
+
}));
|
|
14890
|
+
const recencyIndex2 = await buildRecencyIndex(vaultPath2, entities);
|
|
14891
|
+
saveRecencyToStateDb(recencyIndex2, stateDb2);
|
|
14892
|
+
recencyRebuilt = true;
|
|
14893
|
+
tracker.end({ entities: recencyIndex2.lastMentioned.size });
|
|
14894
|
+
console.error(`[Flywheel] Recency: rebuilt ${recencyIndex2.lastMentioned.size} entities`);
|
|
14895
|
+
} catch (err) {
|
|
14896
|
+
tracker.end({ error: String(err) });
|
|
14897
|
+
console.error("[Flywheel] Recency rebuild failed:", err);
|
|
14898
|
+
}
|
|
14899
|
+
}
|
|
14900
|
+
let cooccurrenceAssociations;
|
|
14901
|
+
if (stateDb2) {
|
|
14902
|
+
tracker.start("cooccurrence", {});
|
|
14903
|
+
try {
|
|
14904
|
+
const entityNames = getAllEntitiesFromDb3(stateDb2).map((e) => e.name);
|
|
14905
|
+
const cooccurrenceIdx = await mineCooccurrences(vaultPath2, entityNames);
|
|
14906
|
+
setCooccurrenceIndex(cooccurrenceIdx);
|
|
14907
|
+
saveCooccurrenceToStateDb(stateDb2, cooccurrenceIdx);
|
|
14908
|
+
cooccurrenceAssociations = cooccurrenceIdx._metadata.total_associations;
|
|
14909
|
+
tracker.end({ associations: cooccurrenceAssociations });
|
|
14910
|
+
console.error(`[Flywheel] Co-occurrence: rebuilt ${cooccurrenceAssociations} associations`);
|
|
14911
|
+
} catch (err) {
|
|
14912
|
+
tracker.end({ error: String(err) });
|
|
14913
|
+
console.error("[Flywheel] Co-occurrence rebuild failed:", err);
|
|
14914
|
+
}
|
|
14915
|
+
}
|
|
14916
|
+
let taskCacheRefreshed = false;
|
|
14917
|
+
tracker.start("task_cache", {});
|
|
14918
|
+
try {
|
|
14919
|
+
if (!flywheelConfig2) {
|
|
14920
|
+
flywheelConfig2 = loadConfig(stateDb2);
|
|
14921
|
+
}
|
|
14922
|
+
await buildTaskCache(vaultPath2, newIndex, getExcludeTags(flywheelConfig2));
|
|
14923
|
+
taskCacheRefreshed = true;
|
|
14924
|
+
tracker.end({ rebuilt: true });
|
|
14925
|
+
console.error("[Flywheel] Task cache rebuilt");
|
|
14926
|
+
} catch (err) {
|
|
14927
|
+
tracker.end({ error: String(err) });
|
|
14928
|
+
console.error("[Flywheel] Task cache rebuild failed:", err);
|
|
14929
|
+
}
|
|
14816
14930
|
let embeddingsRefreshed = 0;
|
|
14817
14931
|
if (hasEmbeddingsIndex()) {
|
|
14932
|
+
tracker.start("embeddings_sync", {});
|
|
14818
14933
|
try {
|
|
14819
14934
|
const progress = await buildEmbeddingsIndex(vaultPath2);
|
|
14820
14935
|
embeddingsRefreshed = progress.total - progress.skipped;
|
|
14936
|
+
tracker.end({ refreshed: embeddingsRefreshed });
|
|
14821
14937
|
if (embeddingsRefreshed > 0) {
|
|
14822
14938
|
console.error(`[Flywheel] Embeddings: ${embeddingsRefreshed} notes updated`);
|
|
14823
14939
|
}
|
|
14824
14940
|
} catch (err) {
|
|
14941
|
+
tracker.end({ error: String(err) });
|
|
14825
14942
|
console.error("[Flywheel] Embedding sync failed:", err);
|
|
14826
14943
|
}
|
|
14827
14944
|
}
|
|
14945
|
+
let entityEmbeddingsRefreshed = 0;
|
|
14946
|
+
if (stateDb2 && hasEntityEmbeddingsIndex()) {
|
|
14947
|
+
tracker.start("entity_embeddings", {});
|
|
14948
|
+
try {
|
|
14949
|
+
const entities = getAllEntitiesFromDb3(stateDb2);
|
|
14950
|
+
if (entities.length > 0) {
|
|
14951
|
+
const entityMap = new Map(entities.map((e) => [e.name, {
|
|
14952
|
+
name: e.name,
|
|
14953
|
+
path: e.path,
|
|
14954
|
+
category: e.category,
|
|
14955
|
+
aliases: e.aliases
|
|
14956
|
+
}]));
|
|
14957
|
+
entityEmbeddingsRefreshed = await buildEntityEmbeddingsIndex(vaultPath2, entityMap);
|
|
14958
|
+
loadEntityEmbeddingsToMemory();
|
|
14959
|
+
tracker.end({ refreshed: entityEmbeddingsRefreshed });
|
|
14960
|
+
if (entityEmbeddingsRefreshed > 0) {
|
|
14961
|
+
console.error(`[Flywheel] Entity embeddings: ${entityEmbeddingsRefreshed} updated`);
|
|
14962
|
+
}
|
|
14963
|
+
} else {
|
|
14964
|
+
tracker.end({ refreshed: 0 });
|
|
14965
|
+
}
|
|
14966
|
+
} catch (err) {
|
|
14967
|
+
tracker.end({ error: String(err) });
|
|
14968
|
+
console.error("[Flywheel] Entity embedding sync failed:", err);
|
|
14969
|
+
}
|
|
14970
|
+
}
|
|
14971
|
+
let indexCached = false;
|
|
14972
|
+
if (stateDb2) {
|
|
14973
|
+
tracker.start("index_cache", {});
|
|
14974
|
+
try {
|
|
14975
|
+
saveVaultIndexToCache(stateDb2, newIndex);
|
|
14976
|
+
indexCached = true;
|
|
14977
|
+
tracker.end({ cached: true });
|
|
14978
|
+
} catch (err) {
|
|
14979
|
+
tracker.end({ error: String(err) });
|
|
14980
|
+
console.error("[Flywheel] Index cache save failed:", err);
|
|
14981
|
+
}
|
|
14982
|
+
}
|
|
14828
14983
|
const duration = Date.now() - startTime;
|
|
14829
14984
|
if (stateDb2) {
|
|
14830
14985
|
recordIndexEvent(stateDb2, {
|
|
14831
14986
|
trigger: "manual_refresh",
|
|
14832
14987
|
duration_ms: duration,
|
|
14833
|
-
note_count: newIndex.notes.size
|
|
14988
|
+
note_count: newIndex.notes.size,
|
|
14989
|
+
steps: tracker.steps
|
|
14834
14990
|
});
|
|
14835
14991
|
}
|
|
14836
14992
|
const output = {
|
|
@@ -14839,7 +14995,15 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
14839
14995
|
entities_count: newIndex.entities.size,
|
|
14840
14996
|
fts5_notes: fts5Notes,
|
|
14841
14997
|
edges_recomputed: edgesRecomputed,
|
|
14998
|
+
hub_scores: hubScoresExported || void 0,
|
|
14999
|
+
graph_snapshot: graphSnapshotRecorded || void 0,
|
|
15000
|
+
suppression_list: suppressionUpdated || void 0,
|
|
15001
|
+
task_cache: taskCacheRefreshed || void 0,
|
|
14842
15002
|
embeddings_refreshed: embeddingsRefreshed || void 0,
|
|
15003
|
+
entity_embeddings_refreshed: entityEmbeddingsRefreshed || void 0,
|
|
15004
|
+
recency_rebuilt: recencyRebuilt || void 0,
|
|
15005
|
+
cooccurrence_associations: cooccurrenceAssociations,
|
|
15006
|
+
index_cached: indexCached || void 0,
|
|
14843
15007
|
duration_ms: duration
|
|
14844
15008
|
};
|
|
14845
15009
|
return {
|
|
@@ -22728,7 +22892,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
|
22728
22892
|
// src/tools/read/semantic.ts
|
|
22729
22893
|
init_embeddings();
|
|
22730
22894
|
import { z as z32 } from "zod";
|
|
22731
|
-
import { getAllEntitiesFromDb as
|
|
22895
|
+
import { getAllEntitiesFromDb as getAllEntitiesFromDb4 } from "@velvetmonkey/vault-core";
|
|
22732
22896
|
function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
22733
22897
|
server2.registerTool(
|
|
22734
22898
|
"init_semantic",
|
|
@@ -22792,7 +22956,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
|
22792
22956
|
const embedded = progress.total - progress.skipped;
|
|
22793
22957
|
let entityEmbedded = 0;
|
|
22794
22958
|
try {
|
|
22795
|
-
const allEntities =
|
|
22959
|
+
const allEntities = getAllEntitiesFromDb4(stateDb2);
|
|
22796
22960
|
const entityMap = /* @__PURE__ */ new Map();
|
|
22797
22961
|
for (const e of allEntities) {
|
|
22798
22962
|
entityMap.set(e.name, {
|
|
@@ -22844,7 +23008,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
|
22844
23008
|
// src/tools/read/merges.ts
|
|
22845
23009
|
init_levenshtein();
|
|
22846
23010
|
import { z as z33 } from "zod";
|
|
22847
|
-
import { getAllEntitiesFromDb as
|
|
23011
|
+
import { getAllEntitiesFromDb as getAllEntitiesFromDb5, getDismissedMergePairs, recordMergeDismissal } from "@velvetmonkey/vault-core";
|
|
22848
23012
|
function normalizeName(name) {
|
|
22849
23013
|
return name.toLowerCase().replace(/[.\-_]/g, "").replace(/js$/, "").replace(/ts$/, "");
|
|
22850
23014
|
}
|
|
@@ -22862,7 +23026,7 @@ function registerMergeTools2(server2, getStateDb3) {
|
|
|
22862
23026
|
content: [{ type: "text", text: JSON.stringify({ suggestions: [], error: "StateDb not available" }) }]
|
|
22863
23027
|
};
|
|
22864
23028
|
}
|
|
22865
|
-
const entities =
|
|
23029
|
+
const entities = getAllEntitiesFromDb5(stateDb2);
|
|
22866
23030
|
if (entities.length === 0) {
|
|
22867
23031
|
return {
|
|
22868
23032
|
content: [{ type: "text", text: JSON.stringify({ suggestions: [] }) }]
|
|
@@ -25122,7 +25286,6 @@ async function main() {
|
|
|
25122
25286
|
})();
|
|
25123
25287
|
}
|
|
25124
25288
|
}
|
|
25125
|
-
var DEFAULT_ENTITY_EXCLUDE_FOLDERS = ["node_modules", "templates", "attachments", "tmp"];
|
|
25126
25289
|
async function updateEntitiesInStateDb(vp, sd) {
|
|
25127
25290
|
const db4 = sd ?? stateDb;
|
|
25128
25291
|
const vault = vp ?? vaultPath;
|
|
@@ -25293,7 +25456,7 @@ async function runPostIndexWork(ctx) {
|
|
|
25293
25456
|
}
|
|
25294
25457
|
});
|
|
25295
25458
|
if (sd) {
|
|
25296
|
-
const entities =
|
|
25459
|
+
const entities = getAllEntitiesFromDb6(sd);
|
|
25297
25460
|
if (entities.length > 0) {
|
|
25298
25461
|
const entityMap = new Map(entities.map((e) => [e.name, {
|
|
25299
25462
|
name: e.name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.156",
|
|
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.
|
|
57
|
+
"@velvetmonkey/vault-core": "^2.0.156",
|
|
58
58
|
"better-sqlite3": "^12.0.0",
|
|
59
59
|
"chokidar": "^4.0.0",
|
|
60
60
|
"gray-matter": "^4.0.3",
|