@djolex999/vir-cli 0.14.0 → 0.15.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.
@@ -0,0 +1,99 @@
1
+ import { existsSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ export const LOCAL_EMBED_MODEL = "bge-small-en-v1.5";
7
+ export const LOCAL_EMBED_DIM = 384;
8
+ // bge-small truncates at 512 tokens. 2000 chars ≈ 500 tokens on the chars/4
9
+ // heuristic — under the model's own cutoff, so what we record as "sent" is
10
+ // what the model actually saw, same discipline as the Ollama context fix.
11
+ export const LOCAL_MAX_CHARS = 2000;
12
+ // fastembed is ~233 MB of node_modules — far too heavy to ship as a dependency
13
+ // of a ~200 KB CLI, so it lives in its own npm prefix under ~/.vir, installed
14
+ // on demand by `vir embed --setup` and loaded dynamically from there.
15
+ export const LOCAL_PROVIDER_DIR = join(homedir(), ".vir", "embedder");
16
+ export const LOCAL_MODEL_CACHE_DIR = join(LOCAL_PROVIDER_DIR, "model-cache");
17
+ // Measured on the 2026-07-31 scratchpad experiment (fastembed 2.1.0, darwin).
18
+ export const SETUP_INSTALL_MB = 233;
19
+ export const SETUP_MODEL_MB = 128;
20
+ export function isLocalProviderInstalled(dir = LOCAL_PROVIDER_DIR) {
21
+ return existsSync(join(dir, "node_modules", "fastembed"));
22
+ }
23
+ async function loadFastembedEngine() {
24
+ const req = createRequire(join(LOCAL_PROVIDER_DIR, "package.json"));
25
+ const resolved = req.resolve("fastembed");
26
+ const imported = (await import(pathToFileURL(resolved).href));
27
+ const mod = "FlagEmbedding" in imported ? imported : imported.default;
28
+ const model = await mod.FlagEmbedding.init({
29
+ model: mod.EmbeddingModel["BGESmallENV15"] ?? "fast-bge-small-en-v1.5",
30
+ cacheDir: LOCAL_MODEL_CACHE_DIR,
31
+ });
32
+ return {
33
+ embedBatch: async (texts) => {
34
+ const out = [];
35
+ for await (const batch of model.embed(texts, 16)) {
36
+ for (const v of batch)
37
+ out.push(Array.from(v));
38
+ }
39
+ return out;
40
+ },
41
+ queryEmbed: async (text) => Array.from(await model.queryEmbed(text)),
42
+ };
43
+ }
44
+ export class LocalProvider {
45
+ engineFactory;
46
+ name = "local";
47
+ modelName = LOCAL_EMBED_MODEL;
48
+ dimensions = LOCAL_EMBED_DIM;
49
+ maxInputChars = LOCAL_MAX_CHARS;
50
+ enginePromise = null;
51
+ // engineFactory is injectable for tests only — production callers pass
52
+ // nothing and get the real fastembed load from LOCAL_PROVIDER_DIR.
53
+ constructor(engineFactory = loadFastembedEngine) {
54
+ this.engineFactory = engineFactory;
55
+ }
56
+ engine() {
57
+ this.enginePromise ??= this.engineFactory();
58
+ return this.enginePromise;
59
+ }
60
+ async available() {
61
+ return isLocalProviderInstalled();
62
+ }
63
+ async embedDoc(text) {
64
+ const sent = text.slice(0, this.maxInputChars);
65
+ const vecs = await (await this.engine()).embedBatch([sent]);
66
+ const embedding = vecs[0];
67
+ if (!embedding || embedding.length === 0) {
68
+ throw new Error("fastembed returned no embedding");
69
+ }
70
+ return {
71
+ embedding,
72
+ sentChars: sent.length,
73
+ truncated: sent.length < text.length,
74
+ };
75
+ }
76
+ async embedQuery(text) {
77
+ return (await this.engine()).queryEmbed(text.slice(0, this.maxInputChars));
78
+ }
79
+ provenance() {
80
+ return { model: this.modelName, dim: this.dimensions };
81
+ }
82
+ }
83
+ // The cost statement `vir embed --setup` prints BEFORE touching the disk or
84
+ // the network. quiet=true drops the decorative lines but never the numbers.
85
+ export function buildSetupPlan(installed, quiet) {
86
+ const lines = [];
87
+ if (installed) {
88
+ lines.push("fastembed already installed — skipping the package install.");
89
+ }
90
+ else {
91
+ lines.push(`This installs fastembed (~${SETUP_INSTALL_MB} MB of node_modules) into ${LOCAL_PROVIDER_DIR}`);
92
+ }
93
+ lines.push(`and downloads the ${LOCAL_EMBED_MODEL} model (~${SETUP_MODEL_MB} MB) on first embed.`);
94
+ if (!quiet) {
95
+ lines.push("Nothing outside ~/.vir is touched; remove that folder to undo.");
96
+ }
97
+ return { lines, needsInstall: !installed };
98
+ }
99
+ //# sourceMappingURL=localProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localProvider.js","sourceRoot":"","sources":["../../src/search/localProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { EMBED_DIM, EMBED_MAX_CHARS, EMBED_MODEL, EmbedderError, embed, embedText, isOllamaAvailable, } from "./embedder.js";
2
+ import { LocalProvider, isLocalProviderInstalled } from "./localProvider.js";
3
+ // Resolution order: configured > Ollama detected > local installed > none.
4
+ // `none` is a supported floor (TF-IDF), never an error state — so a configured
5
+ // provider that turns out unusable falls through to detection instead of
6
+ // blocking. Returns null for none.
7
+ export async function resolveEmbeddingProvider(configured, probes = {}) {
8
+ const ollamaUp = probes.ollamaAvailable ?? isOllamaAvailable;
9
+ const localUp = probes.localInstalled ?? isLocalProviderInstalled;
10
+ if (configured === "none")
11
+ return null;
12
+ if (configured === "ollama" && (await ollamaUp()))
13
+ return new OllamaProvider();
14
+ if (configured === "local" && localUp())
15
+ return new LocalProvider();
16
+ if (await ollamaUp())
17
+ return new OllamaProvider();
18
+ if (localUp())
19
+ return new LocalProvider();
20
+ return null;
21
+ }
22
+ // Module-level memo mirroring isOllamaAvailableCached: one resolution per
23
+ // process is enough (the daemon restarts every run). Not per-config — a single
24
+ // process never runs two configs.
25
+ let _resolvedProvider = null;
26
+ export function resolveActiveProviderCached(configured) {
27
+ _resolvedProvider ??= resolveEmbeddingProvider(configured);
28
+ return _resolvedProvider;
29
+ }
30
+ // Best-effort embed through a provider: never throws, returns null on failure,
31
+ // reports truncation and failure kind through `log` — the provider-agnostic
32
+ // successor of embedder.ts's embeddingForNote.
33
+ export async function embedNoteWithProvider(provider, text, log) {
34
+ try {
35
+ const result = await provider.embedDoc(text);
36
+ if (result.truncated) {
37
+ log?.(`embedding truncated: sent ${result.sentChars} of ${text.length} chars (model context limit)`);
38
+ }
39
+ return result.embedding;
40
+ }
41
+ catch (err) {
42
+ const kind = err instanceof EmbedderError ? err.kind : "network";
43
+ log?.(`embed failed (${kind}): ${err.message}`);
44
+ return null;
45
+ }
46
+ }
47
+ export class OllamaProvider {
48
+ fetchImpl;
49
+ name = "ollama";
50
+ modelName = EMBED_MODEL;
51
+ dimensions = EMBED_DIM;
52
+ maxInputChars = EMBED_MAX_CHARS;
53
+ // fetchImpl is injectable for tests only — production callers pass nothing.
54
+ constructor(fetchImpl) {
55
+ this.fetchImpl = fetchImpl;
56
+ }
57
+ available() {
58
+ return isOllamaAvailable();
59
+ }
60
+ embedDoc(text) {
61
+ return embedText(text, this.fetchImpl);
62
+ }
63
+ // Routed through embed() (not embedText directly) so test suites that mock
64
+ // the embedder module intercept query embedding the same way they always have.
65
+ embedQuery(text) {
66
+ return embed(text, this.fetchImpl);
67
+ }
68
+ provenance() {
69
+ return { model: this.modelName, dim: this.dimensions };
70
+ }
71
+ }
72
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/search/provider.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,KAAK,EACL,SAAS,EACT,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AA6B7E,2EAA2E;AAC3E,+EAA+E;AAC/E,yEAAyE;AACzE,mCAAmC;AACnC,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAA+C,EAC/C,SAAwB,EAAE;IAE1B,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,IAAI,iBAAiB,CAAC;IAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,IAAI,wBAAwB,CAAC;IAClE,IAAI,UAAU,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,QAAQ,EAAE,CAAC;QAAE,OAAO,IAAI,cAAc,EAAE,CAAC;IAC/E,IAAI,UAAU,KAAK,OAAO,IAAI,OAAO,EAAE;QAAE,OAAO,IAAI,aAAa,EAAE,CAAC;IACpE,IAAI,MAAM,QAAQ,EAAE;QAAE,OAAO,IAAI,cAAc,EAAE,CAAC;IAClD,IAAI,OAAO,EAAE;QAAE,OAAO,IAAI,aAAa,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0EAA0E;AAC1E,+EAA+E;AAC/E,kCAAkC;AAClC,IAAI,iBAAiB,GAA6C,IAAI,CAAC;AACvE,MAAM,UAAU,2BAA2B,CACzC,UAA+C;IAE/C,iBAAiB,KAAK,wBAAwB,CAAC,UAAU,CAAC,CAAC;IAC3D,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,4EAA4E;AAC5E,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAA2B,EAC3B,IAAY,EACZ,GAA2B;IAE3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,GAAG,EAAE,CACH,6BAA6B,MAAM,CAAC,SAAS,OAAO,IAAI,CAAC,MAAM,8BAA8B,CAC9F,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,SAAS,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,GAAG,YAAY,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACjE,GAAG,EAAE,CAAC,iBAAiB,IAAI,MAAO,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,OAAO,cAAc;IAOI;IANpB,IAAI,GAAG,QAAiB,CAAC;IACzB,SAAS,GAAG,WAAW,CAAC;IACxB,UAAU,GAAG,SAAS,CAAC;IACvB,aAAa,GAAG,eAAe,CAAC;IAEzC,4EAA4E;IAC5E,YAA6B,SAAqB;QAArB,cAAS,GAAT,SAAS,CAAY;IAAG,CAAC;IAEtD,SAAS;QACP,OAAO,iBAAiB,EAAE,CAAC;IAC7B,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,2EAA2E;IAC3E,+EAA+E;IAC/E,UAAU,CAAC,IAAY;QACrB,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,UAAU;QACR,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACzD,CAAC;CACF"}
@@ -1,6 +1,8 @@
1
1
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
- import { cosineSimilarity, embed, isOllamaAvailable, } from "./embedder.js";
3
+ import { cosineSimilarity } from "./embedder.js";
4
+ import { resolveEmbeddingProvider, } from "./provider.js";
5
+ import { thresholdsFor } from "./thresholds.js";
4
6
  const SKIP_FILES = new Set(["index.md", "log.md"]);
5
7
  // Directories the TF-IDF walk must never index: derived period summaries
6
8
  // (files-only, never in SQLite), human-rejected notes (`vir review` moves
@@ -8,7 +10,7 @@ const SKIP_FILES = new Set(["index.md", "log.md"]);
8
10
  // covered separately — rejected notes' recorded paths no longer exist (the
9
11
  // content read skips them) and getEmbeddings gates on `archived`.
10
12
  const SKIP_DIRS = new Set(["summaries", ".rejected", "archived"]);
11
- const MIN_EMBEDDING_SCORE = 0.3;
13
+ // Resolved per-model from thresholds.ts — see MODEL_THRESHOLDS.
12
14
  // Notes a user has approved via `vir review` carry `verified: true` in their
13
15
  // frontmatter. They get a flat ranking boost so human-verified knowledge floats
14
16
  // above unverified auto-distillations of comparable relevance. Applied before
@@ -27,8 +29,9 @@ export async function search(cfg, db, query, topK = 8) {
27
29
  return (await searchWithOutcome(cfg, db, query, topK)).hits;
28
30
  }
29
31
  export async function searchWithOutcome(cfg, db, query, topK = 8) {
30
- if (await isOllamaAvailable()) {
31
- const attempt = await searchByEmbedding(cfg, db, query, topK);
32
+ const provider = await resolveEmbeddingProvider(cfg.embeddingProvider);
33
+ if (provider) {
34
+ const attempt = await searchByEmbedding(cfg, db, query, topK, provider);
32
35
  // If embeddings produced at least one match above the floor, take it.
33
36
  // Otherwise fall through to TF-IDF: low cosine on every doc means the
34
37
  // query is semantically off; lexical overlap might still find a match.
@@ -38,6 +41,8 @@ export async function searchWithOutcome(cfg, db, query, topK = 8) {
38
41
  method: "embedding",
39
42
  degraded: false,
40
43
  embedError: null,
44
+ excludedMismatched: attempt.excluded,
45
+ noProvider: false,
41
46
  };
42
47
  }
43
48
  return {
@@ -45,43 +50,61 @@ export async function searchWithOutcome(cfg, db, query, topK = 8) {
45
50
  method: "tfidf",
46
51
  degraded: attempt.error !== null,
47
52
  embedError: attempt.error,
53
+ excludedMismatched: attempt.excluded,
54
+ noProvider: false,
48
55
  };
49
56
  }
57
+ // TF-IDF is the supported floor, not a degraded state — but the caller must
58
+ // be able to say "no provider" rather than "embeddings failed".
50
59
  return {
51
60
  hits: searchByTfIdf(cfg, query, topK),
52
61
  method: "tfidf",
53
62
  degraded: false,
54
63
  embedError: null,
64
+ excludedMismatched: 0,
65
+ noProvider: true,
55
66
  };
56
67
  }
57
- async function searchByEmbedding(cfg, db, query, topK) {
68
+ // Splits an embedding pool into rows comparable with the active model and a
69
+ // count of refusals. Cosine between vectors from different models (or different
70
+ // dimensionalities) is meaningless, so those rows must never enter the ranking.
71
+ export function partitionByEmbeddingModel(rows, activeModel) {
72
+ const compatible = rows.filter((r) => r.embeddingModel === activeModel);
73
+ return { compatible, excluded: rows.length - compatible.length };
74
+ }
75
+ async function searchByEmbedding(cfg, db, query, topK, provider) {
58
76
  let queryVec;
59
77
  try {
60
- queryVec = await embed(query);
78
+ queryVec = await provider.embedQuery(query);
61
79
  }
62
80
  catch (err) {
63
- return { hits: [], error: err.message };
81
+ return { hits: [], error: err.message, excluded: 0 };
64
82
  }
65
83
  const root = vaultRoot(cfg);
66
84
  // Sessions, articles, topics, and PDFs are embedded into the same vector
67
85
  // space; concat all four so semantic search covers every layer. No layer gets
68
86
  // a ranking boost — they compete on cosine like everything else.
69
- const rows = [
87
+ const allRows = [
70
88
  ...db.getEmbeddings(root),
71
89
  ...db.getArticleEmbeddings(),
72
90
  ...db.getTopicEmbeddings(root, cfg.topicsDir),
73
91
  ...db.getPdfEmbeddings(),
74
92
  ];
93
+ // Vectors from another model are geometric nonsense against this query —
94
+ // refuse to compare them. Excluded rows are counted, never silently dropped;
95
+ // they re-enter search once re-embedded under the active model.
96
+ const { compatible: rows, excluded } = partitionByEmbeddingModel(allRows, provider.modelName);
75
97
  if (rows.length === 0)
76
- return { hits: [], error: null };
98
+ return { hits: [], error: null, excluded };
77
99
  // Read each candidate's content once, here, so the verified boost can be
78
100
  // applied BEFORE the topK slice — a verified note must be able to outrank an
79
101
  // unverified one just outside the window. Reads are bounded to docs above the
80
102
  // cosine floor (a personal-scale vault), and the content is reused for hits.
103
+ const minScore = thresholdsFor(provider.modelName).minEmbeddingScore;
81
104
  const enriched = [];
82
105
  for (const r of rows) {
83
106
  const s = cosineSimilarity(queryVec, r.embedding);
84
- if (s < MIN_EMBEDDING_SCORE)
107
+ if (s < minScore)
85
108
  continue;
86
109
  let content = "";
87
110
  try {
@@ -120,7 +143,7 @@ async function searchByEmbedding(cfg, db, query, topK) {
120
143
  method: "embedding",
121
144
  };
122
145
  });
123
- return { hits, error: null };
146
+ return { hits, error: null, excluded };
124
147
  }
125
148
  // Maximal Marginal Relevance: greedily reranks a candidate pool to trade off
126
149
  // relevance against diversity. lambda is the relevance weight — 1.0 is pure
@@ -229,7 +252,11 @@ export function searchTfIdf(docs, query, topK = 8) {
229
252
  const df = dfMap.get(term) ?? 0;
230
253
  if (df === 0)
231
254
  continue;
232
- const idf = Math.log(totalDocs / df);
255
+ // Smoothed: log(1 + N/df), never zero. Bare log(N/df) is 0 whenever
256
+ // df === N — which is EVERY term in a single-note vault, making the
257
+ // first note a new user creates unfindable. log(1+x) → log(x) for
258
+ // large x, so established-vault rankings are essentially unchanged.
259
+ const idf = Math.log(1 + totalDocs / df);
233
260
  // Normalize TF by doc length so long docs don't dominate.
234
261
  const tfNorm = tf / Math.max(1, d.tokens.length);
235
262
  score += tfNorm * idf;
@@ -1 +1 @@
1
- {"version":3,"file":"retriever.js","sourceRoot":"","sources":["../../src/search/retriever.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"retriever.js","sourceRoot":"","sources":["../../src/search/retriever.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export const MODEL_THRESHOLDS = {
2
+ "nomic-embed-text": { minEmbeddingScore: 0.3, relatedMinSim: 0.6 },
3
+ // Calibrated 2026-07-31 by quantile-matching nomic's floors on a real
4
+ // 389-note vault (20 queries; 75,466 doc-doc pairs). The two models'
5
+ // DOC-DOC cosine distributions are nearly identical (p50 0.744 vs 0.741,
6
+ // p90 0.800 vs 0.795), so relatedMinSim carries over unchanged. QUERY-DOC
7
+ // scores run ~0.15 hotter on bge (its query instruction prefix): nomic's
8
+ // 0.3 floor sits ~0.12 below its in-domain minimum (0.416), so bge gets
9
+ // the same margin below its own minimum (0.456) → 0.35. Remaining unknown:
10
+ // where garbage/out-of-domain queries score under bge — measuring that
11
+ // distribution against the vault would pin the floor exactly.
12
+ "bge-small-en-v1.5": { minEmbeddingScore: 0.35, relatedMinSim: 0.6 },
13
+ };
14
+ const FALLBACK = MODEL_THRESHOLDS["nomic-embed-text"];
15
+ export function thresholdsFor(model) {
16
+ return MODEL_THRESHOLDS[model] ?? FALLBACK;
17
+ }
18
+ //# sourceMappingURL=thresholds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thresholds.js","sourceRoot":"","sources":["../../src/search/thresholds.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,gBAAgB,GAAoC;IAC/D,kBAAkB,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE;IAClE,sEAAsE;IACtE,qEAAqE;IACrE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,wEAAwE;IACxE,2EAA2E;IAC3E,uEAAuE;IACvE,8DAA8D;IAC9D,mBAAmB,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE;CACrE,CAAC;AAEF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,kBAAkB,CAAE,CAAC;AAEvD,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC;AAC7C,CAAC"}
package/dist/state/db.js CHANGED
@@ -10,6 +10,13 @@ const GATED_SKIP_REASONS = new Set([
10
10
  "sidechain-transcript",
11
11
  "agent-transcript",
12
12
  ]);
13
+ // Every embedding written before provenance existed came from Ollama's
14
+ // nomic-embed-text (the only embedder any vir binary ever had), so legacy rows
15
+ // backfill to this on migration.
16
+ export const LEGACY_EMBEDDING_PROVENANCE = {
17
+ model: "nomic-embed-text",
18
+ dim: 768,
19
+ };
13
20
  const ADDED_COLUMNS = [
14
21
  { name: "content", ddl: "ALTER TABLE sessions ADD COLUMN content TEXT" },
15
22
  { name: "category", ddl: "ALTER TABLE sessions ADD COLUMN category TEXT" },
@@ -148,6 +155,35 @@ export class StateDb {
148
155
  if (!existing.has(col.name))
149
156
  this.db.exec(col.ddl);
150
157
  }
158
+ // Embedding provenance, all four embedding-bearing tables. Backfill is safe
159
+ // and idempotent: rows embedded before provenance existed can only have come
160
+ // from nomic (the sole embedder in every prior release), and the WHERE
161
+ // clause never touches rows that already carry provenance.
162
+ for (const table of ["sessions", "articles", "pdfs", "topics"]) {
163
+ const cols = new Set(this.db.prepare(`PRAGMA table_info(${table})`).all().map((r) => r.name));
164
+ if (!cols.has("embedding_model")) {
165
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN embedding_model TEXT`);
166
+ }
167
+ if (!cols.has("embedding_dim")) {
168
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN embedding_dim INTEGER`);
169
+ }
170
+ this.db.exec(`UPDATE ${table}
171
+ SET embedding_model = '${LEGACY_EMBEDDING_PROVENANCE.model}',
172
+ embedding_dim = ${LEGACY_EMBEDDING_PROVENANCE.dim}
173
+ WHERE embedding IS NOT NULL AND embedding_model IS NULL`);
174
+ }
175
+ }
176
+ // True when `table` carries the provenance columns. The read-only MCP path
177
+ // skips migrations, so an upgraded-but-never-written DB may lack them; read
178
+ // paths must degrade to legacy provenance instead of erroring.
179
+ provenanceCache = new Map();
180
+ hasProvenance(table) {
181
+ const cached = this.provenanceCache.get(table);
182
+ if (cached !== undefined)
183
+ return cached;
184
+ const has = this.db.prepare(`PRAGMA table_info(${table})`).all().some((r) => r.name === "embedding_model");
185
+ this.provenanceCache.set(table, has);
186
+ return has;
151
187
  }
152
188
  getByPath(path) {
153
189
  return this.db
@@ -451,14 +487,15 @@ export class StateDb {
451
487
  // Embedding storage is keyed by sessionId (the JSONL basename), since callers
452
488
  // typically have that handy and not the full path. Match suffix-anchored:
453
489
  // `path LIKE '%/<sessionId>.jsonl'`.
454
- storeEmbedding(sessionId, embedding) {
490
+ storeEmbedding(sessionId, embedding, provenance) {
455
491
  this.db
456
- .prepare("UPDATE sessions SET embedding = ? WHERE path LIKE ?")
457
- .run(JSON.stringify(embedding), `%/${sessionId}.jsonl`);
492
+ .prepare("UPDATE sessions SET embedding = ?, embedding_model = ?, embedding_dim = ? WHERE path LIKE ?")
493
+ .run(JSON.stringify(embedding), provenance.model, provenance.dim, `%/${sessionId}.jsonl`);
458
494
  }
459
495
  getEmbeddings(vaultRoot) {
496
+ const prov = this.hasProvenance("sessions");
460
497
  const rows = this.db
461
- .prepare(`SELECT path, embedding, topic, category, project
498
+ .prepare(`SELECT path, embedding, topic, category, project${prov ? ", embedding_model, embedding_dim" : ""}
462
499
  FROM sessions
463
500
  WHERE skipped = 0
464
501
  AND error IS NULL
@@ -491,6 +528,8 @@ export class StateDb {
491
528
  project: r.project ?? "",
492
529
  filePath,
493
530
  embedding: vec,
531
+ embeddingModel: r.embedding_model ?? LEGACY_EMBEDDING_PROVENANCE.model,
532
+ embeddingDim: r.embedding_dim ?? vec.length,
494
533
  });
495
534
  }
496
535
  return out;
@@ -500,9 +539,12 @@ export class StateDb {
500
539
  .prepare("UPDATE sessions SET archived = 1 WHERE path = ?")
501
540
  .run(path);
502
541
  }
542
+ // New content invalidates the stored vector — NULL it so the self-heal sweep
543
+ // re-embeds. The sweep only fills NULLs, so a stale non-NULL embedding here
544
+ // would never heal (the 0.8.2-class blind spot, on the UPDATE path).
503
545
  updateContent(path, content) {
504
546
  this.db
505
- .prepare("UPDATE sessions SET content = ? WHERE path = ?")
547
+ .prepare("UPDATE sessions SET content = ?, embedding = NULL, embedding_model = NULL, embedding_dim = NULL WHERE path = ?")
506
548
  .run(content, path);
507
549
  }
508
550
  reset() {
@@ -601,10 +643,10 @@ export class StateDb {
601
643
  AND note_path IS NOT NULL`)
602
644
  .all();
603
645
  }
604
- storeArticleEmbedding(path, embedding) {
646
+ storeArticleEmbedding(path, embedding, provenance) {
605
647
  this.db
606
- .prepare("UPDATE articles SET embedding = ? WHERE path = ?")
607
- .run(JSON.stringify(embedding), path);
648
+ .prepare("UPDATE articles SET embedding = ?, embedding_model = ?, embedding_dim = ? WHERE path = ?")
649
+ .run(JSON.stringify(embedding), provenance.model, provenance.dim, path);
608
650
  }
609
651
  // Article embeddings, shaped like session EmbeddingRow so the retriever can
610
652
  // concat both lists. filePath is the stored note path; project is empty
@@ -612,8 +654,9 @@ export class StateDb {
612
654
  getArticleEmbeddings() {
613
655
  if (!this.hasArticlesTable())
614
656
  return [];
657
+ const prov = this.hasProvenance("articles");
615
658
  const rows = this.db
616
- .prepare(`SELECT note_path, embedding, title, category
659
+ .prepare(`SELECT note_path, embedding, title, category${prov ? ", embedding_model, embedding_dim" : ""}
617
660
  FROM articles
618
661
  WHERE skipped = 0
619
662
  AND error IS NULL
@@ -641,6 +684,8 @@ export class StateDb {
641
684
  project: "",
642
685
  filePath: r.note_path,
643
686
  embedding: vec,
687
+ embeddingModel: r.embedding_model ?? LEGACY_EMBEDDING_PROVENANCE.model,
688
+ embeddingDim: r.embedding_dim ?? vec.length,
644
689
  });
645
690
  }
646
691
  return out;
@@ -728,10 +773,10 @@ export class StateDb {
728
773
  AND note_path IS NOT NULL`)
729
774
  .all();
730
775
  }
731
- storePdfEmbedding(path, embedding) {
776
+ storePdfEmbedding(path, embedding, provenance) {
732
777
  this.db
733
- .prepare("UPDATE pdfs SET embedding = ? WHERE path = ?")
734
- .run(JSON.stringify(embedding), path);
778
+ .prepare("UPDATE pdfs SET embedding = ?, embedding_model = ?, embedding_dim = ? WHERE path = ?")
779
+ .run(JSON.stringify(embedding), provenance.model, provenance.dim, path);
735
780
  }
736
781
  // PDF embeddings, shaped like session/article EmbeddingRow so the retriever can
737
782
  // concat all sources. filePath is the stored note path; project is empty (PDFs
@@ -739,8 +784,9 @@ export class StateDb {
739
784
  getPdfEmbeddings() {
740
785
  if (!this.hasPdfsTable())
741
786
  return [];
787
+ const prov = this.hasProvenance("pdfs");
742
788
  const rows = this.db
743
- .prepare(`SELECT note_path, embedding, title, category
789
+ .prepare(`SELECT note_path, embedding, title, category${prov ? ", embedding_model, embedding_dim" : ""}
744
790
  FROM pdfs
745
791
  WHERE skipped = 0
746
792
  AND error IS NULL
@@ -768,6 +814,8 @@ export class StateDb {
768
814
  project: "",
769
815
  filePath: r.note_path,
770
816
  embedding: vec,
817
+ embeddingModel: r.embedding_model ?? LEGACY_EMBEDDING_PROVENANCE.model,
818
+ embeddingDim: r.embedding_dim ?? vec.length,
771
819
  });
772
820
  }
773
821
  return out;
@@ -781,8 +829,9 @@ export class StateDb {
781
829
  getTopicEmbeddings(vaultRoot, topicsDir) {
782
830
  if (!this.hasTopicsTable())
783
831
  return [];
832
+ const prov = this.hasProvenance("topics");
784
833
  const rows = this.db
785
- .prepare(`SELECT id, embedding, title
834
+ .prepare(`SELECT id, embedding, title${prov ? ", embedding_model, embedding_dim" : ""}
786
835
  FROM topics
787
836
  WHERE embedding IS NOT NULL`)
788
837
  .all();
@@ -807,6 +856,8 @@ export class StateDb {
807
856
  project: "",
808
857
  filePath: join(vaultRoot, topicsDir, `${r.id}.md`),
809
858
  embedding: vec,
859
+ embeddingModel: r.embedding_model ?? LEGACY_EMBEDDING_PROVENANCE.model,
860
+ embeddingDim: r.embedding_dim ?? vec.length,
810
861
  });
811
862
  }
812
863
  return out;
@@ -869,10 +920,10 @@ export class StateDb {
869
920
  // Future-proofing for v0.7.3 (topic-aware retrieval). Stored now so topics
870
921
  // are searchable then without a backfill; the retriever does not read these
871
922
  // yet — keeping topics out of `vir query` per the plugin-compat contract.
872
- storeTopicEmbedding(id, embedding) {
923
+ storeTopicEmbedding(id, embedding, provenance) {
873
924
  this.db
874
- .prepare("UPDATE topics SET embedding = ? WHERE id = ?")
875
- .run(JSON.stringify(embedding), id);
925
+ .prepare("UPDATE topics SET embedding = ?, embedding_model = ?, embedding_dim = ? WHERE id = ?")
926
+ .run(JSON.stringify(embedding), provenance.model, provenance.dim, id);
876
927
  }
877
928
  close() {
878
929
  this.db.close();