@oomkapwn/enquire-mcp 2.15.0 → 2.17.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.
package/dist/hnsw.d.ts CHANGED
@@ -6,6 +6,42 @@ export interface LabeledVector {
6
6
  /** L2-normalized vector. Caller is responsible for the normalization. */
7
7
  vector: Float32Array;
8
8
  }
9
+ /**
10
+ * v2.16.0 — sidecar metadata persisted alongside the .hnsw.bin index. Used
11
+ * for staleness detection on boot: if `signature` matches the current
12
+ * embed-db's signature (computed by `EmbedDb.computeSignature()`), the
13
+ * pre-built HNSW is loaded; otherwise it's rebuilt from scratch.
14
+ *
15
+ * Stored as JSON next to the binary index (`<file>.meta.json`). Keep this
16
+ * format stable — bumping `formatVersion` invalidates all on-disk
17
+ * indexes for users on the new version (they'll rebuild on next boot,
18
+ * which is harmless but visible).
19
+ */
20
+ export interface HnswPersistedMeta {
21
+ formatVersion: 1;
22
+ /** Embedder dim — must match the corpus the index will be queried with. */
23
+ dim: number;
24
+ /** Vector count at write time. */
25
+ size: number;
26
+ /**
27
+ * Embed-db signature at write time — when this differs from the current
28
+ * embed-db's signature, the persisted index is stale and should be
29
+ * rebuilt. We use rowcount + max-id + dim as a tractable signature
30
+ * (full content-hash would require reading every vector).
31
+ */
32
+ signature: string;
33
+ /** Row label → source row map needed to reconstruct hits. JSON-friendly. */
34
+ rowsByLabel: Record<string, {
35
+ rel_path: string;
36
+ chunk_index: number;
37
+ line_start: number;
38
+ line_end: number;
39
+ text_preview: string;
40
+ kind: "md" | "pdf";
41
+ }>;
42
+ /** ISO timestamp of the write — informational. */
43
+ writtenAt: string;
44
+ }
9
45
  /** Build-time HNSW parameters. Defaults tuned for 384-dim cosine on PKM data. */
10
46
  export interface HnswBuildOptions {
11
47
  /** Embedding dimensionality (must match the corpus). */
@@ -52,6 +88,25 @@ export interface HnswIndex {
52
88
  labels: number[];
53
89
  distances: number[];
54
90
  };
91
+ /**
92
+ * v2.16.0 — persist the index to disk for fast reload on next serve
93
+ * start. Writes the binary index to `<file>.bin` and a JSON meta
94
+ * sidecar to `<file>.meta.json` containing the embed-db signature,
95
+ * dim, size, and label→row map. Returns true on successful write.
96
+ *
97
+ * Caller is responsible for choosing `file` (typically alongside the
98
+ * embed-db with `.hnsw` suffix). We separate binary + meta files so
99
+ * a partial write (e.g. crash mid-flush) leaves the meta missing,
100
+ * which the loader treats as "no usable index" → rebuild from scratch.
101
+ */
102
+ saveTo(file: string, rowsByLabel: ReadonlyMap<number, {
103
+ rel_path: string;
104
+ chunk_index: number;
105
+ line_start: number;
106
+ line_end: number;
107
+ text_preview: string;
108
+ kind: "md" | "pdf";
109
+ }>, signature: string): Promise<boolean>;
55
110
  }
56
111
  /**
57
112
  * Build a fresh in-memory HNSW from labeled vectors.
@@ -65,6 +120,23 @@ export interface HnswIndex {
65
120
  * is less than the input count, or if `hnswlib-wasm` failed to load.
66
121
  */
67
122
  export declare function buildHnsw(vectors: ReadonlyArray<LabeledVector>, opts: HnswBuildOptions): Promise<HnswIndex>;
