daftari 1.7.0 → 1.8.0

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.
@@ -4,16 +4,105 @@
4
4
  // from scratch — there is no incremental update path to drift out of sync.
5
5
  // Called on server start and by the vault_reindex tool / --reindex CLI flag.
6
6
  //
7
- // Embedding is best-effort: every chunk across the whole vault is embedded,
8
- // in fixed-size sub-batches so peak memory stays flat as the vault grows. If
9
- // the model is unavailable the documents (and their BM25 tokens) still index;
10
- // only the vector column is left NULL and vectorEnabled is false.
7
+ // Embedding is content-addressed: each chunk's text is hashed (sha256), and
8
+ // the embeddings table is keyed by (content_hash, model). A reindex hashes
9
+ // every chunk, asks the cache which (hash, model) pairs already exist, and
10
+ // only embeds the misses. Edits to one file re-embed that file's changed
11
+ // chunks; renames, moved paragraphs, and identical text in two files all
12
+ // hit the cache and embed zero. Orphaned cache rows (chunks that no longer
13
+ // reference them) are reaped at the end of the pass.
14
+ //
15
+ // If the model is unavailable the documents (and their BM25 tokens) still
16
+ // index; chunks are written with their content_hash but no embedding row is
17
+ // inserted, so the join in vector.ts comes back null and search degrades to
18
+ // lexical-only.
19
+ import { stat } from "node:fs/promises";
11
20
  import { parseDocument } from "../frontmatter/parser.js";
12
21
  import { err, ok } from "../frontmatter/types.js";
13
- import { clearIndex, deleteDocument, documentCount, insertChunk, insertDocument, openIndexDb, setMeta, } from "../storage/index-db.js";
22
+ import { clearIndex, deleteDocument, documentCount, existingEmbeddingHashes, gcOrphanedEmbeddings, getMeta, insertChunkRow, insertDocument, insertEmbedding, openIndexDb, setMeta, } from "../storage/index-db.js";
14
23
  import { listFiles, readFile, resolveVaultPath } from "../storage/local.js";
24
+ import { sha256Hex } from "../utils/hash.js";
15
25
  import { tokenize } from "./bm25.js";
16
- import { chunkText, EMBEDDING_DIM, embed } from "./vector.js";
26
+ import { chunkText, EMBEDDING_DIM, EMBEDDING_MODEL, embed } from "./vector.js";
27
+ // Manifest key in the meta table: JSON object mapping vault-relative path to
28
+ // mtime in ms. Written at the end of a successful reindex and updated by
29
+ // indexDocument after each incremental write, so a startup freshness check
30
+ // can decide whether the persisted index already reflects the files on disk.
31
+ const MANIFEST_META_KEY = "vault_manifest";
32
+ // Walks the vault and produces a fresh path→mtimeMs map. Returns null if any
33
+ // file cannot be stat'd or the file listing fails — caller treats null as
34
+ // "can't prove freshness, fall back to a full reindex."
35
+ async function buildManifest(vaultRoot) {
36
+ const list = await listFiles(vaultRoot);
37
+ if (!list.ok)
38
+ return null;
39
+ const manifest = {};
40
+ for (const relPath of list.value) {
41
+ const resolved = resolveVaultPath(vaultRoot, relPath);
42
+ if (!resolved.ok)
43
+ return null;
44
+ try {
45
+ const st = await stat(resolved.value);
46
+ manifest[relPath] = st.mtimeMs;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ return manifest;
53
+ }
54
+ function readManifest(db) {
55
+ const raw = getMeta(db, MANIFEST_META_KEY);
56
+ if (!raw)
57
+ return null;
58
+ try {
59
+ const parsed = JSON.parse(raw);
60
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
61
+ return null;
62
+ return parsed;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ function writeManifest(db, manifest) {
69
+ setMeta(db, MANIFEST_META_KEY, JSON.stringify(manifest));
70
+ }
71
+ function manifestsMatch(a, b) {
72
+ const aKeys = Object.keys(a);
73
+ const bKeys = Object.keys(b);
74
+ if (aKeys.length !== bKeys.length)
75
+ return false;
76
+ for (const key of aKeys) {
77
+ if (a[key] !== b[key])
78
+ return false;
79
+ }
80
+ return true;
81
+ }
82
+ // Returns true when the persisted index already reflects every markdown file
83
+ // on disk: doc count is non-zero, a manifest exists, and every file's mtime
84
+ // matches the stored value. Used by `main()` to skip a 20+ minute re-embed
85
+ // pass on every restart of a vault that hasn't changed.
86
+ export async function isIndexFresh(vaultRoot) {
87
+ const dbResult = openIndexDb(vaultRoot);
88
+ if (!dbResult.ok)
89
+ return false;
90
+ const db = dbResult.value;
91
+ try {
92
+ if (documentCount(db) === 0)
93
+ return false;
94
+ const stored = readManifest(db);
95
+ if (!stored)
96
+ return false;
97
+ const current = await buildManifest(vaultRoot);
98
+ if (!current)
99
+ return false;
100
+ return manifestsMatch(stored, current);
101
+ }
102
+ finally {
103
+ db.close();
104
+ }
105
+ }
17
106
  // Reads and parses a single markdown file into the shape the index needs.
18
107
  // Returns null when the file should be skipped (unreadable, or malformed YAML
19
108
  // frontmatter) so a reindex never aborts on one bad file.
@@ -32,6 +121,8 @@ async function stageOne(vaultRoot, relPath) {
32
121
  // BM25 indexes title, tags, and body together so a title- or tag-only
33
122
  // match still ranks.
34
123
  const tokens = tokenize(`${fm.title} ${fm.tags.join(" ")} ${body}`);
124
+ const chunks = chunkText(body);
125
+ const hashes = chunks.map((t) => sha256Hex(t));
35
126
  return {
36
127
  doc: {
37
128
  path: relPath,
@@ -48,7 +139,8 @@ async function stageOne(vaultRoot, relPath) {
48
139
  created: fm.created,
49
140
  supersededBy: fm.superseded_by,
50
141
  },
51
- chunks: chunkText(body),
142
+ chunks,
143
+ hashes,
52
144
  };
53
145
  }
54
146
  // Reads and parses every markdown file into the shape the index needs. A file
@@ -69,21 +161,23 @@ async function stageDocuments(vaultRoot) {
69
161
  }
70
162
  return ok({ staged, skipped });
71
163
  }
72
- function writeIndex(db, staged, embeddings) {
164
+ // Inserts the chunk rows in a single transaction. Embeddings are persisted by
165
+ // the caller (with a model identifier) so the chunk write stays oblivious to
166
+ // which model produced the vectors.
167
+ function writeChunkRows(db, staged) {
73
168
  let chunkCount = 0;
74
- let cursor = 0;
75
169
  const write = db.transaction(() => {
76
170
  clearIndex(db);
77
- for (const { doc, chunks } of staged) {
171
+ for (const { doc, chunks, hashes } of staged) {
78
172
  insertDocument(db, doc);
79
173
  chunks.forEach((text, chunkIndex) => {
80
- insertChunk(db, {
174
+ const row = {
81
175
  path: doc.path,
82
176
  chunkIndex,
83
177
  text,
84
- embedding: embeddings[cursor] ?? null,
85
- });
86
- cursor += 1;
178
+ contentHash: hashes[chunkIndex] ?? "",
179
+ };
180
+ insertChunkRow(db, row);
87
181
  chunkCount += 1;
88
182
  });
89
183
  }
@@ -96,31 +190,94 @@ export async function reindexVault(vaultRoot, opts = {}) {
96
190
  if (!staging.ok)
97
191
  return staging;
98
192
  const { staged, skipped } = staging.value;
99
- // One flat list of every chunk's text; embed() processes it in sub-batches.
100
- const allChunkTexts = [];
101
- for (const s of staged)
102
- allChunkTexts.push(...s.chunks);
103
- const embedResult = await embed(allChunkTexts, opts.onProgress);
104
- const vectorEnabled = embedResult.ok;
105
- const embeddings = embedResult.ok
106
- ? embedResult.value
107
- : allChunkTexts.map(() => null);
193
+ // Open the index first so we can ask the embeddings cache which (hash,
194
+ // model) pairs already exist before deciding what to embed.
108
195
  const dbResult = openIndexDb(vaultRoot);
109
196
  if (!dbResult.ok)
110
197
  return dbResult;
111
198
  const db = dbResult.value;
112
199
  const indexedAt = new Date().toISOString();
113
200
  try {
114
- const chunkCount = writeIndex(db, staged, embeddings);
201
+ // Flatten every chunk's text + hash so we can dedupe and query the cache
202
+ // in one shot. A single hash may appear in multiple files (or repeatedly
203
+ // in one file) — embed it once.
204
+ const allHashes = [];
205
+ const allTexts = [];
206
+ for (const s of staged) {
207
+ for (let i = 0; i < s.chunks.length; i++) {
208
+ const h = s.hashes[i] ?? "";
209
+ const t = s.chunks[i] ?? "";
210
+ allHashes.push(h);
211
+ allTexts.push(t);
212
+ }
213
+ }
214
+ const cached = existingEmbeddingHashes(db, EMBEDDING_MODEL, allHashes);
215
+ // Build the deduped miss list. Each unique missing hash gets embedded
216
+ // exactly once; identical chunk text in multiple places shares the row.
217
+ const missTextByHash = new Map();
218
+ for (let i = 0; i < allHashes.length; i++) {
219
+ const h = allHashes[i] ?? "";
220
+ if (cached.has(h))
221
+ continue;
222
+ if (missTextByHash.has(h))
223
+ continue;
224
+ missTextByHash.set(h, allTexts[i] ?? "");
225
+ }
226
+ const missHashes = [...missTextByHash.keys()];
227
+ const missTexts = missHashes.map((h) => missTextByHash.get(h) ?? "");
228
+ const totalChunks = allHashes.length;
229
+ const cacheHits = totalChunks - allHashes.filter((h) => !cached.has(h)).length;
230
+ let vectorEnabled = true;
231
+ if (missTexts.length > 0) {
232
+ const embedResult = await embed(missTexts, opts.onProgress);
233
+ if (embedResult.ok) {
234
+ const writeEmbeds = db.transaction(() => {
235
+ for (let i = 0; i < missHashes.length; i++) {
236
+ const h = missHashes[i] ?? "";
237
+ const vec = embedResult.value[i];
238
+ if (!vec)
239
+ continue;
240
+ insertEmbedding(db, h, EMBEDDING_MODEL, vec, indexedAt);
241
+ }
242
+ });
243
+ writeEmbeds();
244
+ }
245
+ else {
246
+ // Model unavailable. We still want documents + chunk rows so BM25
247
+ // works; vector ranking simply degrades to nothing for this reindex.
248
+ vectorEnabled = false;
249
+ }
250
+ }
251
+ else if (totalChunks === 0) {
252
+ // No chunks at all (empty vault). Treat as vectorEnabled=true by
253
+ // convention — there is nothing to embed but also nothing failed.
254
+ vectorEnabled = true;
255
+ }
256
+ else {
257
+ // Everything was cached. Vector ranking has data already, so the
258
+ // reindex did not need to load the model — still vector-enabled.
259
+ vectorEnabled = true;
260
+ }
261
+ const chunkCount = writeChunkRows(db, staged);
262
+ const orphansRemoved = gcOrphanedEmbeddings(db);
115
263
  setMeta(db, "indexed_at", indexedAt);
116
264
  setMeta(db, "vector_enabled", String(vectorEnabled));
117
265
  setMeta(db, "embedding_dim", String(EMBEDDING_DIM));
266
+ setMeta(db, "embedding_model", EMBEDDING_MODEL);
267
+ // Persist a freshness manifest so the next startup can skip this whole
268
+ // pass when nothing on disk has changed.
269
+ const manifest = await buildManifest(vaultRoot);
270
+ if (manifest)
271
+ writeManifest(db, manifest);
118
272
  return ok({
119
273
  documentCount: staged.length,
120
274
  chunkCount,
121
275
  vectorEnabled,
122
276
  skipped,
123
277
  indexedAt,
278
+ embeddedCount: missHashes.length,
279
+ cacheHits,
280
+ orphansRemoved,
124
281
  });
125
282
  }
126
283
  catch (e) {
@@ -135,8 +292,9 @@ export async function reindexVault(vaultRoot, opts = {}) {
135
292
  //
136
293
  // If the index has never been built it falls back to a full reindex, so the
137
294
  // first write to a fresh vault still produces a complete index rather than a
138
- // one-document one. Otherwise it re-stages just `relPath`, evicts its stale
139
- // rows, and re-inserts embedding only that document's chunks.
295
+ // one-document one. Otherwise it re-stages just `relPath`, hashes its chunks,
296
+ // embeds only those whose (hash, model) pair is not already in the embeddings
297
+ // cache, evicts the document's stale chunk rows, and re-inserts.
140
298
  export async function indexDocument(vaultRoot, relPath) {
141
299
  const dbCheck = openIndexDb(vaultRoot);
142
300
  if (!dbCheck.ok)
@@ -156,30 +314,76 @@ export async function indexDocument(vaultRoot, relPath) {
156
314
  if (!staged) {
157
315
  return err(new Error(`cannot index document: ${relPath}`));
158
316
  }
159
- const { doc, chunks } = staged;
160
- const embedResult = await embed(chunks);
161
- const vectorEnabled = embedResult.ok;
162
- const embeddings = embedResult.ok
163
- ? embedResult.value
164
- : chunks.map(() => null);
317
+ const { doc, chunks, hashes } = staged;
165
318
  const dbResult = openIndexDb(vaultRoot);
166
319
  if (!dbResult.ok)
167
320
  return dbResult;
168
321
  const db = dbResult.value;
322
+ const createdAt = new Date().toISOString();
169
323
  try {
324
+ const cached = existingEmbeddingHashes(db, EMBEDDING_MODEL, hashes);
325
+ const missTextByHash = new Map();
326
+ for (let i = 0; i < hashes.length; i++) {
327
+ const h = hashes[i] ?? "";
328
+ if (cached.has(h))
329
+ continue;
330
+ if (missTextByHash.has(h))
331
+ continue;
332
+ missTextByHash.set(h, chunks[i] ?? "");
333
+ }
334
+ const missHashes = [...missTextByHash.keys()];
335
+ const missTexts = missHashes.map((h) => missTextByHash.get(h) ?? "");
336
+ let vectorEnabled = true;
337
+ if (missTexts.length > 0) {
338
+ const embedResult = await embed(missTexts);
339
+ if (embedResult.ok) {
340
+ const writeEmbeds = db.transaction(() => {
341
+ for (let i = 0; i < missHashes.length; i++) {
342
+ const h = missHashes[i] ?? "";
343
+ const vec = embedResult.value[i];
344
+ if (!vec)
345
+ continue;
346
+ insertEmbedding(db, h, EMBEDDING_MODEL, vec, createdAt);
347
+ }
348
+ });
349
+ writeEmbeds();
350
+ }
351
+ else {
352
+ vectorEnabled = false;
353
+ }
354
+ }
170
355
  const write = db.transaction(() => {
171
356
  deleteDocument(db, doc.path);
172
357
  insertDocument(db, doc);
173
358
  chunks.forEach((text, chunkIndex) => {
174
- insertChunk(db, {
359
+ insertChunkRow(db, {
175
360
  path: doc.path,
176
361
  chunkIndex,
177
362
  text,
178
- embedding: embeddings[chunkIndex] ?? null,
363
+ contentHash: hashes[chunkIndex] ?? "",
179
364
  });
180
365
  });
181
366
  });
182
367
  write();
368
+ // Keep the freshness manifest in sync with this single write so the next
369
+ // startup still sees a current index. Stat the file we just indexed and
370
+ // patch only its entry — re-statting the whole vault would defeat the
371
+ // point of the incremental path.
372
+ const stored = readManifest(db);
373
+ if (stored) {
374
+ const resolved = resolveVaultPath(vaultRoot, relPath);
375
+ if (resolved.ok) {
376
+ try {
377
+ const st = await stat(resolved.value);
378
+ stored[relPath] = st.mtimeMs;
379
+ writeManifest(db, stored);
380
+ }
381
+ catch {
382
+ // ignore — manifest just stays stale for this entry; worst case is
383
+ // one extra reindex on next startup.
384
+ }
385
+ }
386
+ }
183
387
  return ok({ chunkCount: chunks.length, vectorEnabled });
184
388
  }
185
389
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"reindex.js","sourceRoot":"","sources":["../../src/search/reindex.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reindex.js","sourceRoot":"","sources":["../../src/search/reindex.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,qDAAqD;AACrD,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,4EAA4E;AAC5E,gBAAgB;AAEhB,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAe,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAEL,UAAU,EACV,cAAc,EACd,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,OAAO,EAGP,cAAc,EACd,cAAc,EACd,eAAe,EACf,WAAW,EACX,OAAO,GACR,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE/E,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,6EAA6E;AAC7E,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAE3C,6EAA6E;AAC7E,0EAA0E;AAC1E,wDAAwD;AACxD,KAAK,UAAU,aAAa,CAAC,SAAiB;IAC5C,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACtC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,EAAW;IAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QAChF,OAAO,MAAgC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EAAW,EAAE,QAAgC;IAClE,OAAO,CAAC,EAAE,EAAE,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CAAC,CAAyB,EAAE,CAAyB;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAiB;IAClD,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC1B,IAAI,CAAC;QACH,IAAI,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,OAAO,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AA+BD,0EAA0E;AAC1E,8EAA8E;AAC9E,0DAA0D;AAC1D,KAAK,UAAU,QAAQ,CAAC,SAAiB,EAAE,OAAe;IACxD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IAClC,sEAAsE;IACtE,qBAAqB;IACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/C,OAAO;QACL,GAAG,EAAE;YACH,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,UAAU,EAAE,EAAE,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1D,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,UAAU,EAAE,EAAE,CAAC,UAAU;YACzB,OAAO,EAAE,EAAE,CAAC,OAAO;YACnB,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,OAAO,EAAE,IAAI;YACb,MAAM;YACN,OAAO,EAAE,EAAE,CAAC,QAAQ;YACpB,OAAO,EAAE,EAAE,CAAC,OAAO;YACnB,YAAY,EAAE,EAAE,CAAC,aAAa;SAC/B;QACD,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,uEAAuE;AACvE,8BAA8B;AAC9B,KAAK,UAAU,cAAc,CAC3B,SAAiB;IAEjB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;YACrB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,oCAAoC;AACpC,SAAS,cAAc,CAAC,EAAW,EAAE,MAAwB;IAC3D,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QAChC,UAAU,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;YAC7C,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;gBAClC,MAAM,GAAG,GAAkB;oBACzB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,UAAU;oBACV,IAAI;oBACJ,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE;iBACtC,CAAC;gBACF,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBACxB,UAAU,IAAI,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IACH,KAAK,EAAE,CAAC;IACR,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,OAAuB,EAAE;IAEzB,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,OAAO,CAAC;IAChC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;IAE1C,uEAAuE;IACvE,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;IAE1B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,yEAAyE;QACzE,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5B,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;QAEvE,sEAAsE;QACtE,wEAAwE;QACxE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;QACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC5B,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YACpC,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,UAAU,GAAG,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAErE,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC;QACrC,MAAM,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAE/E,IAAI,aAAa,GAAG,IAAI,CAAC;QACzB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5D,IAAI,WAAW,CAAC,EAAE,EAAE,CAAC;gBACnB,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;oBACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC3C,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBAC9B,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBACjC,IAAI,CAAC,GAAG;4BAAE,SAAS;wBACnB,eAAe,CAAC,EAAE,EAAE,CAAC,EAAE,eAAe,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,WAAW,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,kEAAkE;gBAClE,qEAAqE;gBACrE,aAAa,GAAG,KAAK,CAAC;YACxB,CAAC;QACH,CAAC;aAAM,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YAC7B,iEAAiE;YACjE,kEAAkE;YAClE,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,iEAAiE;YACjE,iEAAiE;YACjE,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QAED,MAAM,UAAU,GAAG,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,cAAc,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;QAEhD,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACrC,OAAO,CAAC,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;QACrD,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,EAAE,EAAE,iBAAiB,EAAE,eAAe,CAAC,CAAC;QAChD,uEAAuE;QACvE,yCAAyC;QACzC,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,QAAQ;YAAE,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC;YACR,aAAa,EAAE,MAAM,CAAC,MAAM;YAC5B,UAAU;YACV,aAAa;YACb,OAAO;YACP,SAAS;YACT,aAAa,EAAE,UAAU,CAAC,MAAM;YAChC,SAAS;YACT,cAAc;SACf,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAOD,uEAAuE;AACvE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,SAAiB,EACjB,OAAe;IAEf,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,OAAO,CAAC;IAChC,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAEtB,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC1B,OAAO,EAAE,CAAC;YACR,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU;YACjC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa;SACxC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAEvC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAC;IAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;QACpE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;QACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC5B,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YACpC,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,MAAM,UAAU,GAAG,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAErE,IAAI,aAAa,GAAG,IAAI,CAAC;QACzB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;YAC3C,IAAI,WAAW,CAAC,EAAE,EAAE,CAAC;gBACnB,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;oBACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC3C,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;wBAC9B,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBACjC,IAAI,CAAC,GAAG;4BAAE,SAAS;wBACnB,eAAe,CAAC,EAAE,EAAE,CAAC,EAAE,eAAe,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC,CAAC,CAAC;gBACH,WAAW,EAAE,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,aAAa,GAAG,KAAK,CAAC;YACxB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YAChC,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACxB,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;gBAClC,cAAc,CAAC,EAAE,EAAE;oBACjB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,UAAU;oBACV,IAAI;oBACJ,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE;iBACtC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,KAAK,EAAE,CAAC;QACR,yEAAyE;QACzE,wEAAwE;QACxE,sEAAsE;QACtE,iCAAiC;QACjC,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,IAAI,CAAC;oBACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBACtC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;oBAC7B,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,mEAAmE;oBACnE,qCAAqC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAC1D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -20,6 +20,7 @@ export interface IndexedChunk {
20
20
  path: string;
21
21
  chunkIndex: number;
22
22
  text: string;
23
+ contentHash: string;
23
24
  embedding: Float32Array | null;
24
25
  }
25
26
  export declare function indexDbPath(vaultRoot: string): string;
@@ -29,12 +30,22 @@ export declare function deleteDocument(db: IndexDb, path: string): void;
29
30
  export declare function embeddingToBlob(vec: Float32Array): Buffer;
30
31
  export declare function blobToEmbedding(blob: Buffer): Float32Array;
31
32
  export declare function insertDocument(db: IndexDb, doc: IndexedDocument): void;
32
- export declare function insertChunk(db: IndexDb, chunk: IndexedChunk): void;
33
+ export interface ChunkRowInput {
34
+ path: string;
35
+ chunkIndex: number;
36
+ text: string;
37
+ contentHash: string;
38
+ }
39
+ export declare function insertChunkRow(db: IndexDb, chunk: ChunkRowInput): void;
40
+ export declare function existingEmbeddingHashes(db: IndexDb, model: string, hashes: string[]): Set<string>;
41
+ export declare function insertEmbedding(db: IndexDb, contentHash: string, model: string, embedding: Float32Array, createdAt: string): void;
42
+ export declare function gcOrphanedEmbeddings(db: IndexDb): number;
33
43
  export declare function setMeta(db: IndexDb, key: string, value: string): void;
34
44
  export declare function getAllDocuments(db: IndexDb): IndexedDocument[];
35
45
  export declare function getDocument(db: IndexDb, path: string): IndexedDocument | null;
36
- export declare function getAllChunks(db: IndexDb): IndexedChunk[];
37
- export declare function getChunksForPath(db: IndexDb, path: string): IndexedChunk[];
46
+ export declare function getAllChunks(db: IndexDb, model: string): IndexedChunk[];
47
+ export declare function getChunksForPath(db: IndexDb, path: string, model: string): IndexedChunk[];
38
48
  export declare function getMeta(db: IndexDb, key: string): string | null;
39
49
  export declare function documentCount(db: IndexDb): number;
50
+ export declare function embeddingCount(db: IndexDb): number;
40
51
  //# sourceMappingURL=index-db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-db.d.ts","sourceRoot":"","sources":["../../src/storage/index-db.ts"],"names":[],"mappings":"AAgBA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC;AAIxC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;AAID,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErD;AA+BD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAqBrE;AAID,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAE5C;AAKD,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAG9D;AAID,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAEzD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAM1D;AAID,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,GAAG,IAAI,CAqBtE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAUlE;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAErE;AAsCD,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,eAAe,EAAE,CAG9D;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAK7E;AAkBD,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,YAAY,EAAE,CAGxD;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAK1E;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,MAAM,CAGjD"}
1
+ {"version":3,"file":"index-db.d.ts","sourceRoot":"","sources":["../../src/storage/index-db.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,18 @@
2
2
  //
3
3
  // The index is a derived cache: it holds nothing the markdown files don't
4
4
  // already hold, so .daftari/index.db can be deleted and rebuilt at any time
5
- // (see search/reindex.ts). Two tables carry the search payload:
5
+ // (see search/reindex.ts). Three tables carry the search payload:
6
6
  //
7
- // documents — one row per markdown file: frontmatter fields, the full body,
8
- // and the BM25 token list (JSON).
9
- // chunks — one row per embedded text chunk: the chunk text and its vector
10
- // embedding (Float32 BLOB, or NULL when embedding was skipped).
7
+ // documents — one row per markdown file: frontmatter fields, the full body,
8
+ // and the BM25 token list (JSON).
9
+ // chunks — one row per embedded text chunk: the chunk text and a
10
+ // content_hash (sha256 of the chunk text) that joins to the
11
+ // embeddings table for the current model.
12
+ // embeddings — one row per (content_hash, model) pair. Content-addressed,
13
+ // so identical chunk text shares one row across files and
14
+ // across reindexes. A model migration can keep the same
15
+ // content_hash present under two model values at once, which
16
+ // is why the primary key is composite.
11
17
  //
12
18
  // A small meta table records index-wide facts (embedding dimension, whether
13
19
  // vectors were built, when the index was last rebuilt).
@@ -15,7 +21,7 @@ import { mkdirSync } from "node:fs";
15
21
  import { join } from "node:path";
16
22
  import Database from "better-sqlite3";
17
23
  import { err, ok } from "../frontmatter/types.js";
18
- const SCHEMA_VERSION = "2";
24
+ const SCHEMA_VERSION = "3";
19
25
  // The .daftari control directory is excluded from vault listings, so the index
20
26
  // file lives there without ever being mistaken for vault content.
21
27
  export function indexDbPath(vaultRoot) {
@@ -38,12 +44,20 @@ CREATE TABLE IF NOT EXISTS documents (
38
44
  superseded_by TEXT
39
45
  );
40
46
  CREATE TABLE IF NOT EXISTS chunks (
41
- path TEXT NOT NULL,
42
- chunk_index INTEGER NOT NULL,
43
- text TEXT NOT NULL,
44
- embedding BLOB,
47
+ path TEXT NOT NULL,
48
+ chunk_index INTEGER NOT NULL,
49
+ text TEXT NOT NULL,
50
+ content_hash TEXT NOT NULL,
45
51
  PRIMARY KEY (path, chunk_index)
46
52
  );
53
+ CREATE INDEX IF NOT EXISTS idx_chunks_content_hash ON chunks(content_hash);
54
+ CREATE TABLE IF NOT EXISTS embeddings (
55
+ content_hash TEXT NOT NULL,
56
+ model TEXT NOT NULL,
57
+ embedding BLOB NOT NULL,
58
+ created_at TEXT NOT NULL,
59
+ PRIMARY KEY (content_hash, model)
60
+ );
47
61
  CREATE TABLE IF NOT EXISTS meta (
48
62
  key TEXT PRIMARY KEY,
49
63
  value TEXT NOT NULL
@@ -61,7 +75,13 @@ export function openIndexDb(vaultRoot) {
61
75
  );`);
62
76
  const stored = getMeta(db, "schema_version");
63
77
  if (stored !== SCHEMA_VERSION) {
64
- db.exec("DROP TABLE IF EXISTS documents; DROP TABLE IF EXISTS chunks;");
78
+ // Schema bump means a clean rebuild: every derived table is dropped and
79
+ // the freshness manifest is cleared so the next reindex repopulates
80
+ // everything. Trying to ALTER across this change would race the new
81
+ // composite-PK embeddings table; the markdown files are the source of
82
+ // truth and the index is cheap to regenerate.
83
+ db.exec("DROP TABLE IF EXISTS documents; DROP TABLE IF EXISTS chunks; DROP TABLE IF EXISTS embeddings;");
84
+ db.prepare("DELETE FROM meta WHERE key = ?").run("vault_manifest");
65
85
  }
66
86
  db.exec(SCHEMA);
67
87
  setMeta(db, "schema_version", SCHEMA_VERSION);
@@ -73,7 +93,9 @@ export function openIndexDb(vaultRoot) {
73
93
  }
74
94
  }
75
95
  // Drops every indexed row. Called at the start of a rebuild so a reindex never
76
- // leaves rows for files that have since been deleted.
96
+ // leaves rows for files that have since been deleted. Embeddings are NOT
97
+ // cleared here — they are content-addressed and a subsequent gc pass deletes
98
+ // only the orphaned ones, preserving the cache across reindexes.
77
99
  export function clearIndex(db) {
78
100
  db.exec("DELETE FROM documents; DELETE FROM chunks;");
79
101
  }
@@ -102,9 +124,44 @@ export function insertDocument(db, doc) {
102
124
  ttl_days, created, superseded_by)
103
125
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(doc.path, doc.title, doc.collection, doc.domain, doc.status, doc.confidence, doc.updated, JSON.stringify(doc.tags), doc.content, JSON.stringify(doc.tokens), doc.ttlDays, doc.created, doc.supersededBy);
104
126
  }
105
- export function insertChunk(db, chunk) {
106
- db.prepare(`INSERT OR REPLACE INTO chunks (path, chunk_index, text, embedding)
107
- VALUES (?, ?, ?, ?)`).run(chunk.path, chunk.chunkIndex, chunk.text, chunk.embedding ? embeddingToBlob(chunk.embedding) : null);
127
+ export function insertChunkRow(db, chunk) {
128
+ db.prepare(`INSERT OR REPLACE INTO chunks (path, chunk_index, text, content_hash)
129
+ VALUES (?, ?, ?, ?)`).run(chunk.path, chunk.chunkIndex, chunk.text, chunk.contentHash);
130
+ }
131
+ // Returns the set of content_hash values that already have a row for `model`
132
+ // in the embeddings cache. Used by the reindex pass to skip re-embedding any
133
+ // chunk whose text hash is already known.
134
+ export function existingEmbeddingHashes(db, model, hashes) {
135
+ if (hashes.length === 0)
136
+ return new Set();
137
+ // SQLite has a finite SQL variable limit (default 999), so chunk the IN()
138
+ // list to stay well under it.
139
+ const found = new Set();
140
+ const BATCH = 500;
141
+ for (let start = 0; start < hashes.length; start += BATCH) {
142
+ const slice = hashes.slice(start, start + BATCH);
143
+ const placeholders = slice.map(() => "?").join(",");
144
+ const rows = db
145
+ .prepare(`SELECT content_hash FROM embeddings WHERE model = ? AND content_hash IN (${placeholders})`)
146
+ .all(model, ...slice);
147
+ for (const r of rows)
148
+ found.add(r.content_hash);
149
+ }
150
+ return found;
151
+ }
152
+ export function insertEmbedding(db, contentHash, model, embedding, createdAt) {
153
+ db.prepare(`INSERT OR REPLACE INTO embeddings (content_hash, model, embedding, created_at)
154
+ VALUES (?, ?, ?, ?)`).run(contentHash, model, embeddingToBlob(embedding), createdAt);
155
+ }
156
+ // Deletes every embeddings row whose content_hash is referenced by no chunks
157
+ // row. Called at the end of a reindex to reap entries for chunks that no
158
+ // longer exist anywhere in the vault. Returns the number of rows deleted.
159
+ export function gcOrphanedEmbeddings(db) {
160
+ const info = db
161
+ .prepare(`DELETE FROM embeddings
162
+ WHERE content_hash NOT IN (SELECT content_hash FROM chunks)`)
163
+ .run();
164
+ return info.changes;
108
165
  }
109
166
  export function setMeta(db, key, value) {
110
167
  db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(key, value);
@@ -139,17 +196,34 @@ function rowToChunk(row) {
139
196
  path: row.path,
140
197
  chunkIndex: row.chunk_index,
141
198
  text: row.text,
199
+ contentHash: row.content_hash,
142
200
  embedding: row.embedding ? blobToEmbedding(row.embedding) : null,
143
201
  };
144
202
  }
145
- export function getAllChunks(db) {
146
- const rows = db.prepare("SELECT * FROM chunks ORDER BY path, chunk_index").all();
203
+ // Reads every chunk LEFT JOINed against the embeddings cache, filtered to
204
+ // `model`. A chunk whose content_hash has no embeddings row for this model
205
+ // (e.g. the model was unavailable during reindex) comes back with embedding =
206
+ // null and the vector ranker simply skips it, matching the old NULL-blob
207
+ // behaviour.
208
+ export function getAllChunks(db, model) {
209
+ const rows = db
210
+ .prepare(`SELECT c.path, c.chunk_index, c.text, c.content_hash, e.embedding
211
+ FROM chunks c
212
+ LEFT JOIN embeddings e
213
+ ON e.content_hash = c.content_hash AND e.model = ?
214
+ ORDER BY c.path, c.chunk_index`)
215
+ .all(model);
147
216
  return rows.map(rowToChunk);
148
217
  }
149
- export function getChunksForPath(db, path) {
218
+ export function getChunksForPath(db, path, model) {
150
219
  const rows = db
151
- .prepare("SELECT * FROM chunks WHERE path = ? ORDER BY chunk_index")
152
- .all(path);
220
+ .prepare(`SELECT c.path, c.chunk_index, c.text, c.content_hash, e.embedding
221
+ FROM chunks c
222
+ LEFT JOIN embeddings e
223
+ ON e.content_hash = c.content_hash AND e.model = ?
224
+ WHERE c.path = ?
225
+ ORDER BY c.chunk_index`)
226
+ .all(model, path);
153
227
  return rows.map(rowToChunk);
154
228
  }
155
229
  export function getMeta(db, key) {
@@ -160,4 +234,8 @@ export function documentCount(db) {
160
234
  const row = db.prepare("SELECT COUNT(*) AS n FROM documents").get();
161
235
  return row.n;
162
236
  }
237
+ export function embeddingCount(db) {
238
+ const row = db.prepare("SELECT COUNT(*) AS n FROM embeddings").get();
239
+ return row.n;
240
+ }
163
241
  //# sourceMappingURL=index-db.js.map