@velvetmonkey/flywheel-memory 2.0.149 → 2.0.150
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 +170 -572
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -642,9 +642,6 @@ function loadNoteEmbeddingsForPaths(paths) {
|
|
|
642
642
|
}
|
|
643
643
|
return result;
|
|
644
644
|
}
|
|
645
|
-
function getEntityEmbedding(entityName) {
|
|
646
|
-
return getEmbMap().get(entityName) ?? null;
|
|
647
|
-
}
|
|
648
645
|
function getEntityEmbeddingsCount() {
|
|
649
646
|
const db4 = getDb();
|
|
650
647
|
if (!db4) return 0;
|
|
@@ -4922,7 +4919,7 @@ function formatContent(content, format) {
|
|
|
4922
4919
|
const lines = trimmed.split("\n");
|
|
4923
4920
|
return lines.map((line, i) => {
|
|
4924
4921
|
if (i === 0) return `- ${line}`;
|
|
4925
|
-
if (line === "") return "";
|
|
4922
|
+
if (line === "") return " ";
|
|
4926
4923
|
return ` ${line}`;
|
|
4927
4924
|
}).join("\n");
|
|
4928
4925
|
}
|
|
@@ -4933,7 +4930,7 @@ function formatContent(content, format) {
|
|
|
4933
4930
|
const lines = trimmed.split("\n");
|
|
4934
4931
|
return lines.map((line, i) => {
|
|
4935
4932
|
if (i === 0) return `- [ ] ${line}`;
|
|
4936
|
-
if (line === "") return "";
|
|
4933
|
+
if (line === "") return " ";
|
|
4937
4934
|
return ` ${line}`;
|
|
4938
4935
|
}).join("\n");
|
|
4939
4936
|
}
|
|
@@ -4944,7 +4941,7 @@ function formatContent(content, format) {
|
|
|
4944
4941
|
const lines = trimmed.split("\n");
|
|
4945
4942
|
return lines.map((line, i) => {
|
|
4946
4943
|
if (i === 0) return `1. ${line}`;
|
|
4947
|
-
if (line === "") return "";
|
|
4944
|
+
if (line === "") return " ";
|
|
4948
4945
|
return ` ${line}`;
|
|
4949
4946
|
}).join("\n");
|
|
4950
4947
|
}
|
|
@@ -4960,7 +4957,7 @@ function formatContent(content, format) {
|
|
|
4960
4957
|
const indent = " ";
|
|
4961
4958
|
return lines.map((line, i) => {
|
|
4962
4959
|
if (i === 0) return `${prefix}${line}`;
|
|
4963
|
-
if (line === "") return
|
|
4960
|
+
if (line === "") return indent;
|
|
4964
4961
|
return `${indent}${line}`;
|
|
4965
4962
|
}).join("\n");
|
|
4966
4963
|
}
|
|
@@ -5037,7 +5034,7 @@ function insertInSection(content, section, newContent, position, options) {
|
|
|
5037
5034
|
if (indent) {
|
|
5038
5035
|
const contentLines = formattedContent.split("\n");
|
|
5039
5036
|
const indentedContent = contentLines.map((line) => {
|
|
5040
|
-
if (line === "") return line;
|
|
5037
|
+
if (line === "") return indent || line;
|
|
5041
5038
|
return indent + line;
|
|
5042
5039
|
}).join("\n");
|
|
5043
5040
|
lines.splice(section.contentStartLine, 0, indentedContent);
|
|
@@ -5060,7 +5057,7 @@ function insertInSection(content, section, newContent, position, options) {
|
|
|
5060
5057
|
const indent = detectSectionBaseIndentation(lines, section.contentStartLine, section.endLine);
|
|
5061
5058
|
const contentLines = formattedContent.split("\n");
|
|
5062
5059
|
const indentedContent = contentLines.map((line) => {
|
|
5063
|
-
if (line === "") return line;
|
|
5060
|
+
if (line === "") return indent || line;
|
|
5064
5061
|
return indent + line;
|
|
5065
5062
|
}).join("\n");
|
|
5066
5063
|
lines[lastContentLineIdx] = indentedContent;
|
|
@@ -5083,7 +5080,7 @@ function insertInSection(content, section, newContent, position, options) {
|
|
|
5083
5080
|
const indent = detectSectionBaseIndentation(lines, section.contentStartLine, section.endLine);
|
|
5084
5081
|
const contentLines = formattedContent.split("\n");
|
|
5085
5082
|
const indentedContent = contentLines.map((line) => {
|
|
5086
|
-
if (line === "") return line;
|
|
5083
|
+
if (line === "") return indent || line;
|
|
5087
5084
|
return indent + line;
|
|
5088
5085
|
}).join("\n");
|
|
5089
5086
|
lines.splice(insertLine, 0, indentedContent);
|
|
@@ -10719,9 +10716,8 @@ var ALL_CATEGORIES = [
|
|
|
10719
10716
|
];
|
|
10720
10717
|
var PRESETS = {
|
|
10721
10718
|
// Presets
|
|
10722
|
-
default: ["search", "read", "write", "tasks"],
|
|
10723
|
-
|
|
10724
|
-
full: ALL_CATEGORIES.filter((c) => c !== "memory"),
|
|
10719
|
+
default: ["search", "read", "write", "tasks", "memory"],
|
|
10720
|
+
full: [...ALL_CATEGORIES],
|
|
10725
10721
|
// Composable bundles (one per category)
|
|
10726
10722
|
graph: ["graph"],
|
|
10727
10723
|
schema: ["schema"],
|
|
@@ -10735,6 +10731,8 @@ var PRESETS = {
|
|
|
10735
10731
|
};
|
|
10736
10732
|
var DEFAULT_PRESET = "default";
|
|
10737
10733
|
var DEPRECATED_ALIASES = {
|
|
10734
|
+
agent: "default",
|
|
10735
|
+
// agent merged into default — memory now included
|
|
10738
10736
|
minimal: "default",
|
|
10739
10737
|
writer: "default",
|
|
10740
10738
|
// writer was default+tasks, now default includes tasks
|
|
@@ -10849,9 +10847,8 @@ var TOOL_CATEGORY = {
|
|
|
10849
10847
|
tasks: "tasks",
|
|
10850
10848
|
vault_toggle_task: "tasks",
|
|
10851
10849
|
vault_add_task: "tasks",
|
|
10852
|
-
// memory (
|
|
10850
|
+
// memory (2 tools) -- session memory
|
|
10853
10851
|
memory: "memory",
|
|
10854
|
-
recall: "memory",
|
|
10855
10852
|
brief: "memory",
|
|
10856
10853
|
// note-ops (4 tools) -- file management
|
|
10857
10854
|
vault_delete_note: "note-ops",
|
|
@@ -10890,10 +10887,12 @@ function generateInstructions(categories, registry) {
|
|
|
10890
10887
|
parts.push(`Flywheel provides tools to search, read, and write an Obsidian vault's knowledge graph.
|
|
10891
10888
|
|
|
10892
10889
|
Tool selection:
|
|
10893
|
-
1. "search" is the primary tool.
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10890
|
+
1. "search" is the primary tool. One call searches notes, entities, and memories.
|
|
10891
|
+
Each result carries: type (note/entity/memory), frontmatter, tags, aliases,
|
|
10892
|
+
backlinks (ranked by edge weight \xD7 recency), outlinks (existence-checked),
|
|
10893
|
+
section provenance, extracted dates, entity bridges, confidence scores,
|
|
10894
|
+
content snippet or preview, entity category, hub score, and timestamps.
|
|
10895
|
+
This is a decision surface \u2014 usually enough to answer without reading any files.
|
|
10897
10896
|
2. Escalate to "get_note_structure" only when you need the full markdown content
|
|
10898
10897
|
or word count. Use "get_section_content" to read one section by heading name.
|
|
10899
10898
|
3. Start with a broad search: just query text, no filters. Only add folder, tag,
|
|
@@ -10901,7 +10900,7 @@ Tool selection:
|
|
|
10901
10900
|
if (!hasEmbeddingsIndex()) {
|
|
10902
10901
|
parts.push(`
|
|
10903
10902
|
**Setup:** Run \`init_semantic\` once to build embeddings. This unlocks hybrid search (BM25 + semantic),
|
|
10904
|
-
improves
|
|
10903
|
+
improves search results, and enables similarity-based tools. Without it, search is keyword-only.`);
|
|
10905
10904
|
}
|
|
10906
10905
|
if (registry?.isMultiVault) {
|
|
10907
10906
|
parts.push(`
|
|
@@ -10916,9 +10915,9 @@ This server manages multiple vaults. Every tool has an optional "vault" paramete
|
|
|
10916
10915
|
**Frontmatter matters more than content** for Flywheel's intelligence. When creating or updating notes, always set:
|
|
10917
10916
|
- \`type:\` \u2014 drives entity categorization (person, project, technology). Without it, the category is guessed from the name alone and is often wrong.
|
|
10918
10917
|
- \`aliases:\` \u2014 alternative names so the entity is found when referred to differently. Without it, the entity is invisible to searches using alternate names.
|
|
10919
|
-
- \`description:\` \u2014 one-line summary shown in search results and used
|
|
10918
|
+
- \`description:\` \u2014 one-line summary shown in search results and used for entity ranking. Without it, search quality is degraded.
|
|
10920
10919
|
- Tags \u2014 used for filtering, suggestion scoring, and schema analysis.
|
|
10921
|
-
Good frontmatter is the highest-leverage action for improving suggestions,
|
|
10920
|
+
Good frontmatter is the highest-leverage action for improving suggestions, search, and link quality.`);
|
|
10922
10921
|
if (categories.has("read")) {
|
|
10923
10922
|
parts.push(`
|
|
10924
10923
|
## Read
|
|
@@ -10969,7 +10968,10 @@ you say "run the weekly review for this week".`);
|
|
|
10969
10968
|
parts.push(`
|
|
10970
10969
|
## Memory
|
|
10971
10970
|
|
|
10972
|
-
|
|
10971
|
+
"brief" delivers startup context (recent sessions, active entities, stored memories) \u2014 call it at
|
|
10972
|
+
conversation start. "search" finds everything \u2014 notes, entities, and memories in one call. "memory"
|
|
10973
|
+
with action "store" persists observations, facts, or preferences across sessions (e.g. key decisions,
|
|
10974
|
+
user preferences, project status).`);
|
|
10973
10975
|
}
|
|
10974
10976
|
if (categories.has("graph")) {
|
|
10975
10977
|
parts.push(`
|
|
@@ -11014,7 +11016,7 @@ import * as path37 from "path";
|
|
|
11014
11016
|
import { dirname as dirname5, join as join19 } from "path";
|
|
11015
11017
|
import { statSync as statSync6, readFileSync as readFileSync5 } from "fs";
|
|
11016
11018
|
import { fileURLToPath } from "url";
|
|
11017
|
-
import { z as
|
|
11019
|
+
import { z as z39 } from "zod";
|
|
11018
11020
|
import { getSessionId } from "@velvetmonkey/vault-core";
|
|
11019
11021
|
init_vault_scope();
|
|
11020
11022
|
|
|
@@ -13852,64 +13854,6 @@ function enrichResultLight(result, index, stateDb2) {
|
|
|
13852
13854
|
}
|
|
13853
13855
|
return enriched;
|
|
13854
13856
|
}
|
|
13855
|
-
function enrichEntityCompact(entityName, stateDb2, index) {
|
|
13856
|
-
const enriched = {};
|
|
13857
|
-
if (stateDb2) {
|
|
13858
|
-
try {
|
|
13859
|
-
const entity = getEntityByName2(stateDb2, entityName);
|
|
13860
|
-
if (entity) {
|
|
13861
|
-
enriched.category = entity.category;
|
|
13862
|
-
enriched.hub_score = entity.hubScore;
|
|
13863
|
-
if (entity.aliases.length > 0) enriched.aliases = entity.aliases;
|
|
13864
|
-
enriched.path = entity.path;
|
|
13865
|
-
}
|
|
13866
|
-
} catch {
|
|
13867
|
-
}
|
|
13868
|
-
}
|
|
13869
|
-
if (index) {
|
|
13870
|
-
const entityPath = enriched.path ?? index.entities.get(entityName.toLowerCase());
|
|
13871
|
-
if (entityPath) {
|
|
13872
|
-
const note = index.notes.get(entityPath);
|
|
13873
|
-
const normalizedPath = entityPath.toLowerCase().replace(/\.md$/, "");
|
|
13874
|
-
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
13875
|
-
enriched.backlink_count = backlinks.length;
|
|
13876
|
-
if (note) {
|
|
13877
|
-
if (Object.keys(note.frontmatter).length > 0) enriched.frontmatter = note.frontmatter;
|
|
13878
|
-
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
13879
|
-
if (note.outlinks.length > 0) {
|
|
13880
|
-
enriched.outlink_names = getOutlinkNames(note.outlinks, entityPath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
13881
|
-
}
|
|
13882
|
-
}
|
|
13883
|
-
}
|
|
13884
|
-
}
|
|
13885
|
-
return enriched;
|
|
13886
|
-
}
|
|
13887
|
-
function enrichNoteCompact(notePath, stateDb2, index) {
|
|
13888
|
-
const enriched = {};
|
|
13889
|
-
if (!index) return enriched;
|
|
13890
|
-
const note = index.notes.get(notePath);
|
|
13891
|
-
if (!note) return enriched;
|
|
13892
|
-
const normalizedPath = notePath.toLowerCase().replace(/\.md$/, "");
|
|
13893
|
-
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
13894
|
-
if (Object.keys(note.frontmatter).length > 0) enriched.frontmatter = note.frontmatter;
|
|
13895
|
-
if (note.tags.length > 0) enriched.tags = note.tags;
|
|
13896
|
-
enriched.backlink_count = backlinks.length;
|
|
13897
|
-
enriched.modified = note.modified.toISOString();
|
|
13898
|
-
if (note.outlinks.length > 0) {
|
|
13899
|
-
enriched.outlink_names = getOutlinkNames(note.outlinks, notePath, index, stateDb2, COMPACT_OUTLINK_NAMES);
|
|
13900
|
-
}
|
|
13901
|
-
if (stateDb2) {
|
|
13902
|
-
try {
|
|
13903
|
-
const entity = getEntityByName2(stateDb2, note.title);
|
|
13904
|
-
if (entity) {
|
|
13905
|
-
enriched.category = entity.category;
|
|
13906
|
-
enriched.hub_score = entity.hubScore;
|
|
13907
|
-
}
|
|
13908
|
-
} catch {
|
|
13909
|
-
}
|
|
13910
|
-
}
|
|
13911
|
-
return enriched;
|
|
13912
|
-
}
|
|
13913
13857
|
|
|
13914
13858
|
// src/core/read/multihop.ts
|
|
13915
13859
|
import { getEntityByName as getEntityByName3, searchEntities } from "@velvetmonkey/vault-core";
|
|
@@ -14382,7 +14326,7 @@ function sortNotes(notes, sortBy, order) {
|
|
|
14382
14326
|
function registerQueryTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
14383
14327
|
server2.tool(
|
|
14384
14328
|
"search",
|
|
14385
|
-
'Search
|
|
14329
|
+
'Search everything \u2014 notes, entities, and memories \u2014 in one call. Returns a decision surface with three sections: note results (with section provenance, dates, bridges, confidence), matching entity profiles, and relevant memories.\n\nNote results carry full metadata (frontmatter, scored backlinks/outlinks, snippets). Start with just a query, no filters. Narrow with filters only if needed. Use get_note_structure for full content, get_section_content to read one section.\n\nSearches note content (FTS5 + hybrid semantic), entity profiles (people, projects, technologies), and stored memories. Hybrid results included automatically when embeddings are built (via init_semantic).\n\nExample: search({ query: "quarterly review", limit: 5 })\nExample: search({ where: { type: "project", status: "active" } })\n\nMulti-vault: omitting `vault` searches all vaults and merges results. Pass `vault` to search a specific vault.',
|
|
14386
14330
|
{
|
|
14387
14331
|
query: z5.string().optional().describe("Search query text. Required unless using metadata filters (where, has_tag, folder, etc.)"),
|
|
14388
14332
|
// Metadata filters
|
|
@@ -21771,410 +21715,8 @@ function registerMemoryTools(server2, getStateDb3) {
|
|
|
21771
21715
|
);
|
|
21772
21716
|
}
|
|
21773
21717
|
|
|
21774
|
-
// src/tools/read/recall.ts
|
|
21775
|
-
import { z as z26 } from "zod";
|
|
21776
|
-
import { searchEntities as searchEntitiesDb2 } from "@velvetmonkey/vault-core";
|
|
21777
|
-
init_recency();
|
|
21778
|
-
init_cooccurrence();
|
|
21779
|
-
init_wikilinks();
|
|
21780
|
-
init_edgeWeights();
|
|
21781
|
-
init_wikilinkFeedback();
|
|
21782
|
-
init_embeddings();
|
|
21783
|
-
init_stemmer();
|
|
21784
|
-
|
|
21785
|
-
// src/core/read/mmr.ts
|
|
21786
|
-
init_embeddings();
|
|
21787
|
-
function selectByMmr(candidates, limit, lambda = 0.7) {
|
|
21788
|
-
if (candidates.length <= limit) return candidates;
|
|
21789
|
-
if (candidates.length === 0) return [];
|
|
21790
|
-
const maxScore = Math.max(...candidates.map((c) => c.score));
|
|
21791
|
-
if (maxScore === 0) return candidates.slice(0, limit);
|
|
21792
|
-
const normScores = /* @__PURE__ */ new Map();
|
|
21793
|
-
for (const c of candidates) {
|
|
21794
|
-
normScores.set(c.id, c.score / maxScore);
|
|
21795
|
-
}
|
|
21796
|
-
const selected = [];
|
|
21797
|
-
const remaining = new Set(candidates.map((_, i) => i));
|
|
21798
|
-
let bestIdx = 0;
|
|
21799
|
-
let bestScore = -Infinity;
|
|
21800
|
-
for (const idx of remaining) {
|
|
21801
|
-
if (candidates[idx].score > bestScore) {
|
|
21802
|
-
bestScore = candidates[idx].score;
|
|
21803
|
-
bestIdx = idx;
|
|
21804
|
-
}
|
|
21805
|
-
}
|
|
21806
|
-
selected.push(candidates[bestIdx]);
|
|
21807
|
-
remaining.delete(bestIdx);
|
|
21808
|
-
while (selected.length < limit && remaining.size > 0) {
|
|
21809
|
-
let bestMmr = -Infinity;
|
|
21810
|
-
let bestCandidate = -1;
|
|
21811
|
-
for (const idx of remaining) {
|
|
21812
|
-
const candidate = candidates[idx];
|
|
21813
|
-
const relevance = normScores.get(candidate.id) || 0;
|
|
21814
|
-
let maxSim = 0;
|
|
21815
|
-
if (candidate.embedding !== null) {
|
|
21816
|
-
for (const sel of selected) {
|
|
21817
|
-
if (sel.embedding !== null) {
|
|
21818
|
-
const sim = cosineSimilarity(candidate.embedding, sel.embedding);
|
|
21819
|
-
if (sim > maxSim) maxSim = sim;
|
|
21820
|
-
}
|
|
21821
|
-
}
|
|
21822
|
-
}
|
|
21823
|
-
const mmr = lambda * relevance - (1 - lambda) * maxSim;
|
|
21824
|
-
if (mmr > bestMmr) {
|
|
21825
|
-
bestMmr = mmr;
|
|
21826
|
-
bestCandidate = idx;
|
|
21827
|
-
}
|
|
21828
|
-
}
|
|
21829
|
-
if (bestCandidate === -1) break;
|
|
21830
|
-
selected.push(candidates[bestCandidate]);
|
|
21831
|
-
remaining.delete(bestCandidate);
|
|
21832
|
-
}
|
|
21833
|
-
return selected;
|
|
21834
|
-
}
|
|
21835
|
-
|
|
21836
|
-
// src/tools/read/recall.ts
|
|
21837
|
-
function scoreTextRelevance(query, content) {
|
|
21838
|
-
const queryTokens = tokenize(query).map((t) => t.toLowerCase());
|
|
21839
|
-
const queryStems = queryTokens.map((t) => stem(t));
|
|
21840
|
-
const contentLower = content.toLowerCase();
|
|
21841
|
-
const contentTokens = new Set(tokenize(contentLower));
|
|
21842
|
-
const contentStems = new Set([...contentTokens].map((t) => stem(t)));
|
|
21843
|
-
let score = 0;
|
|
21844
|
-
for (let i = 0; i < queryTokens.length; i++) {
|
|
21845
|
-
const token = queryTokens[i];
|
|
21846
|
-
const stemmed = queryStems[i];
|
|
21847
|
-
if (contentTokens.has(token)) {
|
|
21848
|
-
score += 10;
|
|
21849
|
-
} else if (contentStems.has(stemmed)) {
|
|
21850
|
-
score += 5;
|
|
21851
|
-
}
|
|
21852
|
-
}
|
|
21853
|
-
if (contentLower.includes(query.toLowerCase())) {
|
|
21854
|
-
score += 15;
|
|
21855
|
-
}
|
|
21856
|
-
return score;
|
|
21857
|
-
}
|
|
21858
|
-
function getEdgeWeightBoost(entityName, edgeWeightMap) {
|
|
21859
|
-
const avgWeight = edgeWeightMap.get(entityName.toLowerCase());
|
|
21860
|
-
if (!avgWeight || avgWeight <= 1) return 0;
|
|
21861
|
-
return Math.min((avgWeight - 1) * 3, 6);
|
|
21862
|
-
}
|
|
21863
|
-
async function performRecall(stateDb2, query, options = {}) {
|
|
21864
|
-
const {
|
|
21865
|
-
max_results = 20,
|
|
21866
|
-
focus,
|
|
21867
|
-
entity,
|
|
21868
|
-
max_tokens,
|
|
21869
|
-
diversity = 1,
|
|
21870
|
-
vaultPath: vaultPath2
|
|
21871
|
-
} = options;
|
|
21872
|
-
const results = [];
|
|
21873
|
-
const recencyIndex2 = loadRecencyFromStateDb();
|
|
21874
|
-
const edgeWeightMap = getEntityEdgeWeightMap(stateDb2);
|
|
21875
|
-
const feedbackBoosts = getAllFeedbackBoosts(stateDb2);
|
|
21876
|
-
const cooccurrenceIndex2 = getCooccurrenceIndex();
|
|
21877
|
-
if (!focus || focus === "entities") {
|
|
21878
|
-
try {
|
|
21879
|
-
const entityResults = searchEntitiesDb2(stateDb2, query, max_results);
|
|
21880
|
-
for (const e of entityResults) {
|
|
21881
|
-
const textScore = scoreTextRelevance(query, `${e.name} ${e.description || ""}`);
|
|
21882
|
-
const recency = recencyIndex2 ? getRecencyBoost(e.name, recencyIndex2) : 0;
|
|
21883
|
-
const feedback = feedbackBoosts.get(e.name) ?? 0;
|
|
21884
|
-
const edgeWeight = getEdgeWeightBoost(e.name, edgeWeightMap);
|
|
21885
|
-
const total = textScore + recency + feedback + edgeWeight;
|
|
21886
|
-
if (total > 0) {
|
|
21887
|
-
results.push({
|
|
21888
|
-
type: "entity",
|
|
21889
|
-
id: e.name,
|
|
21890
|
-
content: e.description || `Entity: ${e.name} (${e.category})`,
|
|
21891
|
-
score: total,
|
|
21892
|
-
breakdown: {
|
|
21893
|
-
textRelevance: textScore,
|
|
21894
|
-
recencyBoost: recency,
|
|
21895
|
-
cooccurrenceBoost: 0,
|
|
21896
|
-
feedbackBoost: feedback,
|
|
21897
|
-
edgeWeightBoost: edgeWeight,
|
|
21898
|
-
semanticBoost: 0
|
|
21899
|
-
}
|
|
21900
|
-
});
|
|
21901
|
-
}
|
|
21902
|
-
}
|
|
21903
|
-
} catch {
|
|
21904
|
-
}
|
|
21905
|
-
}
|
|
21906
|
-
if (!focus || focus === "notes") {
|
|
21907
|
-
try {
|
|
21908
|
-
const noteResults = searchFTS5("", query, max_results);
|
|
21909
|
-
for (const n of noteResults) {
|
|
21910
|
-
const textScore = Math.max(10, scoreTextRelevance(query, `${n.title || ""} ${n.snippet || ""}`));
|
|
21911
|
-
const entityName = n.title || n.path.replace(/\.md$/, "").split("/").pop() || "";
|
|
21912
|
-
const recency = recencyIndex2 ? getRecencyBoost(entityName, recencyIndex2) : 0;
|
|
21913
|
-
const feedback = feedbackBoosts.get(entityName) ?? 0;
|
|
21914
|
-
const edgeWeight = getEdgeWeightBoost(entityName, edgeWeightMap);
|
|
21915
|
-
const total = textScore + recency + feedback + edgeWeight;
|
|
21916
|
-
results.push({
|
|
21917
|
-
type: "note",
|
|
21918
|
-
id: n.path,
|
|
21919
|
-
content: n.snippet || n.title || n.path,
|
|
21920
|
-
score: total,
|
|
21921
|
-
breakdown: {
|
|
21922
|
-
textRelevance: textScore,
|
|
21923
|
-
recencyBoost: recency,
|
|
21924
|
-
cooccurrenceBoost: 0,
|
|
21925
|
-
// applied in post-pass below
|
|
21926
|
-
feedbackBoost: feedback,
|
|
21927
|
-
edgeWeightBoost: edgeWeight,
|
|
21928
|
-
semanticBoost: 0
|
|
21929
|
-
}
|
|
21930
|
-
});
|
|
21931
|
-
}
|
|
21932
|
-
} catch {
|
|
21933
|
-
}
|
|
21934
|
-
}
|
|
21935
|
-
if (!focus || focus === "memories") {
|
|
21936
|
-
try {
|
|
21937
|
-
const memResults = searchMemories(stateDb2, {
|
|
21938
|
-
query,
|
|
21939
|
-
entity,
|
|
21940
|
-
limit: max_results
|
|
21941
|
-
});
|
|
21942
|
-
const now = Date.now();
|
|
21943
|
-
for (const m of memResults) {
|
|
21944
|
-
const textScore = scoreTextRelevance(query, `${m.key} ${m.value}`);
|
|
21945
|
-
const confidenceBoost = m.confidence * 5;
|
|
21946
|
-
let typeBoost = 0;
|
|
21947
|
-
switch (m.memory_type) {
|
|
21948
|
-
case "fact":
|
|
21949
|
-
typeBoost = 3;
|
|
21950
|
-
break;
|
|
21951
|
-
case "preference":
|
|
21952
|
-
typeBoost = 2;
|
|
21953
|
-
break;
|
|
21954
|
-
case "observation": {
|
|
21955
|
-
const ageDays = (now - m.updated_at) / 864e5;
|
|
21956
|
-
const recencyFactor = Math.max(0.2, 1 - ageDays / 7);
|
|
21957
|
-
typeBoost = 1 + 4 * recencyFactor;
|
|
21958
|
-
break;
|
|
21959
|
-
}
|
|
21960
|
-
case "summary":
|
|
21961
|
-
typeBoost = 1;
|
|
21962
|
-
break;
|
|
21963
|
-
}
|
|
21964
|
-
const memScore = textScore + confidenceBoost + typeBoost;
|
|
21965
|
-
results.push({
|
|
21966
|
-
type: "memory",
|
|
21967
|
-
id: m.key,
|
|
21968
|
-
content: m.value,
|
|
21969
|
-
score: memScore,
|
|
21970
|
-
breakdown: {
|
|
21971
|
-
textRelevance: textScore,
|
|
21972
|
-
recencyBoost: typeBoost,
|
|
21973
|
-
// type-aware boost reported as recency
|
|
21974
|
-
cooccurrenceBoost: 0,
|
|
21975
|
-
feedbackBoost: confidenceBoost,
|
|
21976
|
-
edgeWeightBoost: 0,
|
|
21977
|
-
semanticBoost: 0
|
|
21978
|
-
}
|
|
21979
|
-
});
|
|
21980
|
-
}
|
|
21981
|
-
} catch {
|
|
21982
|
-
}
|
|
21983
|
-
}
|
|
21984
|
-
if (cooccurrenceIndex2) {
|
|
21985
|
-
const seedEntities = /* @__PURE__ */ new Set();
|
|
21986
|
-
for (const r of results) {
|
|
21987
|
-
if (r.type === "entity") {
|
|
21988
|
-
seedEntities.add(r.id);
|
|
21989
|
-
} else if (r.type === "note") {
|
|
21990
|
-
const name = r.id.replace(/\.md$/, "").split("/").pop() || "";
|
|
21991
|
-
if (name) seedEntities.add(name);
|
|
21992
|
-
}
|
|
21993
|
-
}
|
|
21994
|
-
for (const r of results) {
|
|
21995
|
-
if (r.type === "entity" || r.type === "note") {
|
|
21996
|
-
const name = r.type === "entity" ? r.id : r.id.replace(/\.md$/, "").split("/").pop() || "";
|
|
21997
|
-
const boost = getCooccurrenceBoost(name, seedEntities, cooccurrenceIndex2, recencyIndex2);
|
|
21998
|
-
if (boost > 0) {
|
|
21999
|
-
r.breakdown.cooccurrenceBoost = boost;
|
|
22000
|
-
r.score += boost;
|
|
22001
|
-
}
|
|
22002
|
-
}
|
|
22003
|
-
}
|
|
22004
|
-
}
|
|
22005
|
-
if ((!focus || focus === "entities") && query.length >= 20 && hasEntityEmbeddingsIndex()) {
|
|
22006
|
-
try {
|
|
22007
|
-
const embedding = await embedTextCached(query);
|
|
22008
|
-
const semanticMatches = findSemanticallySimilarEntities(embedding, max_results);
|
|
22009
|
-
for (const match of semanticMatches) {
|
|
22010
|
-
if (match.similarity < 0.3) continue;
|
|
22011
|
-
const boost = match.similarity * 15;
|
|
22012
|
-
const existing = results.find((r) => r.type === "entity" && r.id === match.entityName);
|
|
22013
|
-
if (existing) {
|
|
22014
|
-
existing.score += boost;
|
|
22015
|
-
existing.breakdown.semanticBoost = boost;
|
|
22016
|
-
} else {
|
|
22017
|
-
results.push({
|
|
22018
|
-
type: "entity",
|
|
22019
|
-
id: match.entityName,
|
|
22020
|
-
content: `Semantically similar to: "${query}"`,
|
|
22021
|
-
score: boost,
|
|
22022
|
-
breakdown: {
|
|
22023
|
-
textRelevance: 0,
|
|
22024
|
-
recencyBoost: 0,
|
|
22025
|
-
cooccurrenceBoost: 0,
|
|
22026
|
-
feedbackBoost: 0,
|
|
22027
|
-
edgeWeightBoost: 0,
|
|
22028
|
-
semanticBoost: boost
|
|
22029
|
-
}
|
|
22030
|
-
});
|
|
22031
|
-
}
|
|
22032
|
-
}
|
|
22033
|
-
} catch {
|
|
22034
|
-
}
|
|
22035
|
-
}
|
|
22036
|
-
results.sort((a, b) => b.score - a.score);
|
|
22037
|
-
const seen = /* @__PURE__ */ new Set();
|
|
22038
|
-
const deduped = results.filter((r) => {
|
|
22039
|
-
const key = `${r.type}:${r.id}`;
|
|
22040
|
-
if (seen.has(key)) return false;
|
|
22041
|
-
seen.add(key);
|
|
22042
|
-
return true;
|
|
22043
|
-
});
|
|
22044
|
-
let selected;
|
|
22045
|
-
if (hasEmbeddingsIndex() && deduped.length > max_results) {
|
|
22046
|
-
const notePaths = deduped.filter((r) => r.type === "note").map((r) => r.id);
|
|
22047
|
-
const noteEmbeddings = loadNoteEmbeddingsForPaths(notePaths);
|
|
22048
|
-
let queryEmbedding = null;
|
|
22049
|
-
const mmrCandidates = [];
|
|
22050
|
-
for (const r of deduped) {
|
|
22051
|
-
let embedding = null;
|
|
22052
|
-
if (r.type === "entity") {
|
|
22053
|
-
embedding = getEntityEmbedding(r.id);
|
|
22054
|
-
} else if (r.type === "note") {
|
|
22055
|
-
embedding = noteEmbeddings.get(r.id) ?? null;
|
|
22056
|
-
} else if (r.type === "memory") {
|
|
22057
|
-
try {
|
|
22058
|
-
if (!queryEmbedding) queryEmbedding = await embedTextCached(query);
|
|
22059
|
-
embedding = await embedTextCached(r.content);
|
|
22060
|
-
} catch {
|
|
22061
|
-
}
|
|
22062
|
-
}
|
|
22063
|
-
mmrCandidates.push({ id: `${r.type}:${r.id}`, score: r.score, embedding });
|
|
22064
|
-
}
|
|
22065
|
-
const mmrSelected = selectByMmr(mmrCandidates, max_results, diversity);
|
|
22066
|
-
const selectedIds = new Set(mmrSelected.map((m) => m.id));
|
|
22067
|
-
selected = deduped.filter((r) => selectedIds.has(`${r.type}:${r.id}`));
|
|
22068
|
-
const orderMap = new Map(mmrSelected.map((m, i) => [m.id, i]));
|
|
22069
|
-
selected.sort((a, b) => (orderMap.get(`${a.type}:${a.id}`) ?? 0) - (orderMap.get(`${b.type}:${b.id}`) ?? 0));
|
|
22070
|
-
} else {
|
|
22071
|
-
selected = deduped.slice(0, max_results);
|
|
22072
|
-
}
|
|
22073
|
-
if (vaultPath2) {
|
|
22074
|
-
const queryTokens = tokenize(query).map((t) => t.toLowerCase());
|
|
22075
|
-
let queryEmb = null;
|
|
22076
|
-
if (hasEmbeddingsIndex()) {
|
|
22077
|
-
try {
|
|
22078
|
-
queryEmb = await embedTextCached(query);
|
|
22079
|
-
} catch {
|
|
22080
|
-
}
|
|
22081
|
-
}
|
|
22082
|
-
for (const r of selected) {
|
|
22083
|
-
if (r.type !== "note") continue;
|
|
22084
|
-
try {
|
|
22085
|
-
const absPath = vaultPath2 + "/" + r.id;
|
|
22086
|
-
const snippets = await extractBestSnippets(absPath, queryEmb, queryTokens);
|
|
22087
|
-
if (snippets.length > 0 && snippets[0].text.length > 0) {
|
|
22088
|
-
r.content = snippets[0].text;
|
|
22089
|
-
}
|
|
22090
|
-
} catch {
|
|
22091
|
-
}
|
|
22092
|
-
}
|
|
22093
|
-
}
|
|
22094
|
-
if (max_tokens) {
|
|
22095
|
-
let tokenBudget = max_tokens;
|
|
22096
|
-
const budgeted = [];
|
|
22097
|
-
for (const r of selected) {
|
|
22098
|
-
const estimatedTokens = Math.ceil(r.content.length / 4);
|
|
22099
|
-
if (tokenBudget - estimatedTokens < 0 && budgeted.length > 0) break;
|
|
22100
|
-
tokenBudget -= estimatedTokens;
|
|
22101
|
-
budgeted.push(r);
|
|
22102
|
-
}
|
|
22103
|
-
return budgeted;
|
|
22104
|
-
}
|
|
22105
|
-
return selected;
|
|
22106
|
-
}
|
|
22107
|
-
function registerRecallTools(server2, getStateDb3, getVaultPath, getIndex) {
|
|
22108
|
-
server2.tool(
|
|
22109
|
-
"recall",
|
|
22110
|
-
"Query everything the system knows about a topic. Searches across entities, notes, and memories with graph-boosted ranking.",
|
|
22111
|
-
{
|
|
22112
|
-
query: z26.string().describe('What to recall (e.g., "Project X", "meetings about auth")'),
|
|
22113
|
-
max_results: z26.number().min(1).max(100).optional().describe("Max results (default: 20)"),
|
|
22114
|
-
focus: z26.enum(["entities", "notes", "memories"]).optional().describe("Limit to a specific result type. Omit for best results (searches everything)."),
|
|
22115
|
-
entity: z26.string().optional().describe("Filter memories by entity association"),
|
|
22116
|
-
max_tokens: z26.number().optional().describe("Token budget for response (truncates lower-ranked results)"),
|
|
22117
|
-
diversity: z26.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
|
|
22118
|
-
},
|
|
22119
|
-
async (args) => {
|
|
22120
|
-
const stateDb2 = getStateDb3();
|
|
22121
|
-
if (!stateDb2) {
|
|
22122
|
-
return {
|
|
22123
|
-
content: [{ type: "text", text: JSON.stringify({ error: "StateDb not available" }) }],
|
|
22124
|
-
isError: true
|
|
22125
|
-
};
|
|
22126
|
-
}
|
|
22127
|
-
const results = await performRecall(stateDb2, args.query, {
|
|
22128
|
-
max_results: args.max_results,
|
|
22129
|
-
focus: args.focus,
|
|
22130
|
-
entity: args.entity,
|
|
22131
|
-
max_tokens: args.max_tokens,
|
|
22132
|
-
diversity: args.diversity,
|
|
22133
|
-
vaultPath: getVaultPath?.()
|
|
22134
|
-
});
|
|
22135
|
-
const entities = results.filter((r) => r.type === "entity");
|
|
22136
|
-
const notes = results.filter((r) => r.type === "note");
|
|
22137
|
-
const memories = results.filter((r) => r.type === "memory");
|
|
22138
|
-
const index = getIndex?.() ?? null;
|
|
22139
|
-
const enrichedNotes = notes.map((n) => ({
|
|
22140
|
-
path: n.id,
|
|
22141
|
-
snippet: n.content,
|
|
22142
|
-
...enrichNoteCompact(n.id, stateDb2, index)
|
|
22143
|
-
}));
|
|
22144
|
-
const hopResults = index ? multiHopBackfill(enrichedNotes, index, stateDb2, {
|
|
22145
|
-
maxBackfill: Math.max(5, (args.max_results ?? 10) - notes.length)
|
|
22146
|
-
}) : [];
|
|
22147
|
-
if (index) {
|
|
22148
|
-
const allNoteResults = [...enrichedNotes, ...hopResults];
|
|
22149
|
-
const expansionTerms = extractExpansionTerms(allNoteResults, args.query, index);
|
|
22150
|
-
const expansionResults = expandQuery(expansionTerms, allNoteResults, index, stateDb2);
|
|
22151
|
-
hopResults.push(...expansionResults);
|
|
22152
|
-
}
|
|
22153
|
-
return {
|
|
22154
|
-
content: [{
|
|
22155
|
-
type: "text",
|
|
22156
|
-
text: JSON.stringify({
|
|
22157
|
-
query: args.query,
|
|
22158
|
-
total: results.length,
|
|
22159
|
-
entities: entities.map((e) => ({
|
|
22160
|
-
name: e.id,
|
|
22161
|
-
description: e.content,
|
|
22162
|
-
...enrichEntityCompact(e.id, stateDb2, index)
|
|
22163
|
-
})),
|
|
22164
|
-
notes: [...enrichedNotes, ...hopResults],
|
|
22165
|
-
memories: memories.map((m) => ({
|
|
22166
|
-
key: m.id,
|
|
22167
|
-
value: m.content
|
|
22168
|
-
}))
|
|
22169
|
-
}, null, 2)
|
|
22170
|
-
}]
|
|
22171
|
-
};
|
|
22172
|
-
}
|
|
22173
|
-
);
|
|
22174
|
-
}
|
|
22175
|
-
|
|
22176
21718
|
// src/tools/read/brief.ts
|
|
22177
|
-
import { z as
|
|
21719
|
+
import { z as z26 } from "zod";
|
|
22178
21720
|
init_corrections();
|
|
22179
21721
|
function estimateTokens2(value) {
|
|
22180
21722
|
const str = JSON.stringify(value);
|
|
@@ -22316,8 +21858,8 @@ function registerBriefTools(server2, getStateDb3) {
|
|
|
22316
21858
|
"brief",
|
|
22317
21859
|
"Get a startup context briefing: recent sessions, active entities, memories, pending corrections, and vault stats. Call at conversation start.",
|
|
22318
21860
|
{
|
|
22319
|
-
max_tokens:
|
|
22320
|
-
focus:
|
|
21861
|
+
max_tokens: z26.number().optional().describe("Token budget (lower-priority sections truncated first)"),
|
|
21862
|
+
focus: z26.string().optional().describe("Focus entity or topic (filters content)")
|
|
22321
21863
|
},
|
|
22322
21864
|
async (args) => {
|
|
22323
21865
|
const stateDb2 = getStateDb3();
|
|
@@ -22368,24 +21910,24 @@ function registerBriefTools(server2, getStateDb3) {
|
|
|
22368
21910
|
}
|
|
22369
21911
|
|
|
22370
21912
|
// src/tools/write/config.ts
|
|
22371
|
-
import { z as
|
|
21913
|
+
import { z as z27 } from "zod";
|
|
22372
21914
|
import { saveFlywheelConfigToDb as saveFlywheelConfigToDb2 } from "@velvetmonkey/vault-core";
|
|
22373
21915
|
var VALID_CONFIG_KEYS = {
|
|
22374
|
-
vault_name:
|
|
22375
|
-
exclude_task_tags:
|
|
22376
|
-
exclude_analysis_tags:
|
|
22377
|
-
exclude_entities:
|
|
22378
|
-
exclude_entity_folders:
|
|
22379
|
-
wikilink_strictness:
|
|
22380
|
-
implicit_detection:
|
|
22381
|
-
implicit_patterns:
|
|
22382
|
-
adaptive_strictness:
|
|
22383
|
-
proactive_linking:
|
|
22384
|
-
proactive_min_score:
|
|
22385
|
-
proactive_max_per_file:
|
|
22386
|
-
proactive_max_per_day:
|
|
22387
|
-
custom_categories:
|
|
22388
|
-
type_boost:
|
|
21916
|
+
vault_name: z27.string(),
|
|
21917
|
+
exclude_task_tags: z27.array(z27.string()),
|
|
21918
|
+
exclude_analysis_tags: z27.array(z27.string()),
|
|
21919
|
+
exclude_entities: z27.array(z27.string()),
|
|
21920
|
+
exclude_entity_folders: z27.array(z27.string()),
|
|
21921
|
+
wikilink_strictness: z27.enum(["conservative", "balanced", "aggressive"]),
|
|
21922
|
+
implicit_detection: z27.boolean(),
|
|
21923
|
+
implicit_patterns: z27.array(z27.string()),
|
|
21924
|
+
adaptive_strictness: z27.boolean(),
|
|
21925
|
+
proactive_linking: z27.boolean(),
|
|
21926
|
+
proactive_min_score: z27.number(),
|
|
21927
|
+
proactive_max_per_file: z27.number(),
|
|
21928
|
+
proactive_max_per_day: z27.number(),
|
|
21929
|
+
custom_categories: z27.record(z27.string(), z27.object({
|
|
21930
|
+
type_boost: z27.number().optional()
|
|
22389
21931
|
}))
|
|
22390
21932
|
};
|
|
22391
21933
|
function registerConfigTools(server2, getConfig2, setConfig, getStateDb3) {
|
|
@@ -22395,9 +21937,9 @@ function registerConfigTools(server2, getConfig2, setConfig, getStateDb3) {
|
|
|
22395
21937
|
title: "Flywheel Config",
|
|
22396
21938
|
description: 'Read or update Flywheel configuration.\n- "get": Returns the current FlywheelConfig\n- "set": Updates a single config key and returns the updated config\n\nExample: flywheel_config({ mode: "get" })\nExample: flywheel_config({ mode: "set", key: "exclude_analysis_tags", value: ["habit", "daily"] })',
|
|
22397
21939
|
inputSchema: {
|
|
22398
|
-
mode:
|
|
22399
|
-
key:
|
|
22400
|
-
value:
|
|
21940
|
+
mode: z27.enum(["get", "set"]).describe("Operation mode"),
|
|
21941
|
+
key: z27.string().optional().describe("Config key to update (required for set mode)"),
|
|
21942
|
+
value: z27.unknown().optional().describe("New value for the key (required for set mode)")
|
|
22401
21943
|
}
|
|
22402
21944
|
},
|
|
22403
21945
|
async ({ mode, key, value }) => {
|
|
@@ -22453,7 +21995,7 @@ function registerConfigTools(server2, getConfig2, setConfig, getStateDb3) {
|
|
|
22453
21995
|
// src/tools/write/enrich.ts
|
|
22454
21996
|
init_wikilinks();
|
|
22455
21997
|
init_wikilinkFeedback();
|
|
22456
|
-
import { z as
|
|
21998
|
+
import { z as z28 } from "zod";
|
|
22457
21999
|
import * as fs32 from "fs/promises";
|
|
22458
22000
|
import * as path35 from "path";
|
|
22459
22001
|
import { scanVaultEntities as scanVaultEntities3, SCHEMA_VERSION as SCHEMA_VERSION2 } from "@velvetmonkey/vault-core";
|
|
@@ -22758,10 +22300,10 @@ function registerInitTools(server2, getVaultPath, getStateDb3) {
|
|
|
22758
22300
|
"vault_init",
|
|
22759
22301
|
`Initialize vault for Flywheel. Modes: "status" (check what's ready/missing), "run" (execute missing init steps), "enrich" (scan notes with zero wikilinks and apply entity links).`,
|
|
22760
22302
|
{
|
|
22761
|
-
mode:
|
|
22762
|
-
dry_run:
|
|
22763
|
-
batch_size:
|
|
22764
|
-
offset:
|
|
22303
|
+
mode: z28.enum(["status", "run", "enrich"]).default("status").describe("Operation mode (default: status)"),
|
|
22304
|
+
dry_run: z28.boolean().default(true).describe("For enrich mode: preview without modifying files (default: true)"),
|
|
22305
|
+
batch_size: z28.number().default(50).describe("For enrich mode: max notes per invocation (default: 50)"),
|
|
22306
|
+
offset: z28.number().default(0).describe("For enrich mode: skip this many eligible notes (for pagination)")
|
|
22765
22307
|
},
|
|
22766
22308
|
async ({ mode, dry_run, batch_size, offset }) => {
|
|
22767
22309
|
const stateDb2 = getStateDb3();
|
|
@@ -22788,7 +22330,7 @@ function registerInitTools(server2, getVaultPath, getStateDb3) {
|
|
|
22788
22330
|
}
|
|
22789
22331
|
|
|
22790
22332
|
// src/tools/read/metrics.ts
|
|
22791
|
-
import { z as
|
|
22333
|
+
import { z as z29 } from "zod";
|
|
22792
22334
|
function registerMetricsTools(server2, getIndex, getStateDb3) {
|
|
22793
22335
|
server2.registerTool(
|
|
22794
22336
|
"vault_growth",
|
|
@@ -22796,10 +22338,10 @@ function registerMetricsTools(server2, getIndex, getStateDb3) {
|
|
|
22796
22338
|
title: "Vault Growth",
|
|
22797
22339
|
description: 'Track vault growth over time. Modes: "current" (live snapshot), "history" (time series), "trends" (deltas vs N days ago), "index_activity" (rebuild history). Tracks 11 metrics: note_count, link_count, orphan_count, tag_count, entity_count, avg_links_per_note, link_density, connected_ratio, wikilink_accuracy, wikilink_feedback_volume, wikilink_suppressed_count.',
|
|
22798
22340
|
inputSchema: {
|
|
22799
|
-
mode:
|
|
22800
|
-
metric:
|
|
22801
|
-
days_back:
|
|
22802
|
-
limit:
|
|
22341
|
+
mode: z29.enum(["current", "history", "trends", "index_activity"]).describe("Query mode: current snapshot, historical time series, trend analysis, or index rebuild activity"),
|
|
22342
|
+
metric: z29.string().optional().describe('Filter to specific metric (e.g., "note_count"). Omit for all metrics.'),
|
|
22343
|
+
days_back: z29.number().optional().describe("Number of days to look back for history/trends (default: 30)"),
|
|
22344
|
+
limit: z29.number().optional().describe("Number of recent events to return for index_activity mode (default: 20)")
|
|
22803
22345
|
}
|
|
22804
22346
|
},
|
|
22805
22347
|
async ({ mode, metric, days_back, limit: eventLimit }) => {
|
|
@@ -22872,7 +22414,7 @@ function registerMetricsTools(server2, getIndex, getStateDb3) {
|
|
|
22872
22414
|
}
|
|
22873
22415
|
|
|
22874
22416
|
// src/tools/read/activity.ts
|
|
22875
|
-
import { z as
|
|
22417
|
+
import { z as z30 } from "zod";
|
|
22876
22418
|
function registerActivityTools(server2, getStateDb3, getSessionId2) {
|
|
22877
22419
|
server2.registerTool(
|
|
22878
22420
|
"vault_activity",
|
|
@@ -22880,10 +22422,10 @@ function registerActivityTools(server2, getStateDb3, getSessionId2) {
|
|
|
22880
22422
|
title: "Vault Activity",
|
|
22881
22423
|
description: 'Track tool usage patterns and session activity. Modes:\n- "session": Current session summary (tools called, notes accessed)\n- "sessions": List of recent sessions\n- "note_access": Notes ranked by query frequency\n- "tool_usage": Tool usage patterns (most-used tools, avg duration)',
|
|
22882
22424
|
inputSchema: {
|
|
22883
|
-
mode:
|
|
22884
|
-
session_id:
|
|
22885
|
-
days_back:
|
|
22886
|
-
limit:
|
|
22425
|
+
mode: z30.enum(["session", "sessions", "note_access", "tool_usage"]).describe("Activity query mode"),
|
|
22426
|
+
session_id: z30.string().optional().describe("Specific session ID (for session mode, defaults to current)"),
|
|
22427
|
+
days_back: z30.number().optional().describe("Number of days to look back (default: 30)"),
|
|
22428
|
+
limit: z30.number().optional().describe("Maximum results to return (default: 20)")
|
|
22887
22429
|
}
|
|
22888
22430
|
},
|
|
22889
22431
|
async ({ mode, session_id, days_back, limit: resultLimit }) => {
|
|
@@ -22950,12 +22492,65 @@ function registerActivityTools(server2, getStateDb3, getSessionId2) {
|
|
|
22950
22492
|
}
|
|
22951
22493
|
|
|
22952
22494
|
// src/tools/read/similarity.ts
|
|
22953
|
-
import { z as
|
|
22495
|
+
import { z as z31 } from "zod";
|
|
22954
22496
|
|
|
22955
22497
|
// src/core/read/similarity.ts
|
|
22956
22498
|
init_embeddings();
|
|
22957
22499
|
import * as fs33 from "fs";
|
|
22958
22500
|
import * as path36 from "path";
|
|
22501
|
+
|
|
22502
|
+
// src/core/read/mmr.ts
|
|
22503
|
+
init_embeddings();
|
|
22504
|
+
function selectByMmr(candidates, limit, lambda = 0.7) {
|
|
22505
|
+
if (candidates.length <= limit) return candidates;
|
|
22506
|
+
if (candidates.length === 0) return [];
|
|
22507
|
+
const maxScore = Math.max(...candidates.map((c) => c.score));
|
|
22508
|
+
if (maxScore === 0) return candidates.slice(0, limit);
|
|
22509
|
+
const normScores = /* @__PURE__ */ new Map();
|
|
22510
|
+
for (const c of candidates) {
|
|
22511
|
+
normScores.set(c.id, c.score / maxScore);
|
|
22512
|
+
}
|
|
22513
|
+
const selected = [];
|
|
22514
|
+
const remaining = new Set(candidates.map((_, i) => i));
|
|
22515
|
+
let bestIdx = 0;
|
|
22516
|
+
let bestScore = -Infinity;
|
|
22517
|
+
for (const idx of remaining) {
|
|
22518
|
+
if (candidates[idx].score > bestScore) {
|
|
22519
|
+
bestScore = candidates[idx].score;
|
|
22520
|
+
bestIdx = idx;
|
|
22521
|
+
}
|
|
22522
|
+
}
|
|
22523
|
+
selected.push(candidates[bestIdx]);
|
|
22524
|
+
remaining.delete(bestIdx);
|
|
22525
|
+
while (selected.length < limit && remaining.size > 0) {
|
|
22526
|
+
let bestMmr = -Infinity;
|
|
22527
|
+
let bestCandidate = -1;
|
|
22528
|
+
for (const idx of remaining) {
|
|
22529
|
+
const candidate = candidates[idx];
|
|
22530
|
+
const relevance = normScores.get(candidate.id) || 0;
|
|
22531
|
+
let maxSim = 0;
|
|
22532
|
+
if (candidate.embedding !== null) {
|
|
22533
|
+
for (const sel of selected) {
|
|
22534
|
+
if (sel.embedding !== null) {
|
|
22535
|
+
const sim = cosineSimilarity(candidate.embedding, sel.embedding);
|
|
22536
|
+
if (sim > maxSim) maxSim = sim;
|
|
22537
|
+
}
|
|
22538
|
+
}
|
|
22539
|
+
}
|
|
22540
|
+
const mmr = lambda * relevance - (1 - lambda) * maxSim;
|
|
22541
|
+
if (mmr > bestMmr) {
|
|
22542
|
+
bestMmr = mmr;
|
|
22543
|
+
bestCandidate = idx;
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
if (bestCandidate === -1) break;
|
|
22547
|
+
selected.push(candidates[bestCandidate]);
|
|
22548
|
+
remaining.delete(bestCandidate);
|
|
22549
|
+
}
|
|
22550
|
+
return selected;
|
|
22551
|
+
}
|
|
22552
|
+
|
|
22553
|
+
// src/core/read/similarity.ts
|
|
22959
22554
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
22960
22555
|
"the",
|
|
22961
22556
|
"be",
|
|
@@ -23229,9 +22824,9 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
|
23229
22824
|
title: "Find Similar Notes",
|
|
23230
22825
|
description: "Find notes similar to a given note using FTS5 keyword matching. When embeddings have been built (via init_semantic), automatically uses hybrid ranking (BM25 + embedding similarity via Reciprocal Rank Fusion). Already-linked notes are automatically excluded.",
|
|
23231
22826
|
inputSchema: {
|
|
23232
|
-
path:
|
|
23233
|
-
limit:
|
|
23234
|
-
diversity:
|
|
22827
|
+
path: z31.string().describe('Path to the source note (relative to vault root, e.g. "projects/alpha.md")'),
|
|
22828
|
+
limit: z31.number().optional().describe("Maximum number of similar notes to return (default: 10)"),
|
|
22829
|
+
diversity: z31.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
|
|
23235
22830
|
}
|
|
23236
22831
|
},
|
|
23237
22832
|
async ({ path: path39, limit, diversity }) => {
|
|
@@ -23276,7 +22871,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
|
23276
22871
|
|
|
23277
22872
|
// src/tools/read/semantic.ts
|
|
23278
22873
|
init_embeddings();
|
|
23279
|
-
import { z as
|
|
22874
|
+
import { z as z32 } from "zod";
|
|
23280
22875
|
import { getAllEntitiesFromDb as getAllEntitiesFromDb3 } from "@velvetmonkey/vault-core";
|
|
23281
22876
|
function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
23282
22877
|
server2.registerTool(
|
|
@@ -23285,7 +22880,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
|
23285
22880
|
title: "Initialize Semantic Search",
|
|
23286
22881
|
description: "Download the embedding model and build semantic search index for this vault. After running, search and find_similar automatically use hybrid ranking (BM25 + semantic). Run once per vault \u2014 subsequent calls skip already-embedded notes unless force=true.",
|
|
23287
22882
|
inputSchema: {
|
|
23288
|
-
force:
|
|
22883
|
+
force: z32.boolean().optional().describe(
|
|
23289
22884
|
"Rebuild all embeddings even if they already exist (default: false)"
|
|
23290
22885
|
)
|
|
23291
22886
|
}
|
|
@@ -23380,7 +22975,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
|
23380
22975
|
|
|
23381
22976
|
// src/tools/read/merges.ts
|
|
23382
22977
|
init_levenshtein();
|
|
23383
|
-
import { z as
|
|
22978
|
+
import { z as z33 } from "zod";
|
|
23384
22979
|
import { getAllEntitiesFromDb as getAllEntitiesFromDb4, getDismissedMergePairs, recordMergeDismissal } from "@velvetmonkey/vault-core";
|
|
23385
22980
|
function normalizeName(name) {
|
|
23386
22981
|
return name.toLowerCase().replace(/[.\-_]/g, "").replace(/js$/, "").replace(/ts$/, "");
|
|
@@ -23390,7 +22985,7 @@ function registerMergeTools2(server2, getStateDb3) {
|
|
|
23390
22985
|
"suggest_entity_merges",
|
|
23391
22986
|
"Find potential duplicate entities that could be merged based on name similarity",
|
|
23392
22987
|
{
|
|
23393
|
-
limit:
|
|
22988
|
+
limit: z33.number().optional().default(50).describe("Maximum number of suggestions to return")
|
|
23394
22989
|
},
|
|
23395
22990
|
async ({ limit }) => {
|
|
23396
22991
|
const stateDb2 = getStateDb3();
|
|
@@ -23492,11 +23087,11 @@ function registerMergeTools2(server2, getStateDb3) {
|
|
|
23492
23087
|
"dismiss_merge_suggestion",
|
|
23493
23088
|
"Permanently dismiss a merge suggestion so it never reappears",
|
|
23494
23089
|
{
|
|
23495
|
-
source_path:
|
|
23496
|
-
target_path:
|
|
23497
|
-
source_name:
|
|
23498
|
-
target_name:
|
|
23499
|
-
reason:
|
|
23090
|
+
source_path: z33.string().describe("Path of the source entity"),
|
|
23091
|
+
target_path: z33.string().describe("Path of the target entity"),
|
|
23092
|
+
source_name: z33.string().describe("Name of the source entity"),
|
|
23093
|
+
target_name: z33.string().describe("Name of the target entity"),
|
|
23094
|
+
reason: z33.string().describe("Original suggestion reason")
|
|
23500
23095
|
},
|
|
23501
23096
|
async ({ source_path, target_path, source_name, target_name, reason }) => {
|
|
23502
23097
|
const stateDb2 = getStateDb3();
|
|
@@ -23515,7 +23110,7 @@ function registerMergeTools2(server2, getStateDb3) {
|
|
|
23515
23110
|
}
|
|
23516
23111
|
|
|
23517
23112
|
// src/tools/read/temporalAnalysis.ts
|
|
23518
|
-
import { z as
|
|
23113
|
+
import { z as z34 } from "zod";
|
|
23519
23114
|
init_wikilinks();
|
|
23520
23115
|
function formatDate3(d) {
|
|
23521
23116
|
return d.toISOString().split("T")[0];
|
|
@@ -24041,9 +23636,9 @@ function registerTemporalAnalysisTools(server2, getIndex, getVaultPath, getState
|
|
|
24041
23636
|
title: "Context Around Date",
|
|
24042
23637
|
description: "Reconstruct what was happening in the vault around a specific date. Shows modified/created notes, active entities, wikilink activity, and file moves within a time window.",
|
|
24043
23638
|
inputSchema: {
|
|
24044
|
-
date:
|
|
24045
|
-
window_days:
|
|
24046
|
-
limit:
|
|
23639
|
+
date: z34.string().describe("Center date in YYYY-MM-DD format"),
|
|
23640
|
+
window_days: z34.coerce.number().default(3).describe("Days before and after the center date (default 3 = 7-day window)"),
|
|
23641
|
+
limit: z34.coerce.number().default(50).describe("Maximum number of notes to return")
|
|
24047
23642
|
}
|
|
24048
23643
|
},
|
|
24049
23644
|
async ({ date, window_days, limit: requestedLimit }) => {
|
|
@@ -24058,12 +23653,12 @@ function registerTemporalAnalysisTools(server2, getIndex, getVaultPath, getState
|
|
|
24058
23653
|
title: "Predict Stale Notes",
|
|
24059
23654
|
description: "Multi-signal staleness prediction. Scores notes by importance (backlinks, hub score, tasks, status) and staleness risk (age, entity disconnect, task urgency). Returns concrete recommendations: archive, update, review, or low_priority.",
|
|
24060
23655
|
inputSchema: {
|
|
24061
|
-
days:
|
|
24062
|
-
min_importance:
|
|
24063
|
-
include_recommendations:
|
|
24064
|
-
folder:
|
|
24065
|
-
limit:
|
|
24066
|
-
offset:
|
|
23656
|
+
days: z34.coerce.number().default(30).describe("Notes not modified in this many days (default 30)"),
|
|
23657
|
+
min_importance: z34.coerce.number().default(0).describe("Filter by minimum importance score 0-100 (default 0)"),
|
|
23658
|
+
include_recommendations: z34.boolean().default(true).describe("Include action recommendations (default true)"),
|
|
23659
|
+
folder: z34.string().optional().describe("Limit to notes in this folder"),
|
|
23660
|
+
limit: z34.coerce.number().default(30).describe("Maximum results to return (default 30)"),
|
|
23661
|
+
offset: z34.coerce.number().default(0).describe("Results to skip for pagination (default 0)")
|
|
24067
23662
|
}
|
|
24068
23663
|
},
|
|
24069
23664
|
async ({ days, min_importance, include_recommendations, folder, limit: requestedLimit, offset }) => {
|
|
@@ -24087,9 +23682,9 @@ function registerTemporalAnalysisTools(server2, getIndex, getVaultPath, getState
|
|
|
24087
23682
|
title: "Track Concept Evolution",
|
|
24088
23683
|
description: "Timeline of how an entity has evolved: link additions/removals, feedback events, category changes, co-occurrence shifts. Shows current state, chronological event history, link durability stats, and top co-occurrence neighbors.",
|
|
24089
23684
|
inputSchema: {
|
|
24090
|
-
entity:
|
|
24091
|
-
days_back:
|
|
24092
|
-
include_cooccurrence:
|
|
23685
|
+
entity: z34.string().describe("Entity name (case-insensitive)"),
|
|
23686
|
+
days_back: z34.coerce.number().default(90).describe("How far back to look (default 90 days)"),
|
|
23687
|
+
include_cooccurrence: z34.boolean().default(true).describe("Include co-occurrence neighbors (default true)")
|
|
24093
23688
|
}
|
|
24094
23689
|
},
|
|
24095
23690
|
async ({ entity, days_back, include_cooccurrence }) => {
|
|
@@ -24109,10 +23704,10 @@ function registerTemporalAnalysisTools(server2, getIndex, getVaultPath, getState
|
|
|
24109
23704
|
title: "Temporal Summary",
|
|
24110
23705
|
description: "Generate a vault pulse report for a time period. Composes context, staleness prediction, and concept evolution into a single summary. Shows activity snapshot, entity momentum, and maintenance alerts. Use for weekly/monthly/quarterly reviews.",
|
|
24111
23706
|
inputSchema: {
|
|
24112
|
-
start_date:
|
|
24113
|
-
end_date:
|
|
24114
|
-
focus_entities:
|
|
24115
|
-
limit:
|
|
23707
|
+
start_date: z34.string().describe("Start of period in YYYY-MM-DD format"),
|
|
23708
|
+
end_date: z34.string().describe("End of period in YYYY-MM-DD format"),
|
|
23709
|
+
focus_entities: z34.array(z34.string()).optional().describe("Specific entities to track evolution for (default: top 5 active entities in period)"),
|
|
23710
|
+
limit: z34.coerce.number().default(50).describe("Maximum notes to include in context snapshot")
|
|
24116
23711
|
}
|
|
24117
23712
|
},
|
|
24118
23713
|
async ({ start_date, end_date, focus_entities, limit: requestedLimit }) => {
|
|
@@ -24131,15 +23726,15 @@ function registerTemporalAnalysisTools(server2, getIndex, getVaultPath, getState
|
|
|
24131
23726
|
}
|
|
24132
23727
|
|
|
24133
23728
|
// src/tools/read/sessionHistory.ts
|
|
24134
|
-
import { z as
|
|
23729
|
+
import { z as z35 } from "zod";
|
|
24135
23730
|
function registerSessionHistoryTools(server2, getStateDb3) {
|
|
24136
23731
|
server2.tool(
|
|
24137
23732
|
"vault_session_history",
|
|
24138
23733
|
"View session history. Without session_id: lists recent sessions. With session_id: returns chronological tool invocations, notes accessed, and timing. Hierarchical sessions supported (parent ID includes child sessions).",
|
|
24139
23734
|
{
|
|
24140
|
-
session_id:
|
|
24141
|
-
include_children:
|
|
24142
|
-
limit:
|
|
23735
|
+
session_id: z35.string().optional().describe("Session ID for detail view. Omit for recent sessions list."),
|
|
23736
|
+
include_children: z35.boolean().optional().describe("Include child sessions (default: true)"),
|
|
23737
|
+
limit: z35.number().min(1).max(500).optional().describe("Max invocations to return in detail view (default: 200)")
|
|
24143
23738
|
},
|
|
24144
23739
|
async (args) => {
|
|
24145
23740
|
const stateDb2 = getStateDb3();
|
|
@@ -24187,7 +23782,7 @@ function registerSessionHistoryTools(server2, getStateDb3) {
|
|
|
24187
23782
|
}
|
|
24188
23783
|
|
|
24189
23784
|
// src/tools/read/entityHistory.ts
|
|
24190
|
-
import { z as
|
|
23785
|
+
import { z as z36 } from "zod";
|
|
24191
23786
|
|
|
24192
23787
|
// src/core/read/entityHistory.ts
|
|
24193
23788
|
function normalizeTimestamp(ts) {
|
|
@@ -24345,8 +23940,8 @@ function registerEntityHistoryTools(server2, getStateDb3) {
|
|
|
24345
23940
|
"vault_entity_history",
|
|
24346
23941
|
"Get a unified timeline of everything about an entity: when it was linked, feedback received, suggestion scores, edge weight changes, metadata mutations, memories, and corrections. Sorted chronologically with pagination.",
|
|
24347
23942
|
{
|
|
24348
|
-
entity_name:
|
|
24349
|
-
event_types:
|
|
23943
|
+
entity_name: z36.string().describe("Entity name to query (case-insensitive)"),
|
|
23944
|
+
event_types: z36.array(z36.enum([
|
|
24350
23945
|
"application",
|
|
24351
23946
|
"feedback",
|
|
24352
23947
|
"suggestion",
|
|
@@ -24355,10 +23950,10 @@ function registerEntityHistoryTools(server2, getStateDb3) {
|
|
|
24355
23950
|
"memory",
|
|
24356
23951
|
"correction"
|
|
24357
23952
|
])).optional().describe("Filter to specific event types. Omit for all types."),
|
|
24358
|
-
start_date:
|
|
24359
|
-
end_date:
|
|
24360
|
-
limit:
|
|
24361
|
-
offset:
|
|
23953
|
+
start_date: z36.string().optional().describe("Start date (YYYY-MM-DD) for date range filter"),
|
|
23954
|
+
end_date: z36.string().optional().describe("End date (YYYY-MM-DD) for date range filter"),
|
|
23955
|
+
limit: z36.number().min(1).max(200).optional().describe("Max events to return (default: 50)"),
|
|
23956
|
+
offset: z36.number().min(0).optional().describe("Offset for pagination (default: 0)")
|
|
24362
23957
|
},
|
|
24363
23958
|
async (args) => {
|
|
24364
23959
|
const stateDb2 = getStateDb3();
|
|
@@ -24390,7 +23985,7 @@ function registerEntityHistoryTools(server2, getStateDb3) {
|
|
|
24390
23985
|
}
|
|
24391
23986
|
|
|
24392
23987
|
// src/tools/read/learningReport.ts
|
|
24393
|
-
import { z as
|
|
23988
|
+
import { z as z37 } from "zod";
|
|
24394
23989
|
|
|
24395
23990
|
// src/core/read/learningReport.ts
|
|
24396
23991
|
function isoDate(d) {
|
|
@@ -24526,8 +24121,8 @@ function registerLearningReportTools(server2, getIndex, getStateDb3) {
|
|
|
24526
24121
|
"flywheel_learning_report",
|
|
24527
24122
|
"Get a narrative report of the flywheel auto-linking system's learning progress. Shows: applications by day, feedback (positive/negative), survival rate, top rejected entities, suggestion funnel (evaluations \u2192 applications \u2192 survivals), and graph growth. Use compare=true for period-over-period deltas.",
|
|
24528
24123
|
{
|
|
24529
|
-
days_back:
|
|
24530
|
-
compare:
|
|
24124
|
+
days_back: z37.number().min(1).max(365).optional().describe("Analysis window in days (default: 7). Use 1 for today, 2 for last 48h, etc."),
|
|
24125
|
+
compare: z37.boolean().optional().describe("Include comparison with the preceding equal-length period (default: false)")
|
|
24531
24126
|
},
|
|
24532
24127
|
async (args) => {
|
|
24533
24128
|
const stateDb2 = getStateDb3();
|
|
@@ -24554,7 +24149,7 @@ function registerLearningReportTools(server2, getIndex, getStateDb3) {
|
|
|
24554
24149
|
}
|
|
24555
24150
|
|
|
24556
24151
|
// src/tools/read/calibrationExport.ts
|
|
24557
|
-
import { z as
|
|
24152
|
+
import { z as z38 } from "zod";
|
|
24558
24153
|
|
|
24559
24154
|
// src/core/read/calibrationExport.ts
|
|
24560
24155
|
init_wikilinkFeedback();
|
|
@@ -24847,8 +24442,8 @@ function registerCalibrationExportTools(server2, getIndex, getStateDb3, getConfi
|
|
|
24847
24442
|
"flywheel_calibration_export",
|
|
24848
24443
|
"Export anonymized aggregate scoring data for cross-vault algorithm calibration. No entity names, note paths, or content \u2014 safe to share. Includes: suggestion funnel, per-layer contribution averages, survival rates by entity category, score distribution, suppression stats, recency/co-occurrence effectiveness, and threshold sweep.",
|
|
24849
24444
|
{
|
|
24850
|
-
days_back:
|
|
24851
|
-
include_vault_id:
|
|
24445
|
+
days_back: z38.number().min(1).max(365).optional().describe("Analysis window in days (default: 30)"),
|
|
24446
|
+
include_vault_id: z38.boolean().optional().describe("Include anonymous vault ID for longitudinal tracking (default: true)")
|
|
24852
24447
|
},
|
|
24853
24448
|
async (args) => {
|
|
24854
24449
|
const stateDb2 = getStateDb3();
|
|
@@ -25135,7 +24730,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
25135
24730
|
const schemaIdx = handlerIdx - 1;
|
|
25136
24731
|
const schema = args[schemaIdx];
|
|
25137
24732
|
if (schema && typeof schema === "object" && !Array.isArray(schema)) {
|
|
25138
|
-
schema.vault =
|
|
24733
|
+
schema.vault = z39.string().optional().describe(
|
|
25139
24734
|
`Vault name for multi-vault mode. Available: ${registry.getVaultNames().join(", ")}. Default: ${registry.primaryName}`
|
|
25140
24735
|
);
|
|
25141
24736
|
}
|
|
@@ -25261,7 +24856,6 @@ function registerAllTools(targetServer, ctx) {
|
|
|
25261
24856
|
registerLearningReportTools(targetServer, gvi, gsd);
|
|
25262
24857
|
registerCalibrationExportTools(targetServer, gvi, gsd, gcf);
|
|
25263
24858
|
registerMemoryTools(targetServer, gsd);
|
|
25264
|
-
registerRecallTools(targetServer, gsd, gvp, () => gvi() ?? null);
|
|
25265
24859
|
registerBriefTools(targetServer, gsd);
|
|
25266
24860
|
registerVaultResources(targetServer, () => gvi() ?? null);
|
|
25267
24861
|
}
|
|
@@ -25445,7 +25039,10 @@ function updateFlywheelConfig(config) {
|
|
|
25445
25039
|
flywheelConfig = config;
|
|
25446
25040
|
setWikilinkConfig(config);
|
|
25447
25041
|
const ctx = getActiveVaultContext();
|
|
25448
|
-
if (ctx)
|
|
25042
|
+
if (ctx) {
|
|
25043
|
+
ctx.flywheelConfig = config;
|
|
25044
|
+
setActiveScope(buildVaultScope(ctx));
|
|
25045
|
+
}
|
|
25449
25046
|
}
|
|
25450
25047
|
async function bootVault(ctx, startTime) {
|
|
25451
25048
|
const vp = ctx.vaultPath;
|
|
@@ -25598,6 +25195,7 @@ async function main() {
|
|
|
25598
25195
|
loadVaultCooccurrence(primaryCtx);
|
|
25599
25196
|
activateVault(primaryCtx);
|
|
25600
25197
|
await bootVault(primaryCtx, startTime);
|
|
25198
|
+
activateVault(primaryCtx);
|
|
25601
25199
|
if (vaultConfigs && vaultConfigs.length > 1) {
|
|
25602
25200
|
const secondaryConfigs = vaultConfigs.slice(1);
|
|
25603
25201
|
(async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.150",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 74 tools for search, backlinks, graph queries, mutations, agent memory, and hybrid semantic search.",
|
|
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.150",
|
|
58
58
|
"better-sqlite3": "^12.0.0",
|
|
59
59
|
"chokidar": "^4.0.0",
|
|
60
60
|
"gray-matter": "^4.0.3",
|
|
@@ -85,4 +85,4 @@
|
|
|
85
85
|
"publishConfig": {
|
|
86
86
|
"access": "public"
|
|
87
87
|
}
|
|
88
|
-
}
|
|
88
|
+
}
|