clawvault 3.2.1 → 3.3.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.
Files changed (150) hide show
  1. package/README.md +56 -16
  2. package/bin/clawvault.js +0 -2
  3. package/bin/command-registration.test.js +13 -1
  4. package/bin/help-contract.test.js +14 -0
  5. package/bin/register-core-commands.js +88 -0
  6. package/bin/register-core-commands.test.js +80 -0
  7. package/bin/register-maintenance-commands.js +57 -6
  8. package/bin/register-query-commands.js +10 -28
  9. package/bin/test-helpers/cli-command-fixtures.js +1 -0
  10. package/dist/chunk-2PKBIKDH.js +130 -0
  11. package/dist/{chunk-U67V476Y.js → chunk-2ZDO52B4.js} +18 -1
  12. package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
  13. package/dist/{chunk-AZYOKJYC.js → chunk-4PY655YM.js} +13 -1
  14. package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
  15. package/dist/{chunk-Y3TIJEBP.js → chunk-7SWP5FKU.js} +34 -613
  16. package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
  17. package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
  18. package/dist/{chunk-4ITRXIVT.js → chunk-BLQXXX7Q.js} +6 -6
  19. package/dist/chunk-CSHO3PJB.js +684 -0
  20. package/dist/{chunk-S5OJEGFG.js → chunk-DOIUYIXV.js} +2 -2
  21. package/dist/{chunk-YXQCA6B7.js → chunk-DVOUSOR3.js} +112 -7
  22. package/dist/{chunk-YDWHS4LJ.js → chunk-ECGJYWNA.js} +205 -33
  23. package/dist/{chunk-QMHPQYUV.js → chunk-EL6UBSX5.js} +7 -6
  24. package/dist/chunk-FZ5I2NF7.js +352 -0
  25. package/dist/{chunk-WJVWINEM.js → chunk-GFCHWMGD.js} +55 -6
  26. package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
  27. package/dist/chunk-H3JZIB5O.js +322 -0
  28. package/dist/chunk-HEHO7SMV.js +51 -0
  29. package/dist/{chunk-UCQAOZHW.js → chunk-HGDDW24U.js} +3 -3
  30. package/dist/chunk-J3YUXVID.js +907 -0
  31. package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
  32. package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
  33. package/dist/{chunk-YNIPYN4F.js → chunk-OFOCU2V4.js} +6 -5
  34. package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
  35. package/dist/chunk-PTWPPVC7.js +972 -0
  36. package/dist/{chunk-FAKNOB7Y.js → chunk-QFWERBDP.js} +2 -2
  37. package/dist/{chunk-IIOU45CK.js → chunk-S7N7HI5E.js} +2 -2
  38. package/dist/{chunk-ECRZL5XR.js → chunk-T7E764W3.js} +23 -7
  39. package/dist/chunk-TDWFBDAQ.js +1016 -0
  40. package/dist/{chunk-MNPUYCHQ.js → chunk-TWMI3SNN.js} +6 -5
  41. package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
  42. package/dist/{chunk-PI4WMLMG.js → chunk-VXAGOLDP.js} +1 -1
  43. package/dist/chunk-YCUVAOFC.js +158 -0
  44. package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
  45. package/dist/chunk-ZKWPCBYT.js +600 -0
  46. package/dist/cli/index.js +27 -21
  47. package/dist/commands/archive.js +3 -3
  48. package/dist/commands/backlog.js +1 -1
  49. package/dist/commands/benchmark.d.ts +12 -0
  50. package/dist/commands/benchmark.js +12 -0
  51. package/dist/commands/blocked.js +1 -1
  52. package/dist/commands/canvas.js +2 -2
  53. package/dist/commands/checkpoint.js +1 -1
  54. package/dist/commands/compat.js +1 -1
  55. package/dist/commands/context.js +8 -7
  56. package/dist/commands/doctor.d.ts +8 -3
  57. package/dist/commands/doctor.js +8 -22
  58. package/dist/commands/embed.js +6 -5
  59. package/dist/commands/entities.js +2 -2
  60. package/dist/commands/graph.js +4 -4
  61. package/dist/commands/inbox.d.ts +23 -0
  62. package/dist/commands/inbox.js +11 -0
  63. package/dist/commands/inject.d.ts +1 -1
  64. package/dist/commands/inject.js +5 -5
  65. package/dist/commands/kanban.js +1 -1
  66. package/dist/commands/link.js +9 -9
  67. package/dist/commands/maintain.d.ts +32 -0
  68. package/dist/commands/maintain.js +12 -0
  69. package/dist/commands/migrate-observations.js +3 -3
  70. package/dist/commands/observe.js +11 -10
  71. package/dist/commands/project.js +2 -2
  72. package/dist/commands/rebuild-embeddings.js +48 -17
  73. package/dist/commands/rebuild.js +9 -8
  74. package/dist/commands/recover.js +1 -1
  75. package/dist/commands/reflect.js +6 -6
  76. package/dist/commands/repair-session.js +1 -1
  77. package/dist/commands/replay.js +10 -9
  78. package/dist/commands/session-recap.js +1 -1
  79. package/dist/commands/setup.js +4 -3
  80. package/dist/commands/shell-init.js +1 -1
  81. package/dist/commands/sleep.d.ts +1 -1
  82. package/dist/commands/sleep.js +20 -18
  83. package/dist/commands/status.js +40 -26
  84. package/dist/commands/sync-bd.js +3 -3
  85. package/dist/commands/tailscale.js +3 -3
  86. package/dist/commands/task.js +1 -1
  87. package/dist/commands/template.js +1 -1
  88. package/dist/commands/wake.d.ts +1 -1
  89. package/dist/commands/wake.js +10 -9
  90. package/dist/index.d.ts +175 -16
  91. package/dist/index.js +277 -108
  92. package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
  93. package/dist/lib/auto-linker.js +2 -2
  94. package/dist/lib/canvas-layout.js +1 -1
  95. package/dist/lib/config.js +2 -2
  96. package/dist/lib/entity-index.js +1 -1
  97. package/dist/lib/project-utils.js +2 -2
  98. package/dist/lib/session-repair.js +1 -1
  99. package/dist/lib/session-utils.js +1 -1
  100. package/dist/lib/tailscale.js +1 -1
  101. package/dist/lib/task-utils.js +1 -1
  102. package/dist/lib/template-engine.js +1 -1
  103. package/dist/lib/webdav.js +1 -1
  104. package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
  105. package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
  106. package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
  107. package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
  108. package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
  109. package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
  110. package/dist/openclaw-plugin.d.ts +8 -0
  111. package/dist/openclaw-plugin.js +14 -0
  112. package/dist/transformers.node-A2ZRORSQ.js +46775 -0
  113. package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
  114. package/hooks/clawvault/HOOK.md +25 -8
  115. package/hooks/clawvault/handler.js +215 -78
  116. package/hooks/clawvault/handler.test.js +109 -43
  117. package/hooks/clawvault/integrity.js +112 -0
  118. package/hooks/clawvault/integrity.test.js +32 -0
  119. package/hooks/clawvault/openclaw.plugin.json +133 -15
  120. package/openclaw.plugin.json +131 -203
  121. package/package.json +10 -7
  122. package/bin/register-workgraph-commands.js +0 -451
  123. package/dist/chunk-5PJ4STIC.js +0 -465
  124. package/dist/chunk-ERNE2FZ5.js +0 -189
  125. package/dist/chunk-HR4KN6S2.js +0 -152
  126. package/dist/chunk-IJBFGPCS.js +0 -33
  127. package/dist/chunk-K7PNYS45.js +0 -93
  128. package/dist/chunk-NTOPJI7W.js +0 -207
  129. package/dist/chunk-PG56HX5T.js +0 -154
  130. package/dist/chunk-QPDDIHXE.js +0 -501
  131. package/dist/chunk-WIOLLGAD.js +0 -190
  132. package/dist/chunk-WMGIIABP.js +0 -15
  133. package/dist/ledger-B7g7jhqG.d.ts +0 -44
  134. package/dist/plugin/index.d.ts +0 -352
  135. package/dist/plugin/index.js +0 -4264
  136. package/dist/registry-BR4326o0.d.ts +0 -30
  137. package/dist/store-CA-6sKCJ.d.ts +0 -34
  138. package/dist/thread-B9LhXNU0.d.ts +0 -41
  139. package/dist/workgraph/index.d.ts +0 -5
  140. package/dist/workgraph/index.js +0 -23
  141. package/dist/workgraph/ledger.d.ts +0 -2
  142. package/dist/workgraph/ledger.js +0 -25
  143. package/dist/workgraph/registry.d.ts +0 -2
  144. package/dist/workgraph/registry.js +0 -19
  145. package/dist/workgraph/store.d.ts +0 -2
  146. package/dist/workgraph/store.js +0 -25
  147. package/dist/workgraph/thread.d.ts +0 -2
  148. package/dist/workgraph/thread.js +0 -25
  149. package/dist/workgraph/types.d.ts +0 -54
  150. package/dist/workgraph/types.js +0 -7
@@ -1,13 +1,16 @@
1
1
  import {
2
2
  buildEntityIndex
3
3
  } from "./chunk-J7ZWCI2C.js";
4
+ import {
5
+ extractRawWikiLinks,
6
+ normalizeWikiLinkTarget
7
+ } from "./chunk-33DOSHTA.js";
4
8
 
5
9
  // src/lib/backlinks.ts
6
10
  import * as fs from "fs";
7
11
  import * as path from "path";
8
12
  var CLAWVAULT_DIR = ".clawvault";
9
13
  var BACKLINKS_FILE = "backlinks.json";
10
- var WIKI_LINK_REGEX = /\[\[([^\]]+)\]\]/g;
11
14
  function ensureClawvaultDir(vaultPath) {
12
15
  const dir = path.join(vaultPath, CLAWVAULT_DIR);
13
16
  if (!fs.existsSync(dir)) {
@@ -20,29 +23,47 @@ function toVaultId(vaultPath, filePath) {
20
23
  return relative2.split(path.sep).join("/");
21
24
  }
22
25
  function normalizeLinkTarget(raw) {
23
- let target = raw.trim();
24
- if (!target) return "";
25
- if (target.startsWith("[[") && target.endsWith("]]")) {
26
- target = target.slice(2, -2);
27
- }
28
- const pipeIndex = target.indexOf("|");
29
- if (pipeIndex !== -1) {
30
- target = target.slice(0, pipeIndex);
31
- }
32
- if (target.startsWith("#")) return "";
33
- const hashIndex = target.indexOf("#");
34
- if (hashIndex !== -1) {
35
- target = target.slice(0, hashIndex);
26
+ return normalizeWikiLinkTarget(raw);
27
+ }
28
+ function normalizeLookupCandidate(value) {
29
+ const normalized = normalizeLinkTarget(value);
30
+ if (!normalized) return "";
31
+ const resolved = path.posix.normalize(normalized).replace(/^\/+/, "");
32
+ if (!resolved || resolved === "." || resolved.startsWith("../")) {
33
+ return "";
36
34
  }
37
- target = target.trim();
38
- if (!target) return "";
39
- if (target.endsWith(".md")) {
40
- target = target.slice(0, -3);
35
+ return resolved;
36
+ }
37
+ function buildLookupCandidates(target, sourceId) {
38
+ const candidates = [];
39
+ const sourceDir = path.posix.dirname(sourceId);
40
+ const hasSourceDir = sourceDir !== ".";
41
+ const isRelativeTarget = target.startsWith("./") || target.startsWith("../");
42
+ const addCandidate = (candidate) => {
43
+ const normalized = normalizeLookupCandidate(candidate);
44
+ if (!normalized || candidates.includes(normalized)) return;
45
+ candidates.push(normalized);
46
+ };
47
+ if (isRelativeTarget) {
48
+ if (hasSourceDir) {
49
+ addCandidate(path.posix.join(sourceDir, target));
50
+ } else {
51
+ addCandidate(target);
52
+ }
53
+ if (target.startsWith("./")) {
54
+ addCandidate(target.slice(2));
55
+ }
56
+ return candidates;
41
57
  }
42
- if (target.startsWith("/")) {
43
- target = target.slice(1);
58
+ if (!target.includes("/")) {
59
+ if (hasSourceDir) {
60
+ addCandidate(`${sourceDir}/${target}`);
61
+ }
62
+ addCandidate(target);
63
+ return candidates;
44
64
  }
45
- return target.replace(/\\/g, "/");
65
+ addCandidate(target);
66
+ return candidates;
46
67
  }
47
68
  function listMarkdownFiles(vaultPath) {
48
69
  const files = [];
@@ -75,11 +96,16 @@ function buildKnownIds(vaultPath, files) {
75
96
  }
76
97
  return { ids, idsLower };
77
98
  }
78
- function resolveTarget(target, known, entityIndex) {
99
+ function resolveTarget(target, sourceId, known, entityIndex) {
79
100
  if (!target) return null;
80
- if (known.ids.has(target)) return target;
101
+ for (const candidate of buildLookupCandidates(target, sourceId)) {
102
+ if (known.ids.has(candidate)) return candidate;
103
+ const lowerCandidate = candidate.toLowerCase();
104
+ if (known.idsLower.has(lowerCandidate)) {
105
+ return known.idsLower.get(lowerCandidate);
106
+ }
107
+ }
81
108
  const lower = target.toLowerCase();
82
- if (known.idsLower.has(lower)) return known.idsLower.get(lower);
83
109
  if (entityIndex?.entries.has(lower)) return entityIndex.entries.get(lower);
84
110
  return null;
85
111
  }
@@ -93,12 +119,12 @@ function scanVaultLinks(vaultPath, options = {}) {
93
119
  for (const file of files) {
94
120
  const sourceId = toVaultId(vaultPath, file);
95
121
  const content = fs.readFileSync(file, "utf-8");
96
- const matches = content.match(WIKI_LINK_REGEX) || [];
122
+ const matches = extractRawWikiLinks(content);
97
123
  linkCount += matches.length;
98
124
  for (const match of matches) {
99
125
  const target = normalizeLinkTarget(match);
100
126
  if (!target) continue;
101
- const resolved = resolveTarget(target, known, entityIndex);
127
+ const resolved = resolveTarget(target, sourceId, known, entityIndex);
102
128
  if (!resolved) {
103
129
  orphans.push({ source: sourceId, target });
104
130
  continue;
@@ -21,10 +21,17 @@ var OBSERVER_COMPRESSION_PROVIDERS = [
21
21
  var THEMES = ["neural", "minimal", "none"];
22
22
  var CONTEXT_PROFILES = ["default", "planning", "incident", "handoff", "auto"];
23
23
  var FACT_EXTRACTION_MODES = ["off", "rule", "llm", "hybrid"];
24
+ var SEARCH_BACKENDS = ["in-process", "qmd"];
25
+ var SEARCH_EMBEDDING_PROVIDERS = ["none", "openai", "gemini", "ollama"];
26
+ var SEARCH_RERANK_PROVIDERS = ["none", "jina", "voyage", "siliconflow", "pinecone"];
27
+ var MODEL_TIERS = ["background", "default", "complex"];
24
28
  var SUPPORTED_CONFIG_KEYS = [
25
29
  "name",
26
30
  "categories",
27
31
  "theme",
32
+ "models.background",
33
+ "models.default",
34
+ "models.complex",
28
35
  "observe.model",
29
36
  "observe.provider",
30
37
  "observer.compression.provider",
@@ -37,7 +44,20 @@ var SUPPORTED_CONFIG_KEYS = [
37
44
  "graph.maxHops",
38
45
  "inject.maxResults",
39
46
  "inject.useLlm",
40
- "inject.scope"
47
+ "inject.scope",
48
+ "search.backend",
49
+ "search.qmdFallback",
50
+ "search.chunkSize",
51
+ "search.chunkOverlap",
52
+ "search.embeddings.provider",
53
+ "search.embeddings.model",
54
+ "search.embeddings.baseUrl",
55
+ "search.embeddings.apiKey",
56
+ "search.rerank.provider",
57
+ "search.rerank.model",
58
+ "search.rerank.endpoint",
59
+ "search.rerank.apiKey",
60
+ "search.rerank.weight"
41
61
  ];
42
62
  var DEFAULT_THEME = "none";
43
63
  var DEFAULT_OBSERVE_MODEL = "gemini-2.0-flash";
@@ -49,6 +69,13 @@ var DEFAULT_GRAPH_MAX_HOPS = 2;
49
69
  var DEFAULT_INJECT_MAX_RESULTS = 8;
50
70
  var DEFAULT_INJECT_USE_LLM = true;
51
71
  var DEFAULT_INJECT_SCOPE = ["global"];
72
+ var DEFAULT_SEARCH_BACKEND = "in-process";
73
+ var DEFAULT_SEARCH_QMD_FALLBACK = true;
74
+ var DEFAULT_SEARCH_CHUNK_SIZE = 700;
75
+ var DEFAULT_SEARCH_CHUNK_OVERLAP = 100;
76
+ var DEFAULT_SEARCH_EMBEDDINGS_PROVIDER = "none";
77
+ var DEFAULT_SEARCH_RERANK_PROVIDER = "none";
78
+ var DEFAULT_SEARCH_RERANK_WEIGHT = 0.6;
52
79
  function configPathFor(vaultPath) {
53
80
  return path.join(path.resolve(vaultPath), CONFIG_FILE);
54
81
  }
@@ -93,6 +120,18 @@ function asPositiveInteger(value) {
93
120
  }
94
121
  return null;
95
122
  }
123
+ function asFiniteNumber(value) {
124
+ if (typeof value === "number" && Number.isFinite(value)) {
125
+ return value;
126
+ }
127
+ if (typeof value === "string") {
128
+ const parsed = Number.parseFloat(value);
129
+ if (Number.isFinite(parsed)) {
130
+ return parsed;
131
+ }
132
+ }
133
+ return null;
134
+ }
96
135
  function asBoolean(value) {
97
136
  if (typeof value === "boolean") {
98
137
  return value;
@@ -123,6 +162,15 @@ function isContextProfile(value) {
123
162
  function isFactExtractionMode(value) {
124
163
  return typeof value === "string" && FACT_EXTRACTION_MODES.includes(value);
125
164
  }
165
+ function isSearchBackend(value) {
166
+ return typeof value === "string" && SEARCH_BACKENDS.includes(value);
167
+ }
168
+ function isSearchEmbeddingProvider(value) {
169
+ return typeof value === "string" && SEARCH_EMBEDDING_PROVIDERS.includes(value);
170
+ }
171
+ function isSearchRerankProvider(value) {
172
+ return typeof value === "string" && SEARCH_RERANK_PROVIDERS.includes(value);
173
+ }
126
174
  function normalizeRouteTarget(target) {
127
175
  const trimmed = target.trim().replace(/^\/+/, "").replace(/\/+$/, "");
128
176
  if (!trimmed) {
@@ -201,6 +249,7 @@ function withDefaults(vaultPath, config) {
201
249
  name: path.basename(resolvedPath),
202
250
  categories: [...DEFAULT_CATEGORIES],
203
251
  theme: DEFAULT_THEME,
252
+ models: {},
204
253
  observe: {
205
254
  model: DEFAULT_OBSERVE_MODEL,
206
255
  provider: DEFAULT_OBSERVE_PROVIDER
@@ -221,9 +270,30 @@ function withDefaults(vaultPath, config) {
221
270
  useLlm: DEFAULT_INJECT_USE_LLM,
222
271
  scope: [...DEFAULT_INJECT_SCOPE]
223
272
  },
273
+ search: {
274
+ backend: DEFAULT_SEARCH_BACKEND,
275
+ qmdFallback: DEFAULT_SEARCH_QMD_FALLBACK,
276
+ chunkSize: DEFAULT_SEARCH_CHUNK_SIZE,
277
+ chunkOverlap: DEFAULT_SEARCH_CHUNK_OVERLAP,
278
+ embeddings: {
279
+ provider: DEFAULT_SEARCH_EMBEDDINGS_PROVIDER
280
+ },
281
+ rerank: {
282
+ provider: DEFAULT_SEARCH_RERANK_PROVIDER,
283
+ weight: DEFAULT_SEARCH_RERANK_WEIGHT
284
+ }
285
+ },
224
286
  routes: []
225
287
  };
226
288
  const observeRecord = config.observe && typeof config.observe === "object" && !Array.isArray(config.observe) ? config.observe : {};
289
+ const modelsRecord = config.models && typeof config.models === "object" && !Array.isArray(config.models) ? config.models : {};
290
+ const normalizedModels = {};
291
+ for (const tier of MODEL_TIERS) {
292
+ const candidate = modelsRecord[tier];
293
+ if (typeof candidate === "string" && candidate.trim()) {
294
+ normalizedModels[tier] = candidate.trim();
295
+ }
296
+ }
227
297
  const contextRecord = config.context && typeof config.context === "object" && !Array.isArray(config.context) ? config.context : {};
228
298
  const observerRecord = config.observer && typeof config.observer === "object" && !Array.isArray(config.observer) ? config.observer : {};
229
299
  const compressionRecord = observerRecord.compression && typeof observerRecord.compression === "object" && !Array.isArray(observerRecord.compression) ? observerRecord.compression : {};
@@ -246,11 +316,15 @@ function withDefaults(vaultPath, config) {
246
316
  normalizedCompression.apiKey = compressionApiKey;
247
317
  }
248
318
  const injectRecord = config.inject && typeof config.inject === "object" && !Array.isArray(config.inject) ? config.inject : {};
319
+ const searchRecord = config.search && typeof config.search === "object" && !Array.isArray(config.search) ? config.search : {};
320
+ const searchEmbeddingsRecord = searchRecord.embeddings && typeof searchRecord.embeddings === "object" && !Array.isArray(searchRecord.embeddings) ? searchRecord.embeddings : {};
321
+ const searchRerankRecord = searchRecord.rerank && typeof searchRecord.rerank === "object" && !Array.isArray(searchRecord.rerank) ? searchRecord.rerank : {};
249
322
  return {
250
323
  ...config,
251
324
  name: typeof config.name === "string" && config.name.trim() ? config.name.trim() : defaults.name,
252
325
  categories: asStringArray(config.categories) ?? defaults.categories,
253
326
  theme: isTheme(config.theme) ? config.theme : defaults.theme,
327
+ models: normalizedModels,
254
328
  observe: {
255
329
  ...observeRecord,
256
330
  model: typeof observeRecord.model === "string" && observeRecord.model.trim() ? observeRecord.model.trim() : defaults.observe.model,
@@ -276,6 +350,32 @@ function withDefaults(vaultPath, config) {
276
350
  useLlm: asBoolean(injectRecord.useLlm) ?? defaults.inject.useLlm,
277
351
  scope: asStringArray(injectRecord.scope) ?? (typeof injectRecord.scope === "string" ? injectRecord.scope.split(",").map((entry) => entry.trim()).filter(Boolean) : null) ?? [...defaults.inject.scope]
278
352
  },
353
+ search: {
354
+ ...searchRecord,
355
+ backend: isSearchBackend(searchRecord.backend) ? searchRecord.backend : defaults.search.backend,
356
+ qmdFallback: asBoolean(searchRecord.qmdFallback) ?? defaults.search.qmdFallback,
357
+ chunkSize: asPositiveInteger(searchRecord.chunkSize) ?? defaults.search.chunkSize,
358
+ chunkOverlap: asPositiveInteger(searchRecord.chunkOverlap) ?? defaults.search.chunkOverlap,
359
+ embeddings: {
360
+ ...searchEmbeddingsRecord,
361
+ provider: isSearchEmbeddingProvider(searchEmbeddingsRecord.provider) ? searchEmbeddingsRecord.provider : defaults.search.embeddings.provider,
362
+ model: typeof searchEmbeddingsRecord.model === "string" && searchEmbeddingsRecord.model.trim() ? searchEmbeddingsRecord.model.trim() : void 0,
363
+ baseUrl: typeof searchEmbeddingsRecord.baseUrl === "string" && searchEmbeddingsRecord.baseUrl.trim() ? searchEmbeddingsRecord.baseUrl.trim() : void 0,
364
+ apiKey: typeof searchEmbeddingsRecord.apiKey === "string" && searchEmbeddingsRecord.apiKey.trim() ? searchEmbeddingsRecord.apiKey.trim() : void 0
365
+ },
366
+ rerank: {
367
+ ...searchRerankRecord,
368
+ provider: isSearchRerankProvider(searchRerankRecord.provider) ? searchRerankRecord.provider : defaults.search.rerank.provider,
369
+ model: typeof searchRerankRecord.model === "string" && searchRerankRecord.model.trim() ? searchRerankRecord.model.trim() : void 0,
370
+ endpoint: typeof searchRerankRecord.endpoint === "string" && searchRerankRecord.endpoint.trim() ? searchRerankRecord.endpoint.trim() : void 0,
371
+ apiKey: typeof searchRerankRecord.apiKey === "string" && searchRerankRecord.apiKey.trim() ? searchRerankRecord.apiKey.trim() : void 0,
372
+ weight: (() => {
373
+ const parsed = asFiniteNumber(searchRerankRecord.weight);
374
+ if (parsed === null) return defaults.search.rerank.weight;
375
+ return Math.max(0, Math.min(1, parsed));
376
+ })()
377
+ }
378
+ },
279
379
  routes: normalizeRoutes(config.routes)
280
380
  };
281
381
  }
@@ -309,6 +409,12 @@ function coerceManagedValue(key, value) {
309
409
  }
310
410
  return value;
311
411
  }
412
+ if (key === "models.background" || key === "models.default" || key === "models.complex") {
413
+ if (typeof value !== "string" || !value.trim()) {
414
+ throw new Error(`Config key "${key}" must be a non-empty string.`);
415
+ }
416
+ return value.trim();
417
+ }
312
418
  if (key === "observe.provider") {
313
419
  if (!isObserveProvider(value)) {
314
420
  throw new Error(`Config key "observe.provider" must be one of: ${OBSERVE_PROVIDERS.join(", ")}`);
@@ -394,6 +500,59 @@ function coerceManagedValue(key, value) {
394
500
  }
395
501
  return normalized;
396
502
  }
503
+ if (key === "search.backend") {
504
+ if (!isSearchBackend(value)) {
505
+ throw new Error(`Config key "search.backend" must be one of: ${SEARCH_BACKENDS.join(", ")}`);
506
+ }
507
+ return value;
508
+ }
509
+ if (key === "search.qmdFallback") {
510
+ const parsed = asBoolean(value);
511
+ if (parsed === null) {
512
+ throw new Error('Config key "search.qmdFallback" must be a boolean.');
513
+ }
514
+ return parsed;
515
+ }
516
+ if (key === "search.chunkSize" || key === "search.chunkOverlap") {
517
+ const parsed = asPositiveInteger(value);
518
+ if (parsed === null) {
519
+ throw new Error(`Config key "${key}" must be a positive integer.`);
520
+ }
521
+ return parsed;
522
+ }
523
+ if (key === "search.embeddings.provider") {
524
+ if (!isSearchEmbeddingProvider(value)) {
525
+ throw new Error(
526
+ `Config key "search.embeddings.provider" must be one of: ${SEARCH_EMBEDDING_PROVIDERS.join(", ")}`
527
+ );
528
+ }
529
+ return value;
530
+ }
531
+ if (key === "search.embeddings.model" || key === "search.embeddings.baseUrl" || key === "search.rerank.model" || key === "search.rerank.endpoint") {
532
+ if (typeof value !== "string" || !value.trim()) {
533
+ throw new Error(`Config key "${key}" must be a non-empty string.`);
534
+ }
535
+ return value.trim();
536
+ }
537
+ if (key === "search.embeddings.apiKey" || key === "search.rerank.apiKey") {
538
+ if (typeof value !== "string") {
539
+ throw new Error(`Config key "${key}" must be a string.`);
540
+ }
541
+ return value.trim();
542
+ }
543
+ if (key === "search.rerank.provider") {
544
+ if (!isSearchRerankProvider(value)) {
545
+ throw new Error(`Config key "search.rerank.provider" must be one of: ${SEARCH_RERANK_PROVIDERS.join(", ")}`);
546
+ }
547
+ return value;
548
+ }
549
+ if (key === "search.rerank.weight") {
550
+ const parsed = asFiniteNumber(value);
551
+ if (parsed === null || parsed < 0 || parsed > 1) {
552
+ throw new Error('Config key "search.rerank.weight" must be a number between 0 and 1.');
553
+ }
554
+ return parsed;
555
+ }
397
556
  throw new Error(`Unsupported config key: ${key}`);
398
557
  }
399
558
  function toComparablePattern(pattern) {
@@ -435,6 +594,7 @@ function resetConfig(vaultPath) {
435
594
  document.name = defaultName;
436
595
  document.categories = [...DEFAULT_CATEGORIES];
437
596
  document.theme = DEFAULT_THEME;
597
+ document.models = {};
438
598
  document.observe = {
439
599
  model: DEFAULT_OBSERVE_MODEL,
440
600
  provider: DEFAULT_OBSERVE_PROVIDER
@@ -457,6 +617,19 @@ function resetConfig(vaultPath) {
457
617
  useLlm: DEFAULT_INJECT_USE_LLM,
458
618
  scope: [...DEFAULT_INJECT_SCOPE]
459
619
  };
620
+ document.search = {
621
+ backend: DEFAULT_SEARCH_BACKEND,
622
+ qmdFallback: DEFAULT_SEARCH_QMD_FALLBACK,
623
+ chunkSize: DEFAULT_SEARCH_CHUNK_SIZE,
624
+ chunkOverlap: DEFAULT_SEARCH_CHUNK_OVERLAP,
625
+ embeddings: {
626
+ provider: DEFAULT_SEARCH_EMBEDDINGS_PROVIDER
627
+ },
628
+ rerank: {
629
+ provider: DEFAULT_SEARCH_RERANK_PROVIDER,
630
+ weight: DEFAULT_SEARCH_RERANK_WEIGHT
631
+ }
632
+ };
460
633
  document.routes = [];
461
634
  if (typeof document.lastUpdated === "string") {
462
635
  document.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
@@ -1,15 +1,15 @@
1
- import {
2
- parseSessionFile
3
- } from "./chunk-P5EPF6MB.js";
4
1
  import {
5
2
  observeActiveSessions
6
- } from "./chunk-PI4WMLMG.js";
3
+ } from "./chunk-VXAGOLDP.js";
7
4
  import {
8
5
  Observer
9
- } from "./chunk-Y3TIJEBP.js";
6
+ } from "./chunk-7SWP5FKU.js";
10
7
  import {
11
8
  resolveVaultPath
12
- } from "./chunk-GNJL4YGR.js";
9
+ } from "./chunk-GJO3CFUN.js";
10
+ import {
11
+ parseSessionFile
12
+ } from "./chunk-MW5C6ZQA.js";
13
13
  import {
14
14
  getObservationPath
15
15
  } from "./chunk-Z2XBWN7A.js";