@velvetmonkey/flywheel-memory 2.4.2 → 2.4.3
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 +275 -60
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8392,6 +8392,155 @@ function createStepTracker() {
|
|
|
8392
8392
|
}
|
|
8393
8393
|
};
|
|
8394
8394
|
}
|
|
8395
|
+
function compactStep(step) {
|
|
8396
|
+
const out = step.output ?? {};
|
|
8397
|
+
let summary;
|
|
8398
|
+
switch (step.name) {
|
|
8399
|
+
case "entity_scan":
|
|
8400
|
+
summary = {
|
|
8401
|
+
entity_count: asNum(out.entity_count),
|
|
8402
|
+
added_count: asArrayLen(out.added),
|
|
8403
|
+
removed_count: asArrayLen(out.removed),
|
|
8404
|
+
alias_change_count: asArrayLen(out.alias_changes),
|
|
8405
|
+
category_change_count: asArrayLen(out.category_changes)
|
|
8406
|
+
};
|
|
8407
|
+
break;
|
|
8408
|
+
case "hub_scores":
|
|
8409
|
+
summary = {
|
|
8410
|
+
updated: asNum(out.updated),
|
|
8411
|
+
diff_count: asArrayLen(out.diffs)
|
|
8412
|
+
};
|
|
8413
|
+
break;
|
|
8414
|
+
case "forward_links":
|
|
8415
|
+
summary = {
|
|
8416
|
+
total_resolved: asNum(out.total_resolved),
|
|
8417
|
+
total_dead: asNum(out.total_dead),
|
|
8418
|
+
new_dead_count: asArrayLen(out.new_dead_links),
|
|
8419
|
+
diff_count: asArrayLen(out.link_diffs)
|
|
8420
|
+
};
|
|
8421
|
+
break;
|
|
8422
|
+
case "wikilink_check":
|
|
8423
|
+
summary = {
|
|
8424
|
+
tracked_count: asArrayLen(out.tracked),
|
|
8425
|
+
mention_count: asArrayLen(out.mentions)
|
|
8426
|
+
};
|
|
8427
|
+
break;
|
|
8428
|
+
case "prospect_scan": {
|
|
8429
|
+
const prospects = Array.isArray(out.prospects) ? out.prospects : [];
|
|
8430
|
+
summary = {
|
|
8431
|
+
implicit_count: prospects.reduce((s, p) => s + (Array.isArray(p.implicit) ? p.implicit.length : 0), 0),
|
|
8432
|
+
dead_match_count: prospects.reduce((s, p) => s + (Array.isArray(p.deadLinkMatches) ? p.deadLinkMatches.length : 0), 0)
|
|
8433
|
+
};
|
|
8434
|
+
break;
|
|
8435
|
+
}
|
|
8436
|
+
case "suggestion_scoring":
|
|
8437
|
+
summary = { scored_files: asNum(out.scored_files) };
|
|
8438
|
+
break;
|
|
8439
|
+
case "implicit_feedback":
|
|
8440
|
+
summary = {
|
|
8441
|
+
removal_count: asArrayLen(out.removals),
|
|
8442
|
+
addition_count: asArrayLen(out.additions),
|
|
8443
|
+
suppressed_count: asArrayLen(out.newly_suppressed)
|
|
8444
|
+
};
|
|
8445
|
+
break;
|
|
8446
|
+
case "note_embeddings":
|
|
8447
|
+
summary = { updated: asNum(out.updated), removed: asNum(out.removed) };
|
|
8448
|
+
break;
|
|
8449
|
+
case "entity_embeddings":
|
|
8450
|
+
summary = { updated: asNum(out.updated) };
|
|
8451
|
+
break;
|
|
8452
|
+
case "fts5_incremental":
|
|
8453
|
+
summary = { updated: asNum(out.updated), removed: asNum(out.removed) };
|
|
8454
|
+
break;
|
|
8455
|
+
case "index_rebuild":
|
|
8456
|
+
summary = {
|
|
8457
|
+
note_count: asNum(out.note_count),
|
|
8458
|
+
entity_count: asNum(out.entity_count),
|
|
8459
|
+
tag_count: asNum(out.tag_count)
|
|
8460
|
+
};
|
|
8461
|
+
break;
|
|
8462
|
+
case "index_cache":
|
|
8463
|
+
summary = { saved: asBool(out.saved) };
|
|
8464
|
+
break;
|
|
8465
|
+
case "task_cache":
|
|
8466
|
+
summary = { updated: asNum(out.updated), removed: asNum(out.removed) };
|
|
8467
|
+
break;
|
|
8468
|
+
case "tag_scan":
|
|
8469
|
+
summary = { added_count: asNum(out.total_added), removed_count: asNum(out.total_removed) };
|
|
8470
|
+
break;
|
|
8471
|
+
case "drain_proactive_queue":
|
|
8472
|
+
summary = {
|
|
8473
|
+
total_applied: asNum(out.total_applied),
|
|
8474
|
+
expired: asNum(out.expired),
|
|
8475
|
+
skipped_mtime: asNum(out.skipped_mtime),
|
|
8476
|
+
skipped_daily_cap: asNum(out.skipped_daily_cap)
|
|
8477
|
+
};
|
|
8478
|
+
break;
|
|
8479
|
+
case "proactive_enqueue":
|
|
8480
|
+
summary = { enqueued: asNum(out.enqueued), total_candidates: asNum(out.total_candidates) };
|
|
8481
|
+
break;
|
|
8482
|
+
case "recency":
|
|
8483
|
+
case "cooccurrence":
|
|
8484
|
+
case "edge_weights":
|
|
8485
|
+
summary = { rebuilt: asBool(out.rebuilt) };
|
|
8486
|
+
break;
|
|
8487
|
+
case "incremental_recency":
|
|
8488
|
+
summary = { entities_updated: asNum(out.entities_updated) };
|
|
8489
|
+
break;
|
|
8490
|
+
case "corrections":
|
|
8491
|
+
summary = { processed: asNum(out.processed) };
|
|
8492
|
+
break;
|
|
8493
|
+
case "retrieval_cooccurrence":
|
|
8494
|
+
summary = { pairs_inserted: asNum(out.pairs_inserted) };
|
|
8495
|
+
break;
|
|
8496
|
+
case "integrity_check":
|
|
8497
|
+
case "maintenance":
|
|
8498
|
+
summary = {
|
|
8499
|
+
...out.skipped != null ? { skipped: asBool(out.skipped) } : {},
|
|
8500
|
+
...typeof out.reason === "string" ? { reason: out.reason } : {}
|
|
8501
|
+
};
|
|
8502
|
+
break;
|
|
8503
|
+
case "note_moves":
|
|
8504
|
+
summary = { count: asNum(out.count ?? (Array.isArray(out.renames) ? out.renames.length : 0)) };
|
|
8505
|
+
break;
|
|
8506
|
+
default:
|
|
8507
|
+
summary = {};
|
|
8508
|
+
for (const [k, v] of Object.entries(out)) {
|
|
8509
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "string") {
|
|
8510
|
+
summary[k] = v;
|
|
8511
|
+
}
|
|
8512
|
+
}
|
|
8513
|
+
break;
|
|
8514
|
+
}
|
|
8515
|
+
return {
|
|
8516
|
+
name: step.name,
|
|
8517
|
+
duration_ms: step.duration_ms,
|
|
8518
|
+
...step.skipped ? { skipped: true, skip_reason: step.skip_reason } : {},
|
|
8519
|
+
summary
|
|
8520
|
+
};
|
|
8521
|
+
}
|
|
8522
|
+
function compactPipelineRun(event) {
|
|
8523
|
+
const paths = event.changed_paths ?? [];
|
|
8524
|
+
return {
|
|
8525
|
+
timestamp: event.timestamp,
|
|
8526
|
+
trigger: event.trigger,
|
|
8527
|
+
duration_ms: event.duration_ms,
|
|
8528
|
+
files_changed: event.files_changed,
|
|
8529
|
+
changed_paths_total: event.files_changed ?? paths.length,
|
|
8530
|
+
changed_paths_sample: paths.slice(0, 3),
|
|
8531
|
+
step_count: event.steps?.length ?? 0,
|
|
8532
|
+
steps: (event.steps ?? []).map(compactStep)
|
|
8533
|
+
};
|
|
8534
|
+
}
|
|
8535
|
+
function asNum(v) {
|
|
8536
|
+
return typeof v === "number" ? v : 0;
|
|
8537
|
+
}
|
|
8538
|
+
function asBool(v) {
|
|
8539
|
+
return typeof v === "boolean" ? v : false;
|
|
8540
|
+
}
|
|
8541
|
+
function asArrayLen(v) {
|
|
8542
|
+
return Array.isArray(v) ? v.length : 0;
|
|
8543
|
+
}
|
|
8395
8544
|
function recordIndexEvent(stateDb2, event) {
|
|
8396
8545
|
stateDb2.db.prepare(
|
|
8397
8546
|
`INSERT INTO index_events (timestamp, trigger, duration_ms, success, note_count, files_changed, changed_paths, error, steps)
|
|
@@ -11407,7 +11556,7 @@ function getSessionHistory(stateDb2, sessionId) {
|
|
|
11407
11556
|
}));
|
|
11408
11557
|
}
|
|
11409
11558
|
function getSessionDetail(stateDb2, sessionId, options = {}) {
|
|
11410
|
-
const { include_children = true, limit =
|
|
11559
|
+
const { include_children = true, limit = 50 } = options;
|
|
11411
11560
|
const rows = include_children ? stateDb2.db.prepare(`
|
|
11412
11561
|
SELECT * FROM tool_invocations
|
|
11413
11562
|
WHERE session_id = ? OR session_id LIKE ?
|
|
@@ -13330,7 +13479,8 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
13330
13479
|
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 }) => {
|
|
13331
13480
|
requireIndex();
|
|
13332
13481
|
const limit = Math.min(requestedLimit ?? 10, MAX_LIMIT);
|
|
13333
|
-
const
|
|
13482
|
+
const enrichN = Math.min(requestedDetailCount ?? 5, limit);
|
|
13483
|
+
const expandN = Math.min(enrichN, 8);
|
|
13334
13484
|
const index = getIndex();
|
|
13335
13485
|
const vaultPath2 = getVaultPath();
|
|
13336
13486
|
if (prefix && query) {
|
|
@@ -13384,7 +13534,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
13384
13534
|
const limitedNotes = matchingNotes.slice(0, limit);
|
|
13385
13535
|
const stateDb2 = getStateDb4();
|
|
13386
13536
|
const notes = limitedNotes.map(
|
|
13387
|
-
(note, i) => (i <
|
|
13537
|
+
(note, i) => (i < enrichN ? enrichResult : enrichResultLight)({ path: note.path, title: note.title }, index, stateDb2)
|
|
13388
13538
|
);
|
|
13389
13539
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
13390
13540
|
total_matches: totalMatches,
|
|
@@ -13519,7 +13669,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
13519
13669
|
await enhanceSnippets(results2, query, vaultPath2);
|
|
13520
13670
|
if (consumer === "llm") {
|
|
13521
13671
|
applySandwichOrdering(results2);
|
|
13522
|
-
await expandToSections(results2, index, vaultPath2,
|
|
13672
|
+
await expandToSections(results2, index, vaultPath2, expandN);
|
|
13523
13673
|
stripInternalFields(results2);
|
|
13524
13674
|
}
|
|
13525
13675
|
const entitySection2 = await entitySectionPromise;
|
|
@@ -13567,7 +13717,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
13567
13717
|
await enhanceSnippets(results2, query, vaultPath2);
|
|
13568
13718
|
if (consumer === "llm") {
|
|
13569
13719
|
applySandwichOrdering(results2);
|
|
13570
|
-
await expandToSections(results2, index, vaultPath2,
|
|
13720
|
+
await expandToSections(results2, index, vaultPath2, expandN);
|
|
13571
13721
|
stripInternalFields(results2);
|
|
13572
13722
|
}
|
|
13573
13723
|
const entitySection2 = await entitySectionPromise;
|
|
@@ -13594,7 +13744,7 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
13594
13744
|
await enhanceSnippets(results, query, vaultPath2);
|
|
13595
13745
|
if (consumer === "llm") {
|
|
13596
13746
|
applySandwichOrdering(results);
|
|
13597
|
-
await expandToSections(results, index, vaultPath2,
|
|
13747
|
+
await expandToSections(results, index, vaultPath2, expandN);
|
|
13598
13748
|
stripInternalFields(results);
|
|
13599
13749
|
}
|
|
13600
13750
|
const entitySection = await entitySectionPromise;
|
|
@@ -14533,13 +14683,28 @@ function registerGraphExportTools(server2, getIndex, getVaultPath, getStateDb4)
|
|
|
14533
14683
|
include_cooccurrence: z3.boolean().default(true).describe("Include co-occurrence edges between entities"),
|
|
14534
14684
|
min_edge_weight: z3.number().default(0).describe("Minimum edge weight threshold (filters weighted edges)"),
|
|
14535
14685
|
center_entity: z3.string().optional().describe("Center the export on this entity (ego network). Only includes nodes within `depth` hops."),
|
|
14536
|
-
depth: z3.number().default(1).describe("Hops from center_entity to include (default 1). Ignored without center_entity.")
|
|
14686
|
+
depth: z3.number().default(1).describe("Hops from center_entity to include (default 1). Ignored without center_entity."),
|
|
14687
|
+
max_nodes: z3.number().default(500).describe("Maximum nodes in export when no center_entity. Pass higher for full vault exports.")
|
|
14537
14688
|
},
|
|
14538
|
-
async ({ format, include_cooccurrence, min_edge_weight, center_entity, depth }) => {
|
|
14689
|
+
async ({ format, include_cooccurrence, min_edge_weight, center_entity, depth, max_nodes }) => {
|
|
14539
14690
|
requireIndex();
|
|
14540
14691
|
const index = getIndex();
|
|
14541
14692
|
const stateDb2 = getStateDb4?.() ?? null;
|
|
14542
14693
|
const data = buildGraphData(index, stateDb2, { include_cooccurrence, min_edge_weight, center_entity, depth });
|
|
14694
|
+
if (!center_entity && data.nodes.length > max_nodes) {
|
|
14695
|
+
return {
|
|
14696
|
+
content: [{
|
|
14697
|
+
type: "text",
|
|
14698
|
+
text: JSON.stringify({
|
|
14699
|
+
error: `Graph has ${data.nodes.length} nodes and ${data.edges.length} edges, exceeding max_nodes=${max_nodes}. Use center_entity to scope, or pass a higher max_nodes.`,
|
|
14700
|
+
node_count: data.nodes.length,
|
|
14701
|
+
edge_count: data.edges.length,
|
|
14702
|
+
max_nodes
|
|
14703
|
+
})
|
|
14704
|
+
}],
|
|
14705
|
+
isError: true
|
|
14706
|
+
};
|
|
14707
|
+
}
|
|
14543
14708
|
let output;
|
|
14544
14709
|
if (format === "json") {
|
|
14545
14710
|
output = JSON.stringify(data, null, 2);
|
|
@@ -15497,31 +15662,33 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15497
15662
|
trigger: z5.string(),
|
|
15498
15663
|
duration_ms: z5.number(),
|
|
15499
15664
|
files_changed: z5.number().nullable(),
|
|
15500
|
-
|
|
15665
|
+
changed_paths_total: z5.number().describe("Total number of changed files"),
|
|
15666
|
+
changed_paths_sample: z5.array(z5.string()).describe("Up to 3 sample changed paths"),
|
|
15667
|
+
step_count: z5.number().describe("Number of pipeline steps"),
|
|
15501
15668
|
steps: z5.array(z5.object({
|
|
15502
15669
|
name: z5.string(),
|
|
15503
15670
|
duration_ms: z5.number(),
|
|
15504
|
-
input: z5.record(z5.unknown()),
|
|
15505
|
-
output: z5.record(z5.unknown()),
|
|
15506
15671
|
skipped: z5.boolean().optional(),
|
|
15507
|
-
skip_reason: z5.string().optional()
|
|
15508
|
-
|
|
15509
|
-
|
|
15672
|
+
skip_reason: z5.string().optional(),
|
|
15673
|
+
summary: z5.record(z5.union([z5.number(), z5.boolean(), z5.string()]))
|
|
15674
|
+
})).optional().describe("Compact step summaries (full mode only)")
|
|
15675
|
+
}).optional().describe("Most recent watcher pipeline run"),
|
|
15510
15676
|
recent_pipelines: z5.array(z5.object({
|
|
15511
15677
|
timestamp: z5.number(),
|
|
15512
15678
|
trigger: z5.string(),
|
|
15513
15679
|
duration_ms: z5.number(),
|
|
15514
15680
|
files_changed: z5.number().nullable(),
|
|
15515
|
-
|
|
15681
|
+
changed_paths_total: z5.number(),
|
|
15682
|
+
changed_paths_sample: z5.array(z5.string()),
|
|
15683
|
+
step_count: z5.number(),
|
|
15516
15684
|
steps: z5.array(z5.object({
|
|
15517
15685
|
name: z5.string(),
|
|
15518
15686
|
duration_ms: z5.number(),
|
|
15519
|
-
input: z5.record(z5.unknown()),
|
|
15520
|
-
output: z5.record(z5.unknown()),
|
|
15521
15687
|
skipped: z5.boolean().optional(),
|
|
15522
|
-
skip_reason: z5.string().optional()
|
|
15688
|
+
skip_reason: z5.string().optional(),
|
|
15689
|
+
summary: z5.record(z5.union([z5.number(), z5.boolean(), z5.string()]))
|
|
15523
15690
|
}))
|
|
15524
|
-
})).optional().describe("Up to 5 most recent pipeline runs with
|
|
15691
|
+
})).optional().describe("Up to 5 most recent pipeline runs with compact step summaries (full mode only)"),
|
|
15525
15692
|
fts5_ready: z5.boolean().describe("Whether the FTS5 keyword search index is ready"),
|
|
15526
15693
|
fts5_building: z5.boolean().describe("Whether the FTS5 keyword search index is currently building"),
|
|
15527
15694
|
embeddings_building: z5.boolean().describe("Whether semantic embeddings are currently building"),
|
|
@@ -15718,14 +15885,13 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15718
15885
|
try {
|
|
15719
15886
|
const evt = getRecentPipelineEvent(stateDb2);
|
|
15720
15887
|
if (evt && evt.steps && evt.steps.length > 0) {
|
|
15721
|
-
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
};
|
|
15888
|
+
const compact = compactPipelineRun(evt);
|
|
15889
|
+
if (isFull) {
|
|
15890
|
+
lastPipeline = compact;
|
|
15891
|
+
} else {
|
|
15892
|
+
const { steps: _steps, ...metadataOnly } = compact;
|
|
15893
|
+
lastPipeline = metadataOnly;
|
|
15894
|
+
}
|
|
15729
15895
|
}
|
|
15730
15896
|
} catch {
|
|
15731
15897
|
}
|
|
@@ -15733,14 +15899,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15733
15899
|
try {
|
|
15734
15900
|
const events = getRecentIndexEvents(stateDb2, 10).filter((e) => e.steps && e.steps.length > 0).slice(0, 5);
|
|
15735
15901
|
if (events.length > 0) {
|
|
15736
|
-
recentPipelines = events.map((e) => (
|
|
15737
|
-
timestamp: e.timestamp,
|
|
15738
|
-
trigger: e.trigger,
|
|
15739
|
-
duration_ms: e.duration_ms,
|
|
15740
|
-
files_changed: e.files_changed,
|
|
15741
|
-
changed_paths: e.changed_paths,
|
|
15742
|
-
steps: e.steps
|
|
15743
|
-
}));
|
|
15902
|
+
recentPipelines = events.map((e) => compactPipelineRun(e));
|
|
15744
15903
|
}
|
|
15745
15904
|
} catch {
|
|
15746
15905
|
}
|
|
@@ -15903,13 +16062,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15903
16062
|
if (stateDb2) {
|
|
15904
16063
|
try {
|
|
15905
16064
|
const events = getRecentIndexEvents(stateDb2, 10).filter((e) => e.steps && e.steps.length > 0).slice(0, 5);
|
|
15906
|
-
output.recent_runs = events.map((e) => (
|
|
15907
|
-
timestamp: e.timestamp,
|
|
15908
|
-
trigger: e.trigger,
|
|
15909
|
-
duration_ms: e.duration_ms,
|
|
15910
|
-
files_changed: e.files_changed,
|
|
15911
|
-
steps: e.steps
|
|
15912
|
-
}));
|
|
16065
|
+
output.recent_runs = events.map((e) => compactPipelineRun(e));
|
|
15913
16066
|
} catch {
|
|
15914
16067
|
}
|
|
15915
16068
|
}
|
|
@@ -17137,7 +17290,7 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
17137
17290
|
description: "Use when listing all linkable entities grouped by category. Produces the full entity index from the state database with names, aliases, hub scores, and categories. Returns an array of entity profiles. Does not search note content \u2014 only returns entity metadata from the index.",
|
|
17138
17291
|
inputSchema: {
|
|
17139
17292
|
category: z6.string().optional().describe('Filter to a specific category (e.g. "people", "technologies")'),
|
|
17140
|
-
limit: z6.coerce.number().default(
|
|
17293
|
+
limit: z6.coerce.number().default(200).describe("Maximum entities per category (default 200; pass higher for full hydration)")
|
|
17141
17294
|
}
|
|
17142
17295
|
},
|
|
17143
17296
|
async ({
|
|
@@ -17292,10 +17445,11 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17292
17445
|
description: "Use after search identifies a note you need detail on. Produces heading tree, frontmatter, tags, word count, backlink and outlink counts, and optionally full section content. Returns enriched note structure with entity metadata when available. Does not search \u2014 requires an exact path from a prior search result.",
|
|
17293
17446
|
inputSchema: {
|
|
17294
17447
|
path: z7.string().describe("Path to the note"),
|
|
17295
|
-
include_content: z7.boolean().default(
|
|
17448
|
+
include_content: z7.boolean().default(false).describe("Include the text content under each top-level section. Default false for structure only."),
|
|
17449
|
+
max_content_chars: z7.number().default(2e4).describe("Max total chars of section content to include. Sections are truncated at paragraph boundaries.")
|
|
17296
17450
|
}
|
|
17297
17451
|
},
|
|
17298
|
-
async ({ path: path40, include_content }) => {
|
|
17452
|
+
async ({ path: path40, include_content, max_content_chars }) => {
|
|
17299
17453
|
const index = getIndex();
|
|
17300
17454
|
const vaultPath2 = getVaultPath();
|
|
17301
17455
|
const result = await getNoteStructure(index, path40, vaultPath2);
|
|
@@ -17304,11 +17458,26 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17304
17458
|
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path40 }, null, 2) }]
|
|
17305
17459
|
};
|
|
17306
17460
|
}
|
|
17461
|
+
let totalChars = 0;
|
|
17462
|
+
let truncated = false;
|
|
17307
17463
|
if (include_content) {
|
|
17308
17464
|
for (const section of result.sections) {
|
|
17465
|
+
if (totalChars >= max_content_chars) {
|
|
17466
|
+
truncated = true;
|
|
17467
|
+
break;
|
|
17468
|
+
}
|
|
17309
17469
|
const sectionResult = await getSectionContent(index, path40, section.heading.text, vaultPath2, true);
|
|
17310
17470
|
if (sectionResult) {
|
|
17311
|
-
|
|
17471
|
+
let content = sectionResult.content;
|
|
17472
|
+
const remaining = max_content_chars - totalChars;
|
|
17473
|
+
if (content.length > remaining) {
|
|
17474
|
+
const sliced = content.slice(0, remaining);
|
|
17475
|
+
const lastBreak = sliced.lastIndexOf("\n\n");
|
|
17476
|
+
content = lastBreak > 0 ? sliced.slice(0, lastBreak) : sliced;
|
|
17477
|
+
truncated = true;
|
|
17478
|
+
}
|
|
17479
|
+
section.content = content;
|
|
17480
|
+
totalChars += content.length;
|
|
17312
17481
|
}
|
|
17313
17482
|
}
|
|
17314
17483
|
}
|
|
@@ -17335,6 +17504,10 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17335
17504
|
} catch {
|
|
17336
17505
|
}
|
|
17337
17506
|
}
|
|
17507
|
+
if (include_content) {
|
|
17508
|
+
enriched.truncated = truncated;
|
|
17509
|
+
enriched.returned_chars = totalChars;
|
|
17510
|
+
}
|
|
17338
17511
|
return {
|
|
17339
17512
|
content: [{ type: "text", text: JSON.stringify(enriched, null, 2) }]
|
|
17340
17513
|
};
|
|
@@ -17348,10 +17521,11 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17348
17521
|
inputSchema: {
|
|
17349
17522
|
path: z7.string().describe("Path to the note"),
|
|
17350
17523
|
heading: z7.string().describe("Heading text to find"),
|
|
17351
|
-
include_subheadings: z7.boolean().default(true).describe("Include content under subheadings")
|
|
17524
|
+
include_subheadings: z7.boolean().default(true).describe("Include content under subheadings"),
|
|
17525
|
+
max_content_chars: z7.number().default(1e4).describe("Max chars of section content. Truncated at paragraph boundaries.")
|
|
17352
17526
|
}
|
|
17353
17527
|
},
|
|
17354
|
-
async ({ path: path40, heading, include_subheadings }) => {
|
|
17528
|
+
async ({ path: path40, heading, include_subheadings, max_content_chars }) => {
|
|
17355
17529
|
const index = getIndex();
|
|
17356
17530
|
const vaultPath2 = getVaultPath();
|
|
17357
17531
|
const result = await getSectionContent(index, path40, heading, vaultPath2, include_subheadings);
|
|
@@ -17364,8 +17538,15 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17364
17538
|
}, null, 2) }]
|
|
17365
17539
|
};
|
|
17366
17540
|
}
|
|
17541
|
+
let truncated = false;
|
|
17542
|
+
if (result.content.length > max_content_chars) {
|
|
17543
|
+
const sliced = result.content.slice(0, max_content_chars);
|
|
17544
|
+
const lastBreak = sliced.lastIndexOf("\n\n");
|
|
17545
|
+
result.content = lastBreak > 0 ? sliced.slice(0, lastBreak) : sliced;
|
|
17546
|
+
truncated = true;
|
|
17547
|
+
}
|
|
17367
17548
|
return {
|
|
17368
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
17549
|
+
content: [{ type: "text", text: JSON.stringify({ ...result, truncated }, null, 2) }]
|
|
17369
17550
|
};
|
|
17370
17551
|
}
|
|
17371
17552
|
);
|
|
@@ -24150,18 +24331,27 @@ function registerBriefTools(server2, getStateDb4) {
|
|
|
24150
24331
|
if (args.max_tokens) {
|
|
24151
24332
|
let totalTokens2 = 0;
|
|
24152
24333
|
sections.sort((a, b) => a.priority - b.priority);
|
|
24334
|
+
const kept = [];
|
|
24153
24335
|
for (const section of sections) {
|
|
24154
|
-
totalTokens2
|
|
24155
|
-
|
|
24156
|
-
|
|
24157
|
-
|
|
24158
|
-
|
|
24159
|
-
|
|
24160
|
-
|
|
24161
|
-
|
|
24162
|
-
|
|
24336
|
+
if (totalTokens2 >= args.max_tokens) {
|
|
24337
|
+
break;
|
|
24338
|
+
}
|
|
24339
|
+
if (totalTokens2 + section.estimated_tokens <= args.max_tokens) {
|
|
24340
|
+
totalTokens2 += section.estimated_tokens;
|
|
24341
|
+
kept.push(section);
|
|
24342
|
+
} else if (Array.isArray(section.content)) {
|
|
24343
|
+
const remaining = args.max_tokens - totalTokens2;
|
|
24344
|
+
const itemTokens = section.estimated_tokens / Math.max(1, section.content.length);
|
|
24345
|
+
const keepCount = Math.max(1, Math.floor(remaining / itemTokens));
|
|
24346
|
+
section.content = section.content.slice(0, keepCount);
|
|
24347
|
+
section.estimated_tokens = estimateTokens2(section.content);
|
|
24348
|
+
totalTokens2 += section.estimated_tokens;
|
|
24349
|
+
kept.push(section);
|
|
24350
|
+
break;
|
|
24163
24351
|
}
|
|
24164
24352
|
}
|
|
24353
|
+
sections.length = 0;
|
|
24354
|
+
sections.push(...kept);
|
|
24165
24355
|
}
|
|
24166
24356
|
const response = {};
|
|
24167
24357
|
let totalTokens = 0;
|
|
@@ -24672,7 +24862,32 @@ function registerMetricsTools(server2, getIndex, getStateDb4) {
|
|
|
24672
24862
|
const recentEvents = getRecentIndexEvents(stateDb2, eventLimit ?? 20);
|
|
24673
24863
|
result = {
|
|
24674
24864
|
mode: "index_activity",
|
|
24675
|
-
index_activity: {
|
|
24865
|
+
index_activity: {
|
|
24866
|
+
summary,
|
|
24867
|
+
recent_events: recentEvents.map((e) => {
|
|
24868
|
+
const base = {
|
|
24869
|
+
id: e.id,
|
|
24870
|
+
timestamp: e.timestamp,
|
|
24871
|
+
trigger: e.trigger,
|
|
24872
|
+
duration_ms: e.duration_ms,
|
|
24873
|
+
success: e.success,
|
|
24874
|
+
note_count: e.note_count,
|
|
24875
|
+
files_changed: e.files_changed,
|
|
24876
|
+
error: e.error
|
|
24877
|
+
};
|
|
24878
|
+
if (e.steps) {
|
|
24879
|
+
const compact = compactPipelineRun(e);
|
|
24880
|
+
return {
|
|
24881
|
+
...base,
|
|
24882
|
+
changed_paths_total: compact.changed_paths_total,
|
|
24883
|
+
changed_paths_sample: compact.changed_paths_sample,
|
|
24884
|
+
step_count: compact.step_count,
|
|
24885
|
+
steps: compact.steps
|
|
24886
|
+
};
|
|
24887
|
+
}
|
|
24888
|
+
return base;
|
|
24889
|
+
})
|
|
24890
|
+
}
|
|
24676
24891
|
};
|
|
24677
24892
|
break;
|
|
24678
24893
|
}
|
|
@@ -25930,7 +26145,7 @@ function registerSessionHistoryTools(server2, getStateDb4) {
|
|
|
25930
26145
|
{
|
|
25931
26146
|
session_id: z36.string().optional().describe("Session ID for detail view. Omit for recent sessions list."),
|
|
25932
26147
|
include_children: z36.boolean().optional().describe("Include child sessions (default: true)"),
|
|
25933
|
-
limit: z36.number().min(1).max(500).optional().describe("Max invocations to return in detail view (default:
|
|
26148
|
+
limit: z36.number().min(1).max(500).optional().describe("Max invocations to return in detail view (default: 50)")
|
|
25934
26149
|
},
|
|
25935
26150
|
async (args) => {
|
|
25936
26151
|
const stateDb2 = getStateDb4();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
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",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@huggingface/transformers": "^3.8.1",
|
|
57
57
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
58
|
-
"@velvetmonkey/vault-core": "^2.4.
|
|
58
|
+
"@velvetmonkey/vault-core": "^2.4.3",
|
|
59
59
|
"better-sqlite3": "^12.0.0",
|
|
60
60
|
"chokidar": "^4.0.0",
|
|
61
61
|
"gray-matter": "^4.0.3",
|