@owrede/vault-memory 0.9.0 → 0.9.1
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/README.md +3 -3
- package/dist/cli.js +53 -4
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ Reads one or more Obsidian vaults, indexes them with local embeddings via Ollama
|
|
|
6
6
|
|
|
7
7
|
## Status
|
|
8
8
|
|
|
9
|
-
**v0.9.
|
|
9
|
+
**v0.9.1** — Body-hash short-circuit (migration 006): frontmatter-only edits (the common case for `update_frontmatter`, `/log-fact`, `/import-person`) no longer trigger chunk + embedding regeneration. A new `body_hash` column on `notes` lets the watcher detect "body unchanged, frontmatter changed" and keep all existing chunks/embeddings in place — saving one Ollama roundtrip per chunk per frontmatter edit. Legacy rows pre-migration self-heal on next touch.
|
|
10
10
|
|
|
11
|
-
Previous: **v0.
|
|
11
|
+
Previous: **v0.9.0** — Agent-Compatibility & Self-Orientation: OB1-compatible `search`/`fetch` tools so ChatGPT Custom Connectors, Claude.ai, and Deep-Research modes can use vault-memory as a connector; `vault_stats` and `recent_notes` for agent self-orientation on first connect.
|
|
12
12
|
|
|
13
13
|
## Architecture in one paragraph
|
|
14
14
|
|
|
@@ -214,7 +214,7 @@ fetch({id}) → { id, title, text, url, metadata }
|
|
|
214
214
|
```bash
|
|
215
215
|
npm install
|
|
216
216
|
npm run dev # MCP server on stdio with hot reload
|
|
217
|
-
npm test #
|
|
217
|
+
npm test # 324 tests across 35 files (v0.9.1)
|
|
218
218
|
npm run build
|
|
219
219
|
```
|
|
220
220
|
|
package/dist/cli.js
CHANGED
|
@@ -302,7 +302,7 @@ function runMigration005(db) {
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
|
-
var INITIAL_SCHEMA, MIGRATION_002_ALIASES, MIGRATION_003_FIX_DELETE_FKS, MIGRATION_004_VARIABLE_DIMS, MIGRATIONS;
|
|
305
|
+
var INITIAL_SCHEMA, MIGRATION_002_ALIASES, MIGRATION_003_FIX_DELETE_FKS, MIGRATION_004_VARIABLE_DIMS, MIGRATION_006_BODY_HASH, MIGRATIONS;
|
|
306
306
|
var init_schema = __esm({
|
|
307
307
|
"src/db/schema.ts"() {
|
|
308
308
|
"use strict";
|
|
@@ -310,6 +310,9 @@ var init_schema = __esm({
|
|
|
310
310
|
INITIAL_SCHEMA = `
|
|
311
311
|
-- \u2500\u2500 3.1 Raw Layer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
312
312
|
|
|
313
|
+
-- Migration 006 adds body_hash to this table (kept out of v1 schema so
|
|
314
|
+
-- the migration chain has historical accuracy and frequent DB-rebuild
|
|
315
|
+
-- tests do not trip over duplicate-column errors).
|
|
313
316
|
CREATE TABLE IF NOT EXISTS notes (
|
|
314
317
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
315
318
|
path TEXT NOT NULL UNIQUE,
|
|
@@ -493,6 +496,10 @@ INSERT INTO embeddings_1024 (chunk_id, model_id, vector)
|
|
|
493
496
|
SELECT chunk_id, model_id, vector FROM embeddings;
|
|
494
497
|
|
|
495
498
|
DROP TABLE embeddings;
|
|
499
|
+
`;
|
|
500
|
+
MIGRATION_006_BODY_HASH = `
|
|
501
|
+
ALTER TABLE notes ADD COLUMN body_hash TEXT;
|
|
502
|
+
CREATE INDEX IF NOT EXISTS idx_notes_body_hash ON notes(body_hash);
|
|
496
503
|
`;
|
|
497
504
|
MIGRATIONS = [
|
|
498
505
|
{
|
|
@@ -519,6 +526,11 @@ DROP TABLE embeddings;
|
|
|
519
526
|
version: 5,
|
|
520
527
|
description: "add partition key on model_id (two models per dim can coexist)",
|
|
521
528
|
run: runMigration005
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
version: 6,
|
|
532
|
+
description: "add body_hash for frontmatter-only-change short-circuit",
|
|
533
|
+
sql: MIGRATION_006_BODY_HASH
|
|
522
534
|
}
|
|
523
535
|
];
|
|
524
536
|
}
|
|
@@ -540,8 +552,8 @@ var init_notes = __esm({
|
|
|
540
552
|
"SELECT * FROM notes WHERE id = ?"
|
|
541
553
|
);
|
|
542
554
|
this._insert = db.prepare(`
|
|
543
|
-
INSERT INTO notes (path, content, frontmatter, title, hash, mtime, word_count, created_at, updated_at)
|
|
544
|
-
VALUES (@path, @content, @frontmatter, @title, @hash, @mtime, @word_count, @now, @now)
|
|
555
|
+
INSERT INTO notes (path, content, frontmatter, title, hash, body_hash, mtime, word_count, created_at, updated_at)
|
|
556
|
+
VALUES (@path, @content, @frontmatter, @title, @hash, @body_hash, @mtime, @word_count, @now, @now)
|
|
545
557
|
`);
|
|
546
558
|
this._update = db.prepare(`
|
|
547
559
|
UPDATE notes
|
|
@@ -549,6 +561,7 @@ var init_notes = __esm({
|
|
|
549
561
|
frontmatter = @frontmatter,
|
|
550
562
|
title = @title,
|
|
551
563
|
hash = @hash,
|
|
564
|
+
body_hash = @body_hash,
|
|
552
565
|
mtime = @mtime,
|
|
553
566
|
word_count = @word_count,
|
|
554
567
|
updated_at = @now
|
|
@@ -583,6 +596,7 @@ var init_notes = __esm({
|
|
|
583
596
|
frontmatter: input.frontmatter,
|
|
584
597
|
title: input.title,
|
|
585
598
|
hash: input.hash,
|
|
599
|
+
body_hash: input.bodyHash,
|
|
586
600
|
mtime: input.mtime,
|
|
587
601
|
word_count: input.wordCount,
|
|
588
602
|
now
|
|
@@ -595,6 +609,7 @@ var init_notes = __esm({
|
|
|
595
609
|
frontmatter: input.frontmatter,
|
|
596
610
|
title: input.title,
|
|
597
611
|
hash: input.hash,
|
|
612
|
+
body_hash: input.bodyHash,
|
|
598
613
|
mtime: input.mtime,
|
|
599
614
|
word_count: input.wordCount,
|
|
600
615
|
now
|
|
@@ -2297,6 +2312,9 @@ function canonicalJsonStringify(value) {
|
|
|
2297
2312
|
function computeNoteHash(content, frontmatter) {
|
|
2298
2313
|
return sha256(content + canonicalJsonStringify(frontmatter ?? {}));
|
|
2299
2314
|
}
|
|
2315
|
+
function computeBodyHash(content) {
|
|
2316
|
+
return sha256(content);
|
|
2317
|
+
}
|
|
2300
2318
|
var init_hash = __esm({
|
|
2301
2319
|
"src/reader/hash.ts"() {
|
|
2302
2320
|
"use strict";
|
|
@@ -2536,6 +2554,7 @@ async function parseNote(absolutePath, vaultRoot) {
|
|
|
2536
2554
|
const frontmatter = fmData !== void 0 && Object.keys(fmData).length > 0 ? fmData : null;
|
|
2537
2555
|
const title = extractTitle(content) ?? path3.basename(absolutePath, ".md");
|
|
2538
2556
|
const hash = computeNoteHash(content, frontmatter);
|
|
2557
|
+
const bodyHash = computeBodyHash(content);
|
|
2539
2558
|
const mtime = Math.floor(stat.mtimeMs);
|
|
2540
2559
|
const bodyLinks = extractWikilinks(content);
|
|
2541
2560
|
const frontmatterLinks = extractFrontmatterWikilinks(frontmatter);
|
|
@@ -2550,6 +2569,7 @@ async function parseNote(absolutePath, vaultRoot) {
|
|
|
2550
2569
|
frontmatter,
|
|
2551
2570
|
title,
|
|
2552
2571
|
hash,
|
|
2572
|
+
bodyHash,
|
|
2553
2573
|
mtime,
|
|
2554
2574
|
wikilinks,
|
|
2555
2575
|
wordCount
|
|
@@ -3038,6 +3058,7 @@ async function indexVault(vault, options) {
|
|
|
3038
3058
|
frontmatter: parsed.frontmatter ? JSON.stringify(parsed.frontmatter) : null,
|
|
3039
3059
|
title: parsed.title,
|
|
3040
3060
|
hash: parsed.hash,
|
|
3061
|
+
bodyHash: parsed.bodyHash,
|
|
3041
3062
|
mtime: parsed.mtime,
|
|
3042
3063
|
wordCount: parsed.wordCount
|
|
3043
3064
|
});
|
|
@@ -3499,6 +3520,7 @@ async function updateFrontmatter(input) {
|
|
|
3499
3520
|
frontmatter: fmJson,
|
|
3500
3521
|
title,
|
|
3501
3522
|
hash: newHash,
|
|
3523
|
+
bodyHash: computeBodyHash(content),
|
|
3502
3524
|
mtime: Math.floor(stat.mtimeMs),
|
|
3503
3525
|
wordCount
|
|
3504
3526
|
});
|
|
@@ -3582,6 +3604,31 @@ async function indexNote(options) {
|
|
|
3582
3604
|
isNew: false
|
|
3583
3605
|
};
|
|
3584
3606
|
}
|
|
3607
|
+
if (existing && existing.body_hash && existing.body_hash === parsed.bodyHash) {
|
|
3608
|
+
const upsert2 = vault.db.notes.upsertByPath({
|
|
3609
|
+
path: parsed.relativePath,
|
|
3610
|
+
content: parsed.content,
|
|
3611
|
+
frontmatter: parsed.frontmatter ? JSON.stringify(parsed.frontmatter) : null,
|
|
3612
|
+
title: parsed.title,
|
|
3613
|
+
hash: parsed.hash,
|
|
3614
|
+
bodyHash: parsed.bodyHash,
|
|
3615
|
+
mtime: parsed.mtime,
|
|
3616
|
+
wordCount: parsed.wordCount
|
|
3617
|
+
});
|
|
3618
|
+
vault.db.aliases.setForNote(
|
|
3619
|
+
upsert2.id,
|
|
3620
|
+
extractAliases(parsed.frontmatter)
|
|
3621
|
+
);
|
|
3622
|
+
vault.db.wikilinks.deleteByNote(upsert2.id);
|
|
3623
|
+
insertWikilinks2(vault, upsert2.id, parsed.wikilinks);
|
|
3624
|
+
return {
|
|
3625
|
+
status: "indexed",
|
|
3626
|
+
notePath: parsed.relativePath,
|
|
3627
|
+
noteId: upsert2.id,
|
|
3628
|
+
chunksCreated: 0,
|
|
3629
|
+
isNew: false
|
|
3630
|
+
};
|
|
3631
|
+
}
|
|
3585
3632
|
const activeModel = vault.db.models.getActive();
|
|
3586
3633
|
if (!activeModel) {
|
|
3587
3634
|
throw new Error(
|
|
@@ -3599,6 +3646,7 @@ async function indexNote(options) {
|
|
|
3599
3646
|
frontmatter: parsed.frontmatter ? JSON.stringify(parsed.frontmatter) : null,
|
|
3600
3647
|
title: parsed.title,
|
|
3601
3648
|
hash: parsed.hash,
|
|
3649
|
+
bodyHash: parsed.bodyHash,
|
|
3602
3650
|
mtime: parsed.mtime,
|
|
3603
3651
|
wordCount: parsed.wordCount
|
|
3604
3652
|
});
|
|
@@ -4133,6 +4181,7 @@ async function writeNote(input) {
|
|
|
4133
4181
|
frontmatter: written.frontmatter ? JSON.stringify(written.frontmatter) : null,
|
|
4134
4182
|
title,
|
|
4135
4183
|
hash: written.hash,
|
|
4184
|
+
bodyHash: computeBodyHash(written.content),
|
|
4136
4185
|
mtime: Math.floor(stat.mtimeMs),
|
|
4137
4186
|
wordCount: countWords3(written.content)
|
|
4138
4187
|
});
|
|
@@ -5702,7 +5751,7 @@ var init_server = __esm({
|
|
|
5702
5751
|
init_audit3();
|
|
5703
5752
|
init_watcher2();
|
|
5704
5753
|
init_indexer2();
|
|
5705
|
-
VERSION = "0.9.
|
|
5754
|
+
VERSION = "0.9.1";
|
|
5706
5755
|
ReadNoteArgs = z3.object({
|
|
5707
5756
|
vault: z3.string(),
|
|
5708
5757
|
path: z3.string()
|