123
+ /**
124
+ * v2.16.0 — load a previously-persisted HNSW index from disk. Returns
125
+ * `null` (with a stderr warning) if:
126
+ * • Either the .bin or .meta.json file is missing
127
+ * • The meta's `signature` doesn't match the caller's current signature
128
+ * • The meta's `formatVersion` doesn't match
129
+ * • The native lib fails to load the .bin (corrupt / dim mismatch)
130
+ *
131
+ * On success returns `{ index, rowsByLabel }` so the caller can wire
132
+ * both into `searchHybrid`'s `hnsw` context without rebuilding from
133
+ * scratch. Typical boot-time win: ~25s rebuild → ~50ms load on a
134
+ * 50K-chunk vault.
135
+ */
136
+ export declare function loadHnswFromDisk(file: string, expectedSignature: string): Promise<{
137
+ index: HnswIndex;
138
+ rowsByLabel: Map<number, HnswPersistedMeta["rowsByLabel"][string]>;
139
+ } | null>;
68
140
  /**
69
141
  * Convert HNSW search results to EmbedSearchHit using a label → source-row
70
142
  * lookup. The label was assigned by the caller at build time (typically
@@ -1 +1 @@
1
- {"version":3,"file":"hnsw.d.ts","sourceRoot":"","sources":["../src/hnsw.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,CAAC,CAAC,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAClH;AAgDD;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAmDjH;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,EACjD,UAAU,EAAE,WAAW,CACrB,MAAM,EACN;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC;CACpB,CACF,GACA,cAAc,EAAE,CAsBlB"}
1
+ {"version":3,"file":"hnsw.d.ts","sourceRoot":"","sources":["../src/hnsw.ts"],"names":[],"mappings":"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"}
package/dist/hnsw.js CHANGED
@@ -104,9 +104,17 @@ export async function buildHnsw(vectors, opts) {
104
104
  // an intermediate; we use a plain spread which is fast and explicit.
105
105
  ctor.addPoint(Array.from(v.vector), v.label);
106
106
  }
107
+ return wrapNativeIndex(ctor, dim, vectors.length);
108
+ }
109
+ /**
110
+ * v2.16.0 — wrap a native hnswlib-node index (built fresh OR loaded from
111
+ * disk) as our `HnswIndex` type. Factored out of `buildHnsw` so the
112
+ * load-from-disk path returns the same shape without re-running addPoint.
113
+ */
114
+ function wrapNativeIndex(ctor, dim, size) {
107
115
  return {
108
116
  dim,
109
- size: vectors.length,
117
+ size,
110
118
  searchKnn(queryVec, k, qOpts) {
111
119
  if (queryVec.length !== dim) {
112
120
  throw new Error(`HnswIndex.searchKnn: query dim ${queryVec.length} ≠ index dim ${dim}`);
@@ -117,9 +125,98 @@ export async function buildHnsw(vectors, opts) {
117
125
  ctor.setEf(ef);
118
126
  const result = ctor.searchKnn(Array.from(queryVec), k, undefined);
119
127
  return { labels: result.neighbors, distances: result.distances };
128
+ },
129
+ async saveTo(file, rowsByLabel, signature) {
130
+ const fs = await import("node:fs/promises");
131
+ const path = await import("node:path");
132
+ // Write the binary index to <file>.bin and the JSON meta sidecar
133
+ // to <file>.meta.json. We separate them so a partial-write (e.g.
134
+ // crash mid-flush) leaves meta missing → loader rebuilds.
135
+ await fs.mkdir(path.dirname(file), { recursive: true });
136
+ const binFile = `${file}.bin`;
137
+ const metaFile = `${file}.meta.json`;
138
+ // hnswlib-node's writeIndex writes to a host filesystem path
139
+ // directly — much simpler than the WASM Emscripten FS plumbing.
140
+ await ctor.writeIndex(binFile);
141
+ const meta = {
142
+ formatVersion: 1,
143
+ dim,
144
+ size,
145
+ signature,
146
+ rowsByLabel: Object.fromEntries(rowsByLabel),
147
+ writtenAt: new Date().toISOString()
148
+ };
149
+ await fs.writeFile(metaFile, JSON.stringify(meta, null, 2), "utf8");
150
+ return true;
120
151
  }
121
152
  };
122
153
  }
154
+ /**
155
+ * v2.16.0 — load a previously-persisted HNSW index from disk. Returns
156
+ * `null` (with a stderr warning) if:
157
+ * • Either the .bin or .meta.json file is missing
158
+ * • The meta's `signature` doesn't match the caller's current signature
159
+ * • The meta's `formatVersion` doesn't match
160
+ * • The native lib fails to load the .bin (corrupt / dim mismatch)
161
+ *
162
+ * On success returns `{ index, rowsByLabel }` so the caller can wire
163
+ * both into `searchHybrid`'s `hnsw` context without rebuilding from
164
+ * scratch. Typical boot-time win: ~25s rebuild → ~50ms load on a
165
+ * 50K-chunk vault.
166
+ */
167
+ export async function loadHnswFromDisk(file, expectedSignature) {
168
+ const fs = await import("node:fs/promises");
169
+ const binFile = `${file}.bin`;
170
+ const metaFile = `${file}.meta.json`;
171
+ let metaRaw;
172
+ try {
173
+ metaRaw = await fs.readFile(metaFile, "utf8");
174
+ }
175
+ catch {
176
+ return null; // No meta → no persisted index (or partial write).
177
+ }
178
+ let meta;
179
+ try {
180
+ meta = JSON.parse(metaRaw);
181
+ }
182
+ catch (err) {
183
+ process.stderr.write(`enquire: HNSW meta at ${metaFile} is malformed; rebuilding — ${err instanceof Error ? err.message : String(err)}\n`);
184
+ return null;
185
+ }
186
+ if (meta.formatVersion !== 1) {
187
+ process.stderr.write(`enquire: HNSW meta format ${meta.formatVersion} ≠ expected 1; rebuilding (this happens on enquire-mcp upgrade)\n`);
188
+ return null;
189
+ }
190
+ if (meta.signature !== expectedSignature) {
191
+ process.stderr.write(`enquire: HNSW persisted index is stale (signature mismatch — embed-db changed since last write); rebuilding\n`);
192
+ return null;
193
+ }
194
+ // Bin file present?
195
+ try {
196
+ await fs.access(binFile);
197
+ }
198
+ catch {
199
+ process.stderr.write(`enquire: HNSW meta exists but ${binFile} is missing; rebuilding\n`);
200
+ return null;
201
+ }
202
+ // Load the native binary.
203
+ const lib = await loadHnswlib();
204
+ const ctor = new lib.HierarchicalNSW("cosine", meta.dim);
205
+ try {
206
+ await ctor.readIndex(binFile);
207
+ }
208
+ catch (err) {
209
+ process.stderr.write(`enquire: HNSW readIndex failed at ${binFile}; rebuilding — ${err instanceof Error ? err.message : String(err)}\n`);
210
+ return null;
211
+ }
212
+ const index = wrapNativeIndex(ctor, meta.dim, meta.size);
213
+ // Reconstruct the row map.
214
+ const rowsByLabel = new Map();
215
+ for (const [labelStr, row] of Object.entries(meta.rowsByLabel)) {
216
+ rowsByLabel.set(Number.parseInt(labelStr, 10), row);
217
+ }
218
+ return { index, rowsByLabel };
219
+ }
123
220
  /**
124
221
  * Convert HNSW search results to EmbedSearchHit using a label → source-row
125
222
  * lookup. The label was assigned by the caller at build time (typically
package/dist/hnsw.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hnsw.js","sourceRoot":"","sources":["../src/hnsw.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hnsw.js","sourceRoot":"","sources":["../src/hnsw.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -39,6 +39,19 @@ export interface ServeOptions {
39
39
  hnswEf?: string;
40
40
  /** v2.15.0 — late-chunking context windowing for embeddings (default 0 chars). */
41
41
  lateChunkContext?: string;
42
+ /** v2.16.0 — persist HNSW index to disk for fast reload on next serve.
43
+ * Default true (the persistence is a pure optimization; corrupt files
44
+ * fall back to rebuild gracefully). Pass `--no-hnsw-persist` to opt out. */
45
+ hnswPersist?: boolean;
46
+ /** v2.17.0 — vector storage encoding for the persistent embed db.
47
+ * - `"f32"` (default) — Float32 BLOB, identical to v2.16- behavior.
48
+ * - `"int8"` — int8-quantized BLOB + per-vector (vMin, scale) Float32
49
+ * tuple. ~4× storage reduction at ~1-2% recall@10 cost.
50
+ * Mode is per-database; switching modes triggers a full rebuild
51
+ * (the meta-table contamination guard treats it as a schema change).
52
+ * Must match the mode used at build-embeddings time — serving with a
53
+ * different mode would auto-rebuild the index. */
54
+ quantizeEmbeddings?: "f32" | "int8";
42
55
  }
43
56
  declare function main(): Promise<void>;
44
57
  /**
@@ -128,5 +141,12 @@ export declare function buildEmbedText(chunks: ReadonlyArray<{
128
141
  contextChars: number;
129
142
  }): string;
130
143
  declare function parsePositiveInt(raw: string, flag: string): number;
131
- export { main, parsePositiveInt, startServer };
144
+ /**
145
+ * v2.17.0 — validate a `--quantize-embeddings <mode>` value. Accepts the
146
+ * canonical `"f32"` / `"int8"` plus a few user-friendly aliases (`"none"`
147
+ * for f32; `"q8"` for int8). Anything else throws with the exact list of
148
+ * accepted values so the user can fix the typo immediately.
149
+ */
150
+ declare function parseQuantizationMode(raw: string | undefined): "f32" | "int8" | undefined;
151
+ export { main, parsePositiveInt, parseQuantizationMode, startServer };
132
152
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import { chunkContent, defaultIndexFile, FtsIndex } from "./fts5.js";
12
12
  import { appendToNote, archiveNote, chatThreadAppend, chatThreadRead, contextPack, createNote, dataviewQuery, embeddingsSearch, findPath, findSimilar, frontmatterGet, frontmatterSearch, frontmatterSet, getBacklinks, getNoteNeighbors, getOpenQuestions, getOutboundLinks, getRecentEdits, getUnresolvedWikilinks, getVaultStats, lintWiki, listCanvases, listNotes, listPdfs, listTags, ocrPdf, openInUi, paperAudit, readCanvas, readNote, readPdf, renameNote, replaceInNotes, resolveWikilink, searchHybrid, searchText, semanticSearch, validateNoteProposal } from "./tools.js";
13
13
  import { Vault } from "./vault.js";
14
14
  import { VaultWatcher } from "./watcher.js";
15
- const VERSION = "2.15.0";
15
+ const VERSION = "2.17.0";
16
16
  /** Default location for the persistent embedding index, alongside .fts5.db. */
17
17
  function embedDbPath(vaultRoot) {
18
18
  // Match the FTS5 location convention by stripping the .fts5.db extension
@@ -50,7 +50,11 @@ async function main() {
50
50
  .option("--use-hnsw", "v2.13.0 — build an in-memory HNSW vector index on serve start (or rebuild if `.embed.db` is missing). Sub-10ms top-K queries at any vault scale, vs O(n) brute-force without it. Build cost: ~5s for 8K chunks, ~25s for 50K, ~4min for 500K (one-time per serve). Recall@10 ≥ 98% vs brute-force at default params. Requires the `hnswlib-wasm` optionalDependency (~340 KB, pure WASM, no native binding).")
51
51
  .option("--hnsw-ef <n>", "v2.13.0 — HNSW search-time beam width (default 100; must be ≥ requested k). Higher = more accurate, slightly slower. Common range: 50-500. Only effective with `--use-hnsw`.")
52
52
  .option("--late-chunk-context <chars>", "v2.15.0 — late-chunking-style context windowing on embeddings. When > 0, prepends doc title + heading breadcrumb + tails of neighboring chunks (this many chars from each side) before sending to the embedder. Typical +2-5 NDCG@10 retrieval boost at zero new dep cost. Default 0 (off; matches v2.1.0+ breadcrumb-only behavior). Only effective during `build-embeddings` or auto-rebuild.")
53
+ .option("--no-hnsw-persist", "v2.16.0 — disable HNSW index persistence. By default (with --use-hnsw), the index is saved to a sidecar `.hnsw.bin` + `.meta.json` next to `.embed.db` after the first build, then re-loaded on subsequent serve starts when the embed-db signature matches. Skipping persistence means a fresh rebuild every serve start (~25s for 50K chunks). Pass this flag if you can't write to the cache dir or want diagnostic-fresh builds.")
54
+ .option("--quantize-embeddings <mode>", "v2.17.0 — vector storage encoding for the persistent embed db. `f32` (default) is identical to v2.16- behavior. `int8` cuts BLOB size ~4× (per-vector min+scale + int8 bytes) at ~1-2% recall@10 cost. Must match the mode used at `build-embeddings` time — otherwise the index auto-rebuilds on serve start. Accepts `f32`/`float32`/`none` and `int8`/`i8`/`q8`.")
53
55
  .action(async (opts) => {
56
+ // Validate up-front so a bad value fails before we touch the vault.
57
+ parseQuantizationMode(opts.quantizeEmbeddings);
54
58
  await startServer(opts);
55
59
  });
56
60
  // v2.6.0 — remote-MCP HTTP transport. Mirrors `serve` flags + adds HTTP
@@ -84,6 +88,7 @@ async function main() {
84
88
  .option("--disabled-tools <name...>", "Skip registration of specific tools by name.")
85
89
  .option("--enabled-tools <name...>", "Strict allowlist — when set, ONLY listed tools register.")
86
90
  .option("--diagnostic-search-tools", "Register the four single-ranker search tools alongside obsidian_search.")
91
+ .option("--quantize-embeddings <mode>", "v2.17.0 — vector storage encoding for the persistent embed db (`f32` default, `int8` for ~4× smaller BLOBs). Must match the mode used at `build-embeddings` time.")
87
92
  .action(async (opts) => {
88
93
  const tokenFromArg = typeof opts.bearerToken === "string" ? opts.bearerToken.trim() : "";
89
94
  const tokenFromEnv = typeof opts.bearerTokenEnv === "string" ? (process.env[opts.bearerTokenEnv] ?? "").trim() : "";
@@ -107,8 +112,11 @@ async function main() {
107
112
  ? parsePositiveInt(opts.sessionIdleTimeoutMs, "--session-idle-timeout-ms")
108
113
  : 30 * 60 * 1000;
109
114
  const maxSessionsCap = opts.maxSessions !== undefined ? parsePositiveInt(opts.maxSessions, "--max-sessions") : 100;
115
+ // v2.17.0 — fail fast on a typo'd quantization mode.
116
+ const quantMode = parseQuantizationMode(opts.quantizeEmbeddings);
110
117
  const httpOpts = {
111
118
  ...opts,
119
+ ...(quantMode !== undefined ? { quantizeEmbeddings: quantMode } : {}),
112
120
  port: portNum,
113
121
  host: opts.host ?? "127.0.0.1",
114
122
  bearerToken,
@@ -229,12 +237,20 @@ async function main() {
229
237
  .option("--read-paths <pattern...>", "Strict allowlist of glob patterns (repeatable)")
230
238
  .option("--include-pdfs", "v2.8.0 — also embed PDF chunks. Off by default; PDF extraction + embedding is ~10-30x slower than markdown per file.")
231
239
  .option("--late-chunk-context <chars>", "v2.15.0 — context-windowed embedding text (doc title + breadcrumb + neighbor-chunk tails of N chars). Default 0 (off). Typical 100-200 for +2-5 NDCG@10.")
240
+ .option("--quantize-embeddings <mode>", "v2.17.0 — vector storage encoding. `f32` (default) is identical to v2.16- behavior. `int8` uses asymmetric scalar quantization (per-vector min + scale + int8 bytes) for ~4× smaller BLOBs at ~1-2% recall@10 cost. Switching modes triggers a full rebuild via the schema-mismatch path. Accepts `f32`/`float32`/`none` and `int8`/`i8`/`q8`.")
232
241
  .action(async (opts) => {
233
242
  const model = resolveModel(opts.embeddingModel);
234
243
  const vault = new Vault(opts.vault, { excludeGlobs: opts.excludeGlob, readPaths: opts.readPaths });
235
244
  await vault.ensureExists();
236
245
  const embedFile = opts.embedFile ?? embedDbPath(vault.root);
237
- const db = new EmbedDb({ file: embedFile, vaultRoot: vault.root, modelAlias: model.alias, dim: model.dim });
246
+ const quantization = parseQuantizationMode(opts.quantizeEmbeddings) ?? "f32";
247
+ const db = new EmbedDb({
248
+ file: embedFile,
249
+ vaultRoot: vault.root,
250
+ modelAlias: model.alias,
251
+ dim: model.dim,
252
+ quantization
253
+ });
238
254
  const lateChunkContext = opts.lateChunkContext !== undefined
239
255
  ? Math.max(0, parsePositiveInt(opts.lateChunkContext, "--late-chunk-context"))
240
256
  : 0;
@@ -243,7 +259,7 @@ async function main() {
243
259
  process.stderr.write(`enquire: loading embedder ${model.alias} (${model.hfId})...\n`);
244
260
  const embedder = await loadEmbedder(opts.embeddingModel);
245
261
  const report = await syncEmbedDb(vault, db, embedder, { lateChunkContext });
246
- process.stdout.write(`enquire: embed db ${embedFile} (md) — added=${report.added} updated=${report.updated} deleted=${report.deleted} unchanged=${report.unchanged} total_chunks=${report.total_chunks}${lateChunkContext > 0 ? ` late-chunk-context=${lateChunkContext}` : ""}\n`);
262
+ process.stdout.write(`enquire: embed db ${embedFile} (md) — added=${report.added} updated=${report.updated} deleted=${report.deleted} unchanged=${report.unchanged} total_chunks=${report.total_chunks}${lateChunkContext > 0 ? ` late-chunk-context=${lateChunkContext}` : ""}${quantization !== "f32" ? ` quantization=${quantization}` : ""}\n`);
247
263
  if (opts.includePdfs) {
248
264
  const pdfReport = await syncPdfEmbedDb(vault, db, embedder, { lateChunkContext });
249
265
  process.stdout.write(`enquire: embed db ${embedFile} (pdf) — added=${pdfReport.added} updated=${pdfReport.updated} deleted=${pdfReport.deleted} unchanged=${pdfReport.unchanged} total_chunks=${pdfReport.total_chunks}\n`);
@@ -303,6 +319,7 @@ async function main() {
303
319
  .option("--embedding-model <alias>", `Model alias (default: ${DEFAULT_MODEL_ALIAS})`, DEFAULT_MODEL_ALIAS)
304
320
  .option("--include-pdfs", "Also index PDFs (FTS5 + embeddings). Off by default; opt-in because PDF extraction is slower.")
305
321
  .option("--skip-embeddings", "Skip the install-model + build-embeddings steps (only build FTS5)")
322
+ .option("--quantize-embeddings <mode>", "v2.17.0 — vector storage encoding for the embed db (`f32` default, `int8` for ~4× smaller BLOBs). Same semantics as the `build-embeddings` flag.")
306
323
  .action(async (opts) => {
307
324
  const v = new Vault(opts.vault);
308
325
  await v.ensureExists();
@@ -341,11 +358,18 @@ async function main() {
341
358
  // Step 3: build-embeddings.
342
359
  process.stdout.write("\n>> Step 3/3: Build embedding index\n");
343
360
  const embedFile = embedDbPath(v.root);
344
- const db = new EmbedDb({ file: embedFile, vaultRoot: v.root, modelAlias: model.alias, dim: model.dim });
361
+ const quantization = parseQuantizationMode(opts.quantizeEmbeddings) ?? "f32";
362
+ const db = new EmbedDb({
363
+ file: embedFile,
364
+ vaultRoot: v.root,
365
+ modelAlias: model.alias,
366
+ dim: model.dim,
367
+ quantization
368
+ });
345
369
  await db.open();
346
370
  try {
347
371
  const embReport = await syncEmbedDb(v, db, embedder);
348
- process.stdout.write(` embed-db (md): added=${embReport.added} updated=${embReport.updated} unchanged=${embReport.unchanged} chunks=${embReport.total_chunks}\n`);
372
+ process.stdout.write(` embed-db (md): added=${embReport.added} updated=${embReport.updated} unchanged=${embReport.unchanged} chunks=${embReport.total_chunks}${quantization !== "f32" ? ` quantization=${quantization}` : ""}\n`);
349
373
  if (opts.includePdfs) {
350
374
  const pdfReport = await syncPdfEmbedDb(v, db, embedder);
351
375
  process.stdout.write(` embed-db (pdf): added=${pdfReport.added} updated=${pdfReport.updated} unchanged=${pdfReport.unchanged} chunks=${pdfReport.total_chunks}\n`);
@@ -358,6 +382,8 @@ async function main() {
358
382
  process.stdout.write(` enquire-mcp serve --vault ${opts.vault} --persistent-index`);
359
383
  if (opts.includePdfs)
360
384
  process.stdout.write(" --include-pdfs");
385
+ if (quantization !== "f32")
386
+ process.stdout.write(` --quantize-embeddings ${quantization}`);
361
387
  process.stdout.write("\n");
362
388
  process.stdout.write(`Or check status: enquire-mcp doctor --vault ${opts.vault}\n`);
363
389
  });
@@ -562,31 +588,76 @@ export async function prepareServerDeps(opts) {
562
588
  // the meta directly without reopening. For now we accept the
563
589
  // over-default-fallback risk and recommend doctor's output.
564
590
  const model = resolveModel(undefined);
565
- const db = new EmbedDb({ file: embedFile, vaultRoot: vault.root, modelAlias: model.alias, dim: model.dim });
591
+ // v2.17.0 pass through the quantization mode from CLI so the
592
+ // schema check matches what build-embeddings wrote. Default
593
+ // "f32" matches v2.16- behavior for users who don't set it.
594
+ const quantization = opts.quantizeEmbeddings ?? "f32";
595
+ const db = new EmbedDb({
596
+ file: embedFile,
597
+ vaultRoot: vault.root,
598
+ modelAlias: model.alias,
599
+ dim: model.dim,
600
+ quantization
601
+ });
566
602
  await db.open();
567
603
  try {
568
604
  const startMs = Date.now();
569
- const rows = db.getAllVectors();
570
- if (rows.length === 0) {
571
- process.stderr.write(`enquire: --use-hnsw passed but embed-db is empty; skipping HNSW build.\n`);
605
+ // v2.16.0 try to load from disk first if persistence is enabled.
606
+ // Skip-rebuild path: ~50ms read vs ~25s build for 50K-chunk
607
+ // vault when nothing changed since last serve. Staleness
608
+ // detected via `EmbedDb.computeSignature()` mismatch.
609
+ const persistFile = `${embedFile.replace(/\.embed\.db$/, "")}.hnsw`;
610
+ const signature = db.computeSignature();
611
+ const efOverride = opts.hnswEf ? parsePositiveInt(opts.hnswEf, "--hnsw-ef") : undefined;
612
+ let loaded = null;
613
+ if (opts.hnswPersist !== false) {
614
+ const { loadHnswFromDisk } = await import("./hnsw.js");
615
+ const loadResult = await loadHnswFromDisk(persistFile, signature);
616
+ if (loadResult) {
617
+ loaded = { index: loadResult.index, rowByLabel: loadResult.rowsByLabel };
618
+ process.stderr.write(`enquire: HNSW index loaded from disk (${loadResult.index.size} vectors, dim=${loadResult.index.dim}, ${Date.now() - startMs}ms — signature matched)\n`);
619
+ }
620
+ }
621
+ if (loaded) {
622
+ hnswContext = {
623
+ index: loaded.index,
624
+ rowByLabel: loaded.rowByLabel,
625
+ ...(efOverride !== undefined ? { ef: efOverride } : {})
626
+ };
572
627
  }
573
628
  else {
574
- const { buildHnsw } = await import("./hnsw.js");
575
- const index = await buildHnsw(rows.map((r) => ({ label: r.label, vector: r.vector })), { dim: model.dim, maxElements: rows.length });
576
- const rowByLabel = new Map();
577
- for (const r of rows) {
578
- rowByLabel.set(r.label, {
579
- rel_path: r.rel_path,
580
- chunk_index: r.chunk_index,
581
- line_start: r.line_start,
582
- line_end: r.line_end,
583
- text_preview: r.text_preview,
584
- kind: r.kind
585
- });
629
+ const rows = db.getAllVectors();
630
+ if (rows.length === 0) {
631
+ process.stderr.write(`enquire: --use-hnsw passed but embed-db is empty; skipping HNSW build.\n`);
632
+ }
633
+ else {
634
+ const { buildHnsw } = await import("./hnsw.js");
635
+ const index = await buildHnsw(rows.map((r) => ({ label: r.label, vector: r.vector })), { dim: model.dim, maxElements: rows.length });
636
+ const rowByLabel = new Map();
637
+ for (const r of rows) {
638
+ rowByLabel.set(r.label, {
639
+ rel_path: r.rel_path,
640
+ chunk_index: r.chunk_index,
641
+ line_start: r.line_start,
642
+ line_end: r.line_end,
643
+ text_preview: r.text_preview,
644
+ kind: r.kind
645
+ });
646
+ }
647
+ hnswContext = { index, rowByLabel, ...(efOverride !== undefined ? { ef: efOverride } : {}) };
648
+ process.stderr.write(`enquire: HNSW index built (${rows.length} vectors, dim=${model.dim}, ${Date.now() - startMs}ms)\n`);
649
+ // v2.16.0 — persist the freshly-built index for next serve start.
650
+ if (opts.hnswPersist !== false) {
651
+ try {
652
+ await index.saveTo(persistFile, rowByLabel, signature);
653
+ process.stderr.write(`enquire: HNSW index persisted to ${persistFile}.bin (+ .meta.json)\n`);
654
+ }
655
+ catch (err) {
656
+ // Non-fatal — persistence is an optimization. Log + continue.
657
+ process.stderr.write(`enquire: HNSW persist failed (continuing with in-memory index) — ${err instanceof Error ? err.message : String(err)}\n`);
658
+ }
659
+ }
586
660
  }
587
- const efOverride = opts.hnswEf ? parsePositiveInt(opts.hnswEf, "--hnsw-ef") : undefined;
588
- hnswContext = { index, rowByLabel, ...(efOverride !== undefined ? { ef: efOverride } : {}) };
589
- process.stderr.write(`enquire: HNSW index built (${rows.length} vectors, dim=${model.dim}, ${Date.now() - startMs}ms)\n`);
590
661
  }
591
662
  }
592
663
  finally {
@@ -2461,6 +2532,23 @@ function parsePositiveInt(raw, flag) {
2461
2532
  }
2462
2533
  return n;
2463
2534
  }
2535
+ /**
2536
+ * v2.17.0 — validate a `--quantize-embeddings <mode>` value. Accepts the
2537
+ * canonical `"f32"` / `"int8"` plus a few user-friendly aliases (`"none"`
2538
+ * for f32; `"q8"` for int8). Anything else throws with the exact list of
2539
+ * accepted values so the user can fix the typo immediately.
2540
+ */
2541
+ function parseQuantizationMode(raw) {
2542
+ if (raw === undefined)
2543
+ return undefined;
2544
+ const norm = raw.trim().toLowerCase();
2545
+ if (norm === "" || norm === "f32" || norm === "float32" || norm === "none")
2546
+ return "f32";
2547
+ if (norm === "int8" || norm === "i8" || norm === "q8")
2548
+ return "int8";
2549
+ throw new Error(`--quantize-embeddings must be "f32" or "int8" (got "${raw}"). ` +
2550
+ `Aliases: "none"/"float32" → f32, "q8"/"i8" → int8.`);
2551
+ }
2464
2552
  function encodeNotePath(relPath) {
2465
2553
  return relPath.split(path.sep).map(encodeURIComponent).join("/");
2466
2554
  }
@@ -2494,5 +2582,5 @@ if (isCliEntry) {
2494
2582
  process.exit(1);
2495
2583
  });
2496
2584
  }
2497
- export { main, parsePositiveInt, startServer };
2585
+ export { main, parsePositiveInt, parseQuantizationMode, startServer };
2498
2586
  //# sourceMappingURL=index.js.map