sensemaking 0.18.3 → 0.19.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 (368) hide show
  1. package/README.md +22 -4
  2. package/dist/cjs/chunk/group.js +10 -23
  3. package/dist/cjs/chunk/group.js.map +1 -1
  4. package/dist/cjs/chunk/parse.js +2 -5
  5. package/dist/cjs/chunk/parse.js.map +1 -1
  6. package/dist/cjs/cli/index.js.map +1 -1
  7. package/dist/cjs/cli/shared.js +6 -10
  8. package/dist/cjs/cli/shared.js.map +1 -1
  9. package/dist/cjs/cli/status.js +44 -10
  10. package/dist/cjs/cli/status.js.map +1 -1
  11. package/dist/cjs/cli.js +2 -8
  12. package/dist/cjs/cli.js.map +1 -1
  13. package/dist/cjs/commands/map.js +1 -3
  14. package/dist/cjs/commands/map.js.map +1 -1
  15. package/dist/cjs/commands/peek.js.map +1 -1
  16. package/dist/cjs/commands/related.js +3 -5
  17. package/dist/cjs/commands/related.js.map +1 -1
  18. package/dist/cjs/commands/scope.js.map +1 -1
  19. package/dist/cjs/commands/search.js +6 -8
  20. package/dist/cjs/commands/search.js.map +1 -1
  21. package/dist/cjs/commands/signals.js.map +1 -1
  22. package/dist/cjs/config/access.js.map +1 -1
  23. package/dist/cjs/config/index.d.cts +1 -1
  24. package/dist/cjs/config/index.d.ts +1 -1
  25. package/dist/cjs/config/index.js +3 -0
  26. package/dist/cjs/config/index.js.map +1 -1
  27. package/dist/cjs/config/load.js +10 -24
  28. package/dist/cjs/config/load.js.map +1 -1
  29. package/dist/cjs/config/resolve.js.map +1 -1
  30. package/dist/cjs/config/signals.js +2 -3
  31. package/dist/cjs/config/signals.js.map +1 -1
  32. package/dist/cjs/config/types.d.cts +2 -1
  33. package/dist/cjs/config/types.d.ts +2 -1
  34. package/dist/cjs/config/types.js +8 -0
  35. package/dist/cjs/config/types.js.map +1 -1
  36. package/dist/cjs/config/validate.d.cts +1 -1
  37. package/dist/cjs/config/validate.d.ts +1 -1
  38. package/dist/cjs/config/validate.js +9 -10
  39. package/dist/cjs/config/validate.js.map +1 -1
  40. package/dist/cjs/embed/cohere.js +2 -2
  41. package/dist/cjs/embed/cohere.js.map +1 -1
  42. package/dist/cjs/embed/distribution.js +3 -3
  43. package/dist/cjs/embed/distribution.js.map +1 -1
  44. package/dist/cjs/embed/handoff.d.cts +3 -0
  45. package/dist/cjs/embed/handoff.d.ts +3 -0
  46. package/dist/cjs/embed/handoff.js +39 -0
  47. package/dist/cjs/embed/handoff.js.map +1 -0
  48. package/dist/cjs/embed/identity.d.cts +11 -10
  49. package/dist/cjs/embed/identity.d.ts +11 -10
  50. package/dist/cjs/embed/identity.js +30 -15
  51. package/dist/cjs/embed/identity.js.map +1 -1
  52. package/dist/cjs/embed/langfit.js +2 -3
  53. package/dist/cjs/embed/langfit.js.map +1 -1
  54. package/dist/cjs/embed/languages.js +2 -4
  55. package/dist/cjs/embed/languages.js.map +1 -1
  56. package/dist/cjs/embed/openai.js +2 -2
  57. package/dist/cjs/embed/openai.js.map +1 -1
  58. package/dist/cjs/embed/query.js +58 -20
  59. package/dist/cjs/embed/query.js.map +1 -1
  60. package/dist/cjs/embed/registry.js +1 -2
  61. package/dist/cjs/embed/registry.js.map +1 -1
  62. package/dist/cjs/embed/static.d.cts +1 -1
  63. package/dist/cjs/embed/static.d.ts +1 -1
  64. package/dist/cjs/embed/static.js +8 -10
  65. package/dist/cjs/embed/static.js.map +1 -1
  66. package/dist/cjs/embed/store.d.cts +2 -2
  67. package/dist/cjs/embed/store.d.ts +2 -2
  68. package/dist/cjs/embed/store.js +48 -34
  69. package/dist/cjs/embed/store.js.map +1 -1
  70. package/dist/cjs/embed/types.js +1 -3
  71. package/dist/cjs/embed/types.js.map +1 -1
  72. package/dist/cjs/errors.d.cts +1 -1
  73. package/dist/cjs/errors.d.ts +1 -1
  74. package/dist/cjs/errors.js.map +1 -1
  75. package/dist/cjs/features/embed.js +19 -17
  76. package/dist/cjs/features/embed.js.map +1 -1
  77. package/dist/cjs/features/fences.js +5 -10
  78. package/dist/cjs/features/fences.js.map +1 -1
  79. package/dist/cjs/features/links.js +20 -37
  80. package/dist/cjs/features/links.js.map +1 -1
  81. package/dist/cjs/features/sections.js +21 -19
  82. package/dist/cjs/features/sections.js.map +1 -1
  83. package/dist/cjs/features/tags.js +5 -8
  84. package/dist/cjs/features/tags.js.map +1 -1
  85. package/dist/cjs/graph/traverse.js +1 -2
  86. package/dist/cjs/graph/traverse.js.map +1 -1
  87. package/dist/cjs/index.d.cts +1 -1
  88. package/dist/cjs/index.d.ts +1 -1
  89. package/dist/cjs/index.js +3 -0
  90. package/dist/cjs/index.js.map +1 -1
  91. package/dist/cjs/output/column-hint.js +3 -6
  92. package/dist/cjs/output/column-hint.js.map +1 -1
  93. package/dist/cjs/output/output.js +7 -14
  94. package/dist/cjs/output/output.js.map +1 -1
  95. package/dist/cjs/output/search-error.js +2 -5
  96. package/dist/cjs/output/search-error.js.map +1 -1
  97. package/dist/cjs/scan/frontmatter.d.cts +1 -0
  98. package/dist/cjs/scan/frontmatter.d.ts +1 -0
  99. package/dist/cjs/scan/frontmatter.js +14 -18
  100. package/dist/cjs/scan/frontmatter.js.map +1 -1
  101. package/dist/cjs/scan/list.js +2 -3
  102. package/dist/cjs/scan/list.js.map +1 -1
  103. package/dist/cjs/scan/reparse.js +12 -26
  104. package/dist/cjs/scan/reparse.js.map +1 -1
  105. package/dist/cjs/scan/worker-error.js.map +1 -1
  106. package/dist/cjs/store/cache.d.cts +2 -0
  107. package/dist/cjs/store/cache.d.ts +2 -0
  108. package/dist/cjs/store/cache.js +20 -0
  109. package/dist/cjs/store/cache.js.map +1 -0
  110. package/dist/cjs/store/duckdb/batch.js +4 -16
  111. package/dist/cjs/store/duckdb/batch.js.map +1 -1
  112. package/dist/cjs/store/duckdb/connection.js +8 -20
  113. package/dist/cjs/store/duckdb/connection.js.map +1 -1
  114. package/dist/cjs/store/duckdb/fieldStats.js +2 -8
  115. package/dist/cjs/store/duckdb/fieldStats.js.map +1 -1
  116. package/dist/cjs/store/duckdb/lexical.js +21 -29
  117. package/dist/cjs/store/duckdb/lexical.js.map +1 -1
  118. package/dist/cjs/store/duckdb/native.js.map +1 -1
  119. package/dist/cjs/store/duckdb/open.js +12 -13
  120. package/dist/cjs/store/duckdb/open.js.map +1 -1
  121. package/dist/cjs/store/duckdb/reconcile.js +4 -11
  122. package/dist/cjs/store/duckdb/reconcile.js.map +1 -1
  123. package/dist/cjs/store/duckdb/sql-functions.js +2 -4
  124. package/dist/cjs/store/duckdb/sql-functions.js.map +1 -1
  125. package/dist/cjs/store/duckdb/store.js +2 -3
  126. package/dist/cjs/store/duckdb/store.js.map +1 -1
  127. package/dist/cjs/store/duckdb/vectors.js +8 -18
  128. package/dist/cjs/store/duckdb/vectors.js.map +1 -1
  129. package/dist/cjs/store/index.d.cts +3 -2
  130. package/dist/cjs/store/index.d.ts +3 -2
  131. package/dist/cjs/store/index.js +7 -8
  132. package/dist/cjs/store/index.js.map +1 -1
  133. package/dist/cjs/store/native.js +6 -16
  134. package/dist/cjs/store/native.js.map +1 -1
  135. package/dist/cjs/store/sql-functions.js.map +1 -1
  136. package/dist/cjs/store/sqlite/connection.js +4 -8
  137. package/dist/cjs/store/sqlite/connection.js.map +1 -1
  138. package/dist/cjs/store/sqlite/fieldStats.js.map +1 -1
  139. package/dist/cjs/store/sqlite/lexical.js +3 -5
  140. package/dist/cjs/store/sqlite/lexical.js.map +1 -1
  141. package/dist/cjs/store/sqlite/open.d.cts +0 -1
  142. package/dist/cjs/store/sqlite/open.d.ts +0 -1
  143. package/dist/cjs/store/sqlite/open.js +22 -40
  144. package/dist/cjs/store/sqlite/open.js.map +1 -1
  145. package/dist/cjs/store/sqlite/reconcile.js +2 -3
  146. package/dist/cjs/store/sqlite/reconcile.js.map +1 -1
  147. package/dist/cjs/store/sqlite/sql-functions.js +2 -4
  148. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -1
  149. package/dist/cjs/store/sqlite/store.js.map +1 -1
  150. package/dist/cjs/store/transaction.js +1 -3
  151. package/dist/cjs/store/transaction.js.map +1 -1
  152. package/dist/cjs/store/turso/connection.js +2 -6
  153. package/dist/cjs/store/turso/connection.js.map +1 -1
  154. package/dist/cjs/store/turso/fieldStats.js.map +1 -1
  155. package/dist/cjs/store/turso/lexical.d.cts +2 -0
  156. package/dist/cjs/store/turso/lexical.d.ts +2 -0
  157. package/dist/cjs/store/turso/lexical.js +347 -0
  158. package/dist/cjs/store/turso/lexical.js.map +1 -0
  159. package/dist/cjs/store/turso/native.js.map +1 -1
  160. package/dist/cjs/store/turso/open.d.cts +1 -1
  161. package/dist/cjs/store/turso/open.d.ts +1 -1
  162. package/dist/cjs/store/turso/open.js +81 -54
  163. package/dist/cjs/store/turso/open.js.map +1 -1
  164. package/dist/cjs/store/turso/reconcile.js +22 -22
  165. package/dist/cjs/store/turso/reconcile.js.map +1 -1
  166. package/dist/cjs/store/turso/store.js +18 -41
  167. package/dist/cjs/store/turso/store.js.map +1 -1
  168. package/dist/cjs/store/turso/vectors.d.cts +8 -0
  169. package/dist/cjs/store/turso/vectors.d.ts +8 -0
  170. package/dist/cjs/store/turso/vectors.js +435 -0
  171. package/dist/cjs/store/turso/vectors.js.map +1 -0
  172. package/dist/cjs/store/types.js +2 -3
  173. package/dist/cjs/store/types.js.map +1 -1
  174. package/dist/cjs/store/vectors.js.map +1 -1
  175. package/dist/cjs/text/segment.d.cts +1 -0
  176. package/dist/cjs/text/segment.d.ts +1 -0
  177. package/dist/cjs/text/segment.js +10 -14
  178. package/dist/cjs/text/segment.js.map +1 -1
  179. package/dist/cjs/watch.js +2 -5
  180. package/dist/cjs/watch.js.map +1 -1
  181. package/dist/cjs/workers/parse.js.map +1 -1
  182. package/dist/esm/chunk/group.js +10 -23
  183. package/dist/esm/chunk/group.js.map +1 -1
  184. package/dist/esm/chunk/parse.js +4 -8
  185. package/dist/esm/chunk/parse.js.map +1 -1
  186. package/dist/esm/cli/index.js +3 -5
  187. package/dist/esm/cli/index.js.map +1 -1
  188. package/dist/esm/cli/shared.js +12 -22
  189. package/dist/esm/cli/shared.js.map +1 -1
  190. package/dist/esm/cli/status.js +43 -9
  191. package/dist/esm/cli/status.js.map +1 -1
  192. package/dist/esm/cli.js +2 -8
  193. package/dist/esm/cli.js.map +1 -1
  194. package/dist/esm/commands/map.js +1 -3
  195. package/dist/esm/commands/map.js.map +1 -1
  196. package/dist/esm/commands/peek.js.map +1 -1
  197. package/dist/esm/commands/related.js +3 -5
  198. package/dist/esm/commands/related.js.map +1 -1
  199. package/dist/esm/commands/scope.js +3 -5
  200. package/dist/esm/commands/scope.js.map +1 -1
  201. package/dist/esm/commands/search.js +10 -16
  202. package/dist/esm/commands/search.js.map +1 -1
  203. package/dist/esm/commands/signals.js +3 -7
  204. package/dist/esm/commands/signals.js.map +1 -1
  205. package/dist/esm/config/access.js +8 -13
  206. package/dist/esm/config/access.js.map +1 -1
  207. package/dist/esm/config/index.d.ts +1 -1
  208. package/dist/esm/config/index.js +1 -1
  209. package/dist/esm/config/index.js.map +1 -1
  210. package/dist/esm/config/load.js +10 -24
  211. package/dist/esm/config/load.js.map +1 -1
  212. package/dist/esm/config/resolve.js +2 -3
  213. package/dist/esm/config/resolve.js.map +1 -1
  214. package/dist/esm/config/signals.js +3 -5
  215. package/dist/esm/config/signals.js.map +1 -1
  216. package/dist/esm/config/types.d.ts +2 -1
  217. package/dist/esm/config/types.js +8 -2
  218. package/dist/esm/config/types.js.map +1 -1
  219. package/dist/esm/config/validate.d.ts +1 -1
  220. package/dist/esm/config/validate.js +7 -10
  221. package/dist/esm/config/validate.js.map +1 -1
  222. package/dist/esm/embed/cohere.js +2 -2
  223. package/dist/esm/embed/cohere.js.map +1 -1
  224. package/dist/esm/embed/distribution.js +1 -1
  225. package/dist/esm/embed/distribution.js.map +1 -1
  226. package/dist/esm/embed/handoff.d.ts +3 -0
  227. package/dist/esm/embed/handoff.js +21 -0
  228. package/dist/esm/embed/handoff.js.map +1 -0
  229. package/dist/esm/embed/identity.d.ts +11 -10
  230. package/dist/esm/embed/identity.js +29 -26
  231. package/dist/esm/embed/identity.js.map +1 -1
  232. package/dist/esm/embed/langfit.js +2 -3
  233. package/dist/esm/embed/langfit.js.map +1 -1
  234. package/dist/esm/embed/languages.js +2 -4
  235. package/dist/esm/embed/languages.js.map +1 -1
  236. package/dist/esm/embed/openai.js +2 -2
  237. package/dist/esm/embed/openai.js.map +1 -1
  238. package/dist/esm/embed/query.js +21 -6
  239. package/dist/esm/embed/query.js.map +1 -1
  240. package/dist/esm/embed/registry.js +1 -2
  241. package/dist/esm/embed/registry.js.map +1 -1
  242. package/dist/esm/embed/static.d.ts +1 -1
  243. package/dist/esm/embed/static.js +8 -10
  244. package/dist/esm/embed/static.js.map +1 -1
  245. package/dist/esm/embed/store.d.ts +2 -2
  246. package/dist/esm/embed/store.js +28 -23
  247. package/dist/esm/embed/store.js.map +1 -1
  248. package/dist/esm/embed/types.js +1 -3
  249. package/dist/esm/embed/types.js.map +1 -1
  250. package/dist/esm/errors.d.ts +1 -1
  251. package/dist/esm/errors.js.map +1 -1
  252. package/dist/esm/features/embed.js +16 -16
  253. package/dist/esm/features/embed.js.map +1 -1
  254. package/dist/esm/features/fences.js +9 -19
  255. package/dist/esm/features/fences.js.map +1 -1
  256. package/dist/esm/features/links.js +24 -49
  257. package/dist/esm/features/links.js.map +1 -1
  258. package/dist/esm/features/sections.js +20 -20
  259. package/dist/esm/features/sections.js.map +1 -1
  260. package/dist/esm/features/tags.js +5 -8
  261. package/dist/esm/features/tags.js.map +1 -1
  262. package/dist/esm/features/types.js.map +1 -1
  263. package/dist/esm/graph/traverse.js +1 -2
  264. package/dist/esm/graph/traverse.js.map +1 -1
  265. package/dist/esm/index.d.ts +1 -1
  266. package/dist/esm/index.js +1 -1
  267. package/dist/esm/index.js.map +1 -1
  268. package/dist/esm/output/column-hint.js +3 -6
  269. package/dist/esm/output/column-hint.js.map +1 -1
  270. package/dist/esm/output/output.js +9 -20
  271. package/dist/esm/output/output.js.map +1 -1
  272. package/dist/esm/output/search-error.js +2 -5
  273. package/dist/esm/output/search-error.js.map +1 -1
  274. package/dist/esm/scan/frontmatter.d.ts +1 -0
  275. package/dist/esm/scan/frontmatter.js +13 -18
  276. package/dist/esm/scan/frontmatter.js.map +1 -1
  277. package/dist/esm/scan/list.js +2 -3
  278. package/dist/esm/scan/list.js.map +1 -1
  279. package/dist/esm/scan/reparse.js +16 -34
  280. package/dist/esm/scan/reparse.js.map +1 -1
  281. package/dist/esm/scan/worker-error.js.map +1 -1
  282. package/dist/esm/store/cache.d.ts +2 -0
  283. package/dist/esm/store/cache.js +11 -0
  284. package/dist/esm/store/cache.js.map +1 -0
  285. package/dist/esm/store/duckdb/batch.js +4 -16
  286. package/dist/esm/store/duckdb/batch.js.map +1 -1
  287. package/dist/esm/store/duckdb/connection.js +8 -20
  288. package/dist/esm/store/duckdb/connection.js.map +1 -1
  289. package/dist/esm/store/duckdb/fieldStats.js +4 -12
  290. package/dist/esm/store/duckdb/fieldStats.js.map +1 -1
  291. package/dist/esm/store/duckdb/lexical.js +29 -36
  292. package/dist/esm/store/duckdb/lexical.js.map +1 -1
  293. package/dist/esm/store/duckdb/native.js +2 -7
  294. package/dist/esm/store/duckdb/native.js.map +1 -1
  295. package/dist/esm/store/duckdb/open.js +16 -23
  296. package/dist/esm/store/duckdb/open.js.map +1 -1
  297. package/dist/esm/store/duckdb/reconcile.js +4 -11
  298. package/dist/esm/store/duckdb/reconcile.js.map +1 -1
  299. package/dist/esm/store/duckdb/sql-functions.js +4 -9
  300. package/dist/esm/store/duckdb/sql-functions.js.map +1 -1
  301. package/dist/esm/store/duckdb/store.js +6 -14
  302. package/dist/esm/store/duckdb/store.js.map +1 -1
  303. package/dist/esm/store/duckdb/vectors.js +14 -31
  304. package/dist/esm/store/duckdb/vectors.js.map +1 -1
  305. package/dist/esm/store/index.d.ts +3 -2
  306. package/dist/esm/store/index.js +5 -6
  307. package/dist/esm/store/index.js.map +1 -1
  308. package/dist/esm/store/native.js +10 -25
  309. package/dist/esm/store/native.js.map +1 -1
  310. package/dist/esm/store/sql-functions.js +3 -5
  311. package/dist/esm/store/sql-functions.js.map +1 -1
  312. package/dist/esm/store/sqlite/connection.js +4 -8
  313. package/dist/esm/store/sqlite/connection.js.map +1 -1
  314. package/dist/esm/store/sqlite/fieldStats.js +2 -6
  315. package/dist/esm/store/sqlite/fieldStats.js.map +1 -1
  316. package/dist/esm/store/sqlite/lexical.js +5 -9
  317. package/dist/esm/store/sqlite/lexical.js.map +1 -1
  318. package/dist/esm/store/sqlite/open.d.ts +0 -1
  319. package/dist/esm/store/sqlite/open.js +21 -41
  320. package/dist/esm/store/sqlite/open.js.map +1 -1
  321. package/dist/esm/store/sqlite/reconcile.js +6 -13
  322. package/dist/esm/store/sqlite/reconcile.js.map +1 -1
  323. package/dist/esm/store/sqlite/sql-functions.js +2 -4
  324. package/dist/esm/store/sqlite/sql-functions.js.map +1 -1
  325. package/dist/esm/store/sqlite/store.js +1 -2
  326. package/dist/esm/store/sqlite/store.js.map +1 -1
  327. package/dist/esm/store/transaction.js +2 -5
  328. package/dist/esm/store/transaction.js.map +1 -1
  329. package/dist/esm/store/turso/connection.js +2 -6
  330. package/dist/esm/store/turso/connection.js.map +1 -1
  331. package/dist/esm/store/turso/fieldStats.js +2 -5
  332. package/dist/esm/store/turso/fieldStats.js.map +1 -1
  333. package/dist/esm/store/turso/lexical.d.ts +2 -0
  334. package/dist/esm/store/turso/lexical.js +128 -0
  335. package/dist/esm/store/turso/lexical.js.map +1 -0
  336. package/dist/esm/store/turso/native.js +2 -7
  337. package/dist/esm/store/turso/native.js.map +1 -1
  338. package/dist/esm/store/turso/open.d.ts +1 -1
  339. package/dist/esm/store/turso/open.js +36 -29
  340. package/dist/esm/store/turso/open.js.map +1 -1
  341. package/dist/esm/store/turso/reconcile.js +22 -22
  342. package/dist/esm/store/turso/reconcile.js.map +1 -1
  343. package/dist/esm/store/turso/store.js +17 -28
  344. package/dist/esm/store/turso/store.js.map +1 -1
  345. package/dist/esm/store/turso/vectors.d.ts +8 -0
  346. package/dist/esm/store/turso/vectors.js +98 -0
  347. package/dist/esm/store/turso/vectors.js.map +1 -0
  348. package/dist/esm/store/types.js +2 -3
  349. package/dist/esm/store/types.js.map +1 -1
  350. package/dist/esm/store/vectors.js +2 -3
  351. package/dist/esm/store/vectors.js.map +1 -1
  352. package/dist/esm/text/segment.d.ts +1 -0
  353. package/dist/esm/text/segment.js +13 -21
  354. package/dist/esm/text/segment.js.map +1 -1
  355. package/dist/esm/watch.js +2 -5
  356. package/dist/esm/watch.js.map +1 -1
  357. package/dist/esm/workers/parse.js.map +1 -1
  358. package/package.json +3 -1
  359. package/schema.json +2 -2
  360. package/skills/sense/SKILL.md +2 -2
  361. package/skills/sense-setup/SKILL.md +3 -3
  362. package/dist/cjs/store/meta.d.cts +0 -3
  363. package/dist/cjs/store/meta.d.ts +0 -3
  364. package/dist/cjs/store/meta.js +0 -215
  365. package/dist/cjs/store/meta.js.map +0 -1
  366. package/dist/esm/store/meta.d.ts +0 -3
  367. package/dist/esm/store/meta.js +0 -17
  368. package/dist/esm/store/meta.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/validate.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { SIGNAL_NAMES, SIGNAL_PREREQUISITES, type SignalName } from './signals.ts';\nimport type { Config } from './types.ts';\n\n// Shape check for hand-edited files, so a typo names itself instead of surfacing as a\n// TypeError. Unknown top-level keys warn (forward compat); unknown keys inside a block error.\nconst KNOWN_KEYS = new Set(['$schema', 'version', 'presets', 'features', 'embed', 'content', 'store', 'queries']);\nconst KNOWN_PRESET_KEYS = new Set(['include', 'exclude', 'k', 'signals', 'where']);\nconst KNOWN_FEATURE_KEYS = new Set(['links', 'sections', 'tags', 'rank']);\n// Exported so a docs test can assert every key here has a schema.json property -- one source\n// of truth for what the embed block accepts, checked against the other for drift.\nexport const KNOWN_EMBED_KEYS = new Set(['model', 'provider', 'url', 'key', 'languages', 'chunkTokens']);\nconst KNOWN_CONTENT_KEYS = new Set(['tokenize']);\nconst SAVED_SEARCH_KEYS = new Set(['search', 'preset', 'include', 'exclude', 'where', 'k']);\n\nexport function unknownConfigKeys(cfg: Record<string, unknown>): string[] {\n return Object.keys(cfg).filter((k) => !KNOWN_KEYS.has(k));\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every((g) => typeof g === 'string' && g.length > 0);\n}\n\n// Pre-v3 shape check, just enough for migrateConfig to run safely (it reads scan.include\n// directly). The full v3 shape is checked by validateConfig once migration has produced it.\nexport function validateLegacyScan(parsed: unknown, configPath: string): void {\n const cfg = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : null;\n const scan = cfg?.scan as { include?: unknown } | undefined;\n if (!cfg || !scan || !isNonEmptyStringArray(scan.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n}\n\n// Only the shape is checked here. Whether the linked SQLite accepts the tokenizer is settled\n// by probing it in store/sqlite/open.ts, so this never has to carry a table of which version added what.\nfunction validateContentBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content must be an object of { tokenize?: string }`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_CONTENT_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content has unknown key(s) ${unknown.join(', ')}; content takes tokenize`);\n }\n if (block.tokenize !== undefined && (typeof block.tokenize !== 'string' || block.tokenize.trim() === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content.tokenize must be a non-empty string, e.g. \"trigram\" or \"unicode61 tokenchars '-_'\"`);\n }\n}\n\n// \"turso\" is implemented but not offered: it has no lexical or vector search yet, so a tree\n// naming it could not search. Registered in the store registry and exercised by the parity\n// suite; re-add here and in schema.json's enum when those land.\nfunction validateStoreKey(value: unknown, configPath: string): void {\n if (value !== 'sqlite' && value !== 'duckdb') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: store must be \"sqlite\" or \"duckdb\"`);\n }\n}\n\nfunction validateFeaturesBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_FEATURE_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features has unknown key(s) ${unknown.join(', ')}; embed is not a features key -- see the top-level \"embed\" block`);\n }\n for (const [name, v] of Object.entries(block)) {\n if (typeof v !== 'boolean') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean`);\n }\n }\n}\n\nfunction validateEmbedBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed must be an object of { model?, provider?: \"static\"|\"openai\"|\"cohere\", url?, key? }`);\n }\n const embed = value as Record<string, unknown>;\n const unknown = Object.keys(embed).filter((k) => !KNOWN_EMBED_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed has unknown key(s) ${unknown.join(', ')}`);\n }\n if (embed.model !== undefined && typeof embed.model !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model must be a string`);\n }\n // The block exists to name a model; provider settings without one would read as \"vectors\n // configured\" while embedding stays off, which is the ambiguity v4 removed.\n if (embed.model === undefined || embed.model === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model is required when the \"embed\" block is present (it is what turns vectors on); remove the block to index without vectors`);\n }\n if (embed.provider !== undefined && embed.provider !== 'static' && embed.provider !== 'openai' && embed.provider !== 'cohere') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider must be \"static\", \"openai\", or \"cohere\"`);\n }\n if (embed.url !== undefined && typeof embed.url !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.url must be a string`);\n }\n if (embed.key !== undefined && typeof embed.key !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.key must be a string`);\n }\n // openai has no default host, so a missing url would otherwise only surface at the first\n // vector search; a config error belongs at config time.\n if (embed.provider === 'openai' && (embed.url === undefined || embed.url === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider \"openai\" requires embed.url (e.g. \"http://localhost:11434/v1\" for Ollama)`);\n }\n if (embed.languages !== undefined && (!Array.isArray(embed.languages) || embed.languages.length === 0 || embed.languages.some((l) => typeof l !== 'string' || l === ''))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.languages must be a non-empty array of language tags (e.g. [\"en\", \"zh\"])`);\n }\n if (embed.chunkTokens !== undefined && (typeof embed.chunkTokens !== 'number' || !Number.isInteger(embed.chunkTokens) || embed.chunkTokens <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.chunkTokens must be a positive integer`);\n }\n}\n\nfunction isSignalName(value: unknown): value is SignalName {\n return typeof value === 'string' && (SIGNAL_NAMES as readonly string[]).includes(value);\n}\n\nfunction isSignalWeight(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0;\n}\n\n// Shape (an object of signal name -> weight) plus the one cross-signal prerequisite that\n// doesn't need the top-level embed block: links is seeded by word-match rows, so declaring it\n// without words is always empty.\nfunction validatePresetSignals(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value) || Object.keys(value).length === 0) {\n throw new SenseError(\n 'CONFIG_INVALID',\n `${configPath}: presets.${name}.signals must be a non-empty object of signal name -> weight (a finite number > 0), naming ${SIGNAL_NAMES.join(', ')} -- a preset that searches nothing is a mistake; name at least \"words\", or omit \"signals\" to use every signal whose prerequisites hold at weight 1`\n );\n }\n const entries = Object.entries(value as Record<string, unknown>);\n const unknown = entries.filter(([k]) => !isSignalName(k)).map(([k]) => k);\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals names unknown signal(s) ${unknown.join(', ')}; valid signals are ${SIGNAL_NAMES.join(', ')}`);\n }\n for (const [signal, weight] of entries) {\n if (!isSignalWeight(weight)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals.${signal} must be a finite number > 0`);\n }\n }\n const names = new Set(entries.map(([k]) => k));\n for (const [signal, requires] of Object.entries(SIGNAL_PREREQUISITES) as Array<[SignalName, SignalName]>) {\n if (names.has(signal) && !names.has(requires)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals has \"${signal}\" without \"${requires}\"; add \"${requires}\" to presets.${name}.signals, or drop \"${signal}\"`);\n }\n }\n}\n\nfunction validatePreset(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} must be an object`);\n }\n const preset = value as Record<string, unknown>;\n const unknown = Object.keys(preset).filter((k) => !KNOWN_PRESET_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} has unknown key(s) ${unknown.join(', ')}; a preset takes include, exclude, k, signals, where`);\n }\n if (!isNonEmptyStringArray(preset.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.include must be a non-empty array of glob strings`);\n }\n if (preset.exclude !== undefined && !isNonEmptyStringArray(preset.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (preset.signals !== undefined) validatePresetSignals(name, preset.signals, configPath);\n if (preset.k !== undefined && (typeof preset.k !== 'number' || !Number.isInteger(preset.k) || preset.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.k must be a positive integer`);\n }\n if (preset.where !== undefined && typeof preset.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.where must be a SQL condition string`);\n }\n}\n\n// A queries.<name> entry: { sql } or a saved search { search, preset?, include?, exclude?, where?, k? }.\nfunction validateSavedQuery(name: string, value: unknown, configPath: string): void {\n // A bare string used to mean SQL. It now fails rather than being inferred: an entry says\n // which of the two verbs it runs, the same choice the CLI makes explicit.\n if (typeof value === 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must say which verb it runs: { \"sql\": ${JSON.stringify(value.length > 40 ? `${value.slice(0, 37)}...` : value)} } to run it as SQL, or { \"search\": \"...\" } for a ranked search`);\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n }\n const entry = value as Record<string, unknown>;\n if ('sql' in entry) {\n const unknown = Object.keys(entry).filter((k) => k !== 'sql');\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a { sql } query takes only sql`);\n }\n if (typeof entry.sql !== 'string' || entry.sql.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.sql must be a non-empty string`);\n }\n return;\n }\n if ('search' in entry) {\n const unknown = Object.keys(entry).filter((k) => !SAVED_SEARCH_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a saved search takes search, preset, include, exclude, where, k`);\n }\n // A saved entry saves a question; a scope without a question is just flags.\n if (typeof entry.search !== 'string' || entry.search.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.search must be non-empty text`);\n }\n if (entry.preset !== undefined && (typeof entry.preset !== 'string' || entry.preset.length === 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.preset must be a preset name`);\n }\n if (entry.include !== undefined && !isNonEmptyStringArray(entry.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.include must be a non-empty array of glob strings`);\n }\n if (entry.exclude !== undefined && !isNonEmptyStringArray(entry.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (entry.where !== undefined && typeof entry.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.where must be a SQL condition string`);\n }\n if (entry.k !== undefined && (typeof entry.k !== 'number' || !Number.isInteger(entry.k) || entry.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.k must be a positive integer`);\n }\n return;\n }\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n}\n\nexport function validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n\n // `checks` is rejected by name, not warned: silence would hide that assertions are gone.\n if (cfg.checks !== undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: checks was removed in v3 -- sense check no longer asserts on saved queries; a returned row set is the reader's judgment`);\n }\n\n const presets = cfg.presets as Record<string, unknown> | undefined;\n if (!presets || typeof presets !== 'object' || Array.isArray(presets) || Object.keys(presets).length === 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must be a non-empty object of preset name -> { include, exclude?, k?, signals?, where? }`);\n }\n for (const [name, value] of Object.entries(presets)) {\n validatePreset(name, value, configPath);\n }\n if (presets.default === undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must include a \"default\" preset`);\n }\n\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> { sql } or { search }`);\n }\n const queries = cfg.queries as Record<string, unknown>;\n for (const [name, value] of Object.entries(queries)) {\n validateSavedQuery(name, value, configPath);\n }\n\n if (cfg.features !== undefined) {\n validateFeaturesBlock(cfg.features, configPath);\n }\n if (cfg.embed !== undefined) {\n validateEmbedBlock(cfg.embed, configPath);\n }\n if (cfg.content !== undefined) {\n validateContentBlock(cfg.content, configPath);\n }\n if (cfg.store !== undefined) {\n validateStoreKey(cfg.store, configPath);\n }\n\n // vectors' prerequisite is a named model, checked here once embed is known valid (or absent).\n if (cfg.embed === undefined) {\n for (const [name, value] of Object.entries(presets)) {\n const signals = (value as Record<string, unknown>).signals as Record<string, unknown> | undefined;\n if (signals && 'vectors' in signals) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals includes \"vectors\", but no \"embed\" block names a model; add one, or drop \"vectors\" from presets.${name}.signals`);\n }\n }\n }\n\n return cfg as unknown as Config;\n}\n"],"names":["KNOWN_EMBED_KEYS","unknownConfigKeys","validateConfig","validateLegacyScan","KNOWN_KEYS","Set","KNOWN_PRESET_KEYS","KNOWN_FEATURE_KEYS","KNOWN_CONTENT_KEYS","SAVED_SEARCH_KEYS","cfg","Object","keys","filter","k","has","isNonEmptyStringArray","value","Array","isArray","length","every","g","parsed","configPath","scan","include","SenseError","validateContentBlock","block","unknown","join","tokenize","undefined","trim","validateStoreKey","validateFeaturesBlock","entries","name","v","validateEmbedBlock","embed","model","provider","url","key","languages","some","l","chunkTokens","Number","isInteger","isSignalName","SIGNAL_NAMES","includes","isSignalWeight","isFinite","validatePresetSignals","map","signal","weight","names","SIGNAL_PREREQUISITES","requires","validatePreset","preset","exclude","signals","where","validateSavedQuery","JSON","stringify","slice","entry","sql","search","checks","presets","default","queries","features","content","store"],"mappings":";;;;;;;;;;;QAWaA;eAAAA;;QAIGC;eAAAA;;QAgNAC;eAAAA;;QAtMAC;eAAAA;;;wBAzBW;yBACyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGpE,sFAAsF;AACtF,8FAA8F;AAC9F,IAAMC,aAAa,IAAIC,IAAI;IAAC;IAAW;IAAW;IAAW;IAAY;IAAS;IAAW;IAAS;CAAU;AAChH,IAAMC,oBAAoB,IAAID,IAAI;IAAC;IAAW;IAAW;IAAK;IAAW;CAAQ;AACjF,IAAME,qBAAqB,IAAIF,IAAI;IAAC;IAAS;IAAY;IAAQ;CAAO;AAGjE,IAAML,mBAAmB,IAAIK,IAAI;IAAC;IAAS;IAAY;IAAO;IAAO;IAAa;CAAc;AACvG,IAAMG,qBAAqB,IAAIH,IAAI;IAAC;CAAW;AAC/C,IAAMI,oBAAoB,IAAIJ,IAAI;IAAC;IAAU;IAAU;IAAW;IAAW;IAAS;CAAI;AAEnF,SAASJ,kBAAkBS,GAA4B;IAC5D,OAAOC,OAAOC,IAAI,CAACF,KAAKG,MAAM,CAAC,SAACC;eAAM,CAACV,WAAWW,GAAG,CAACD;;AACxD;AAEA,SAASE,sBAAsBC,KAAc;IAC3C,OAAOC,MAAMC,OAAO,CAACF,UAAUA,MAAMG,MAAM,GAAG,KAAKH,MAAMI,KAAK,CAAC,SAACC;eAAM,OAAOA,MAAM,YAAYA,EAAEF,MAAM,GAAG;;AAC5G;AAIO,SAASjB,mBAAmBoB,MAAe,EAAEC,UAAkB;IACpE,IAAMd,MAAM,CAAA,OAAOa,uCAAP,SAAOA,OAAK,MAAM,YAAYA,WAAW,QAAQ,CAACL,MAAMC,OAAO,CAACI,UAAWA,SAAqC;IAC5H,IAAME,OAAOf,gBAAAA,0BAAAA,IAAKe,IAAI;IACtB,IAAI,CAACf,OAAO,CAACe,QAAQ,CAACT,sBAAsBS,KAAKC,OAAO,GAAG;QACzD,MAAM,IAAIC,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;AACF;AAEA,6FAA6F;AAC7F,yGAAyG;AACzG,SAASI,qBAAqBX,KAAc,EAAEO,UAAkB;IAC9D,IAAI,CAAA,OAAOP,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,QAAQ;QACvE,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAMK,QAAQZ;IACd,IAAMa,UAAUnB,OAAOC,IAAI,CAACiB,OAAOhB,MAAM,CAAC,SAACC;eAAM,CAACN,mBAAmBO,GAAG,CAACD;;IACzE,IAAIgB,QAAQV,MAAM,GAAG,GAAG;QACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAA4CG,OAA1CN,YAAW,iCAAkD,OAAnBM,QAAQC,IAAI,CAAC,OAAM;IACzG;IACA,IAAIF,MAAMG,QAAQ,KAAKC,aAAc,CAAA,OAAOJ,MAAMG,QAAQ,KAAK,YAAYH,MAAMG,QAAQ,CAACE,IAAI,OAAO,EAAC,GAAI;QACxG,MAAM,IAAIP,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;AACF;AAEA,4FAA4F;AAC5F,2FAA2F;AAC3F,gEAAgE;AAChE,SAASW,iBAAiBlB,KAAc,EAAEO,UAAkB;IAC1D,IAAIP,UAAU,YAAYA,UAAU,UAAU;QAC5C,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;AACF;AAEA,SAASY,sBAAsBnB,KAAc,EAAEO,UAAkB;IAC/D,IAAI,CAAA,OAAOP,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,QAAQ;QACvE,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAMK,QAAQZ;IACd,IAAMa,UAAUnB,OAAOC,IAAI,CAACiB,OAAOhB,MAAM,CAAC,SAACC;eAAM,CAACP,mBAAmBQ,GAAG,CAACD;;IACzE,IAAIgB,QAAQV,MAAM,GAAG,GAAG;QACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAA6CG,OAA3CN,YAAW,kCAAmD,OAAnBM,QAAQC,IAAI,CAAC,OAAM;IAC1G;QACK,kCAAA,2BAAA;;QAAL,QAAK,YAAmBpB,OAAO0B,OAAO,CAACR,2BAAlC,SAAA,6BAAA,QAAA,yBAAA,iCAA0C;YAA1C,mCAAA,iBAAOS,uBAAMC;YAChB,IAAI,OAAOA,MAAM,WAAW;gBAC1B,MAAM,IAAIZ,oBAAU,CAAC,kBAAkB,AAAC,GAA0BW,OAAxBd,YAAW,eAAkB,OAALc,MAAK;YACzE;QACF;;QAJK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;AAKP;AAEA,SAASE,mBAAmBvB,KAAc,EAAEO,UAAkB;IAC5D,IAAI,CAAA,OAAOP,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,QAAQ;QACvE,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAMiB,QAAQxB;IACd,IAAMa,UAAUnB,OAAOC,IAAI,CAAC6B,OAAO5B,MAAM,CAAC,SAACC;eAAM,CAACd,iBAAiBe,GAAG,CAACD;;IACvE,IAAIgB,QAAQV,MAAM,GAAG,GAAG;QACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAA0CG,OAAxCN,YAAW,+BAAgD,OAAnBM,QAAQC,IAAI,CAAC;IACjG;IACA,IAAIU,MAAMC,KAAK,KAAKT,aAAa,OAAOQ,MAAMC,KAAK,KAAK,UAAU;QAChE,MAAM,IAAIf,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,yFAAyF;IACzF,4EAA4E;IAC5E,IAAIiB,MAAMC,KAAK,KAAKT,aAAaQ,MAAMC,KAAK,KAAK,IAAI;QACnD,MAAM,IAAIf,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAIiB,MAAME,QAAQ,KAAKV,aAAaQ,MAAME,QAAQ,KAAK,YAAYF,MAAME,QAAQ,KAAK,YAAYF,MAAME,QAAQ,KAAK,UAAU;QAC7H,MAAM,IAAIhB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAIiB,MAAMG,GAAG,KAAKX,aAAa,OAAOQ,MAAMG,GAAG,KAAK,UAAU;QAC5D,MAAM,IAAIjB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAIiB,MAAMI,GAAG,KAAKZ,aAAa,OAAOQ,MAAMI,GAAG,KAAK,UAAU;QAC5D,MAAM,IAAIlB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,yFAAyF;IACzF,wDAAwD;IACxD,IAAIiB,MAAME,QAAQ,KAAK,YAAaF,CAAAA,MAAMG,GAAG,KAAKX,aAAaQ,MAAMG,GAAG,KAAK,EAAC,GAAI;QAChF,MAAM,IAAIjB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAIiB,MAAMK,SAAS,KAAKb,aAAc,CAAA,CAACf,MAAMC,OAAO,CAACsB,MAAMK,SAAS,KAAKL,MAAMK,SAAS,CAAC1B,MAAM,KAAK,KAAKqB,MAAMK,SAAS,CAACC,IAAI,CAAC,SAACC;eAAM,OAAOA,MAAM,YAAYA,MAAM;MAAE,GAAI;QACxK,MAAM,IAAIrB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAIiB,MAAMQ,WAAW,KAAKhB,aAAc,CAAA,OAAOQ,MAAMQ,WAAW,KAAK,YAAY,CAACC,OAAOC,SAAS,CAACV,MAAMQ,WAAW,KAAKR,MAAMQ,WAAW,IAAI,CAAA,GAAI;QAChJ,MAAM,IAAItB,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;AACF;AAEA,SAAS4B,aAAanC,KAAc;IAClC,OAAO,OAAOA,UAAU,YAAY,AAACoC,uBAAY,CAAuBC,QAAQ,CAACrC;AACnF;AAEA,SAASsC,eAAetC,KAAc;IACpC,OAAO,OAAOA,UAAU,YAAYiC,OAAOM,QAAQ,CAACvC,UAAUA,QAAQ;AACxE;AAEA,yFAAyF;AACzF,8FAA8F;AAC9F,iCAAiC;AACjC,SAASwC,sBAAsBnB,IAAY,EAAErB,KAAc,EAAEO,UAAkB;IAC7E,IAAI,CAAA,OAAOP,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,UAAUN,OAAOC,IAAI,CAACK,OAAOG,MAAM,KAAK,GAAG;QAC1G,MAAM,IAAIO,oBAAU,CAClB,kBACA,AAAC,GAAyBW,OAAvBd,YAAW,cAA8G6B,OAAlGf,MAAK,+FAAqH,OAAxBe,uBAAY,CAACtB,IAAI,CAAC,OAAM;IAExJ;IACA,IAAMM,UAAU1B,OAAO0B,OAAO,CAACpB;IAC/B,IAAMa,UAAUO,QAAQxB,MAAM,CAAC;iDAAEC;eAAO,CAACsC,aAAatC;OAAI4C,GAAG,CAAC;iDAAE5C;eAAOA;;IACvE,IAAIgB,QAAQV,MAAM,GAAG,GAAG;QACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAoDM,OAAxCQ,MAAK,qCAA4Ee,OAAzCvB,QAAQC,IAAI,CAAC,OAAM,wBAA8C,OAAxBsB,uBAAY,CAACtB,IAAI,CAAC;IACtK;QACK,kCAAA,2BAAA;;QAAL,QAAK,YAA0BM,4BAA1B,SAAA,6BAAA,QAAA,yBAAA,iCAAmC;YAAnC,mCAAA,iBAAOsB,yBAAQC;YAClB,IAAI,CAACL,eAAeK,SAAS;gBAC3B,MAAM,IAAIjC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAA4BmC,OAAhBrB,MAAK,aAAkB,OAAPqB,QAAO;YAC1F;QACF;;QAJK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAKL,IAAME,QAAQ,IAAIxD,IAAIgC,QAAQqB,GAAG,CAAC;iDAAE5C;eAAOA;;QACtC,mCAAA,4BAAA;;QAAL,QAAK,aAA4BH,OAAO0B,OAAO,CAACyB,+BAAoB,sBAA/D,UAAA,8BAAA,SAAA,0BAAA,kCAAqG;YAArG,oCAAA,kBAAOH,2BAAQI;YAClB,IAAIF,MAAM9C,GAAG,CAAC4C,YAAW,CAACE,MAAM9C,GAAG,CAACgD,WAAW;gBAC7C,MAAM,IAAIpC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiCmC,OAArBrB,MAAK,kBAAoCyB,OAApBJ,SAAO,eAAgCI,OAAnBA,UAAS,YAAkCzB,OAAxByB,UAAS,iBAAyCJ,OAA1BrB,MAAK,uBAA4B,OAAPqB,SAAO;YACxL;QACF;;QAJK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;AAKP;AAEA,SAASK,eAAe1B,IAAY,EAAErB,KAAc,EAAEO,UAAkB;IACtE,IAAI,CAAA,OAAOP,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,QAAQ;QACvE,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;IACA,IAAM2B,SAAShD;IACf,IAAMa,UAAUnB,OAAOC,IAAI,CAACqD,QAAQpD,MAAM,CAAC,SAACC;eAAM,CAACR,kBAAkBS,GAAG,CAACD;;IACzE,IAAIgB,QAAQV,MAAM,GAAG,GAAG;QACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAuCM,OAA3BQ,MAAK,wBAAyC,OAAnBR,QAAQC,IAAI,CAAC,OAAM;IACjH;IACA,IAAI,CAACf,sBAAsBiD,OAAOvC,OAAO,GAAG;QAC1C,MAAM,IAAIC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;IACA,IAAI2B,OAAOC,OAAO,KAAKjC,aAAa,CAACjB,sBAAsBiD,OAAOC,OAAO,GAAG;QAC1E,MAAM,IAAIvC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;IACA,IAAI2B,OAAOE,OAAO,KAAKlC,WAAWwB,sBAAsBnB,MAAM2B,OAAOE,OAAO,EAAE3C;IAC9E,IAAIyC,OAAOnD,CAAC,KAAKmB,aAAc,CAAA,OAAOgC,OAAOnD,CAAC,KAAK,YAAY,CAACoC,OAAOC,SAAS,CAACc,OAAOnD,CAAC,KAAKmD,OAAOnD,CAAC,IAAI,CAAA,GAAI;QAC5G,MAAM,IAAIa,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;IACA,IAAI2B,OAAOG,KAAK,KAAKnC,aAAa,OAAOgC,OAAOG,KAAK,KAAK,UAAU;QAClE,MAAM,IAAIzC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;AACF;AAEA,yGAAyG;AACzG,SAAS+B,mBAAmB/B,IAAY,EAAErB,KAAc,EAAEO,UAAkB;IAC1E,yFAAyF;IACzF,0EAA0E;IAC1E,IAAI,OAAOP,UAAU,UAAU;QAC7B,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAA0D8C,OAA9ChC,MAAK,2CAAgH,OAAvEgC,KAAKC,SAAS,CAACtD,MAAMG,MAAM,GAAG,KAAK,AAAC,GAAqB,OAAnBH,MAAMuD,KAAK,CAAC,GAAG,KAAI,SAAOvD,QAAO;IACxL;IACA,IAAI,CAAA,OAAOA,sCAAP,SAAOA,MAAI,MAAM,YAAYA,UAAU,QAAQC,MAAMC,OAAO,CAACF,QAAQ;QACvE,MAAM,IAAIU,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;IACxE;IACA,IAAMmC,QAAQxD;IACd,IAAI,SAASwD,OAAO;QAClB,IAAM3C,UAAUnB,OAAOC,IAAI,CAAC6D,OAAO5D,MAAM,CAAC,SAACC;mBAAMA,MAAM;;QACvD,IAAIgB,QAAQV,MAAM,GAAG,GAAG;YACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAuCM,OAA3BQ,MAAK,wBAAyC,OAAnBR,QAAQC,IAAI,CAAC,OAAM;QACjH;QACA,IAAI,OAAO0C,MAAMC,GAAG,KAAK,YAAYD,MAAMC,GAAG,CAACxC,IAAI,OAAO,IAAI;YAC5D,MAAM,IAAIP,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA;IACF;IACA,IAAI,YAAYmC,OAAO;QACrB,IAAM3C,WAAUnB,OAAOC,IAAI,CAAC6D,OAAO5D,MAAM,CAAC,SAACC;mBAAM,CAACL,kBAAkBM,GAAG,CAACD;;QACxE,IAAIgB,SAAQV,MAAM,GAAG,GAAG;YACtB,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAuCM,OAA3BQ,MAAK,wBAAyC,OAAnBR,SAAQC,IAAI,CAAC,OAAM;QACjH;QACA,4EAA4E;QAC5E,IAAI,OAAO0C,MAAME,MAAM,KAAK,YAAYF,MAAME,MAAM,CAACzC,IAAI,OAAO,IAAI;YAClE,MAAM,IAAIP,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA,IAAImC,MAAMR,MAAM,KAAKhC,aAAc,CAAA,OAAOwC,MAAMR,MAAM,KAAK,YAAYQ,MAAMR,MAAM,CAAC7C,MAAM,KAAK,CAAA,GAAI;YACjG,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA,IAAImC,MAAM/C,OAAO,KAAKO,aAAa,CAACjB,sBAAsByD,MAAM/C,OAAO,GAAG;YACxE,MAAM,IAAIC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA,IAAImC,MAAMP,OAAO,KAAKjC,aAAa,CAACjB,sBAAsByD,MAAMP,OAAO,GAAG;YACxE,MAAM,IAAIvC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA,IAAImC,MAAML,KAAK,KAAKnC,aAAa,OAAOwC,MAAML,KAAK,KAAK,UAAU;YAChE,MAAM,IAAIzC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA,IAAImC,MAAM3D,CAAC,KAAKmB,aAAc,CAAA,OAAOwC,MAAM3D,CAAC,KAAK,YAAY,CAACoC,OAAOC,SAAS,CAACsB,MAAM3D,CAAC,KAAK2D,MAAM3D,CAAC,IAAI,CAAA,GAAI;YACxG,MAAM,IAAIa,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;QACxE;QACA;IACF;IACA,MAAM,IAAIX,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAAiB,OAALc,MAAK;AACxE;AAEO,SAASpC,eAAeqB,MAAe,EAAEC,UAAkB;IAChE,IAAI,CAAA,OAAOD,uCAAP,SAAOA,OAAK,MAAM,YAAYA,WAAW,QAAQL,MAAMC,OAAO,CAACI,SAAS;QAC1E,MAAM,IAAII,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAMd,MAAMa;IAEZ,yFAAyF;IACzF,IAAIb,IAAIkE,MAAM,KAAK3C,WAAW;QAC5B,MAAM,IAAIN,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IAEA,IAAMqD,UAAUnE,IAAImE,OAAO;IAC3B,IAAI,CAACA,WAAW,CAAA,OAAOA,wCAAP,SAAOA,QAAM,MAAM,YAAY3D,MAAMC,OAAO,CAAC0D,YAAYlE,OAAOC,IAAI,CAACiE,SAASzD,MAAM,KAAK,GAAG;QAC1G,MAAM,IAAIO,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;QACK,kCAAA,2BAAA;;QAAL,QAAK,YAAuBb,OAAO0B,OAAO,CAACwC,6BAAtC,SAAA,6BAAA,QAAA,yBAAA,iCAAgD;YAAhD,mCAAA,iBAAOvC,uBAAMrB;YAChB+C,eAAe1B,MAAMrB,OAAOO;QAC9B;;QAFK;QAAA;;;iBAAA,6BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAGL,IAAIqD,QAAQC,OAAO,KAAK7C,WAAW;QACjC,MAAM,IAAIN,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IAEA,IAAId,IAAIqE,OAAO,KAAK9C,WAAWvB,IAAIqE,OAAO,GAAG,CAAC;IAC9C,IAAI,SAAOrE,IAAIqE,OAAO,MAAK,YAAYrE,IAAIqE,OAAO,KAAK,QAAQ7D,MAAMC,OAAO,CAACT,IAAIqE,OAAO,GAAG;QACzF,MAAM,IAAIpD,oBAAU,CAAC,kBAAkB,AAAC,GAAa,OAAXH,YAAW;IACvD;IACA,IAAMuD,UAAUrE,IAAIqE,OAAO;QACtB,mCAAA,4BAAA;;QAAL,QAAK,aAAuBpE,OAAO0B,OAAO,CAAC0C,6BAAtC,UAAA,8BAAA,SAAA,0BAAA,kCAAgD;YAAhD,oCAAA,kBAAOzC,yBAAMrB;YAChBoD,mBAAmB/B,OAAMrB,QAAOO;QAClC;;QAFK;QAAA;;;iBAAA,8BAAA;gBAAA;;;gBAAA;sBAAA;;;;IAIL,IAAId,IAAIsE,QAAQ,KAAK/C,WAAW;QAC9BG,sBAAsB1B,IAAIsE,QAAQ,EAAExD;IACtC;IACA,IAAId,IAAI+B,KAAK,KAAKR,WAAW;QAC3BO,mBAAmB9B,IAAI+B,KAAK,EAAEjB;IAChC;IACA,IAAId,IAAIuE,OAAO,KAAKhD,WAAW;QAC7BL,qBAAqBlB,IAAIuE,OAAO,EAAEzD;IACpC;IACA,IAAId,IAAIwE,KAAK,KAAKjD,WAAW;QAC3BE,iBAAiBzB,IAAIwE,KAAK,EAAE1D;IAC9B;IAEA,8FAA8F;IAC9F,IAAId,IAAI+B,KAAK,KAAKR,WAAW;YACtB,mCAAA,4BAAA;;YAAL,QAAK,aAAuBtB,OAAO0B,OAAO,CAACwC,6BAAtC,UAAA,8BAAA,SAAA,0BAAA,kCAAgD;gBAAhD,oCAAA,kBAAOvC,yBAAMrB;gBAChB,IAAMkD,UAAU,AAAClD,OAAkCkD,OAAO;gBAC1D,IAAIA,WAAW,aAAaA,SAAS;oBACnC,MAAM,IAAIxC,oBAAU,CAAC,kBAAkB,AAAC,GAAyBW,OAAvBd,YAAW,cAA4Hc,OAAhHA,OAAK,6GAAgH,OAALA,OAAK;gBACxL;YACF;;YALK;YAAA;;;qBAAA,8BAAA;oBAAA;;;oBAAA;0BAAA;;;;IAMP;IAEA,OAAO5B;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/config/validate.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { SIGNAL_NAMES, SIGNAL_PREREQUISITES, type SignalName } from './signals.ts';\nimport { type Config, STORE_NAMES, type StoreName } from './types.ts';\n\n// Shape check for hand-edited files, so a typo names itself instead of surfacing as a\n// TypeError. Unknown top-level keys warn (forward compat); unknown keys inside a block error.\nconst KNOWN_KEYS = new Set(['$schema', 'version', 'presets', 'features', 'embed', 'content', 'store', 'queries']);\nconst KNOWN_PRESET_KEYS = new Set(['include', 'exclude', 'k', 'signals', 'where']);\nconst KNOWN_FEATURE_KEYS = new Set(['links', 'sections', 'tags', 'rank']);\n// Exported so a docs test can assert every key here has a schema.json property -- one source\n// of truth for what the embed block accepts, checked against the other for drift.\nexport const KNOWN_EMBED_KEYS = new Set(['model', 'provider', 'url', 'key', 'languages', 'chunkTokens']);\nconst KNOWN_CONTENT_KEYS = new Set(['tokenize']);\nconst SAVED_SEARCH_KEYS = new Set(['search', 'preset', 'include', 'exclude', 'where', 'k']);\n\nexport function unknownConfigKeys(cfg: Record<string, unknown>): string[] {\n return Object.keys(cfg).filter((k) => !KNOWN_KEYS.has(k));\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every((g) => typeof g === 'string' && g.length > 0);\n}\n\n// Pre-v3 shape check, just enough for migrateConfig to run safely (it reads scan.include\n// directly). The full v3 shape is checked by validateConfig once migration has produced it.\nexport function validateLegacyScan(parsed: unknown, configPath: string): void {\n const cfg = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? (parsed as Record<string, unknown>) : null;\n const scan = cfg?.scan as { include?: unknown } | undefined;\n if (!cfg || !scan || !isNonEmptyStringArray(scan.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: scan.include must be a non-empty array of glob strings`);\n }\n}\n\n// Only the shape is checked here. Whether the linked SQLite accepts the tokenizer is settled\n// by probing it in store/sqlite/open.ts, so this never has to carry a table of which version added what.\nfunction validateContentBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content must be an object of { tokenize?: string }`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_CONTENT_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content has unknown key(s) ${unknown.join(', ')}; content takes tokenize`);\n }\n if (block.tokenize !== undefined && (typeof block.tokenize !== 'string' || block.tokenize.trim() === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: content.tokenize must be a non-empty string, e.g. \"trigram\" or \"unicode61 tokenchars '-_'\"`);\n }\n}\n\nfunction validateStoreKey(value: unknown, configPath: string): void {\n if (!STORE_NAMES.includes(value as StoreName)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: store must be ${STORE_NAMES.map((name) => `\"${name}\"`).join(' or ')}`);\n }\n}\n\nfunction validateFeaturesBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features must be an object of name -> boolean`);\n }\n const block = value as Record<string, unknown>;\n const unknown = Object.keys(block).filter((k) => !KNOWN_FEATURE_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features has unknown key(s) ${unknown.join(', ')}; embed is not a features key -- see the top-level \"embed\" block`);\n }\n for (const [name, v] of Object.entries(block)) {\n if (typeof v !== 'boolean') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: features.${name} must be a boolean`);\n }\n }\n}\n\nfunction validateEmbedBlock(value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed must be an object of { model?, provider?: \"static\"|\"openai\"|\"cohere\", url?, key? }`);\n }\n const embed = value as Record<string, unknown>;\n const unknown = Object.keys(embed).filter((k) => !KNOWN_EMBED_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed has unknown key(s) ${unknown.join(', ')}`);\n }\n if (embed.model !== undefined && typeof embed.model !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model must be a string`);\n }\n // The block exists to name a model; provider settings without one would read as \"vectors\n // configured\" while embedding stays off, which is the ambiguity v4 removed.\n if (embed.model === undefined || embed.model === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.model is required when the \"embed\" block is present (it is what turns vectors on); remove the block to index without vectors`);\n }\n if (embed.provider !== undefined && embed.provider !== 'static' && embed.provider !== 'openai' && embed.provider !== 'cohere') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider must be \"static\", \"openai\", or \"cohere\"`);\n }\n if (embed.url !== undefined && typeof embed.url !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.url must be a string`);\n }\n if (embed.key !== undefined && typeof embed.key !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.key must be a string`);\n }\n // openai has no default host, so a missing url would otherwise only surface at the first\n // vector search; a config error belongs at config time.\n if (embed.provider === 'openai' && (embed.url === undefined || embed.url === '')) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.provider \"openai\" requires embed.url (e.g. \"http://localhost:11434/v1\" for Ollama)`);\n }\n if (embed.languages !== undefined && (!Array.isArray(embed.languages) || embed.languages.length === 0 || embed.languages.some((l) => typeof l !== 'string' || l === ''))) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.languages must be a non-empty array of language tags (e.g. [\"en\", \"zh\"])`);\n }\n if (embed.chunkTokens !== undefined && (typeof embed.chunkTokens !== 'number' || !Number.isInteger(embed.chunkTokens) || embed.chunkTokens <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: embed.chunkTokens must be a positive integer`);\n }\n}\n\nfunction isSignalName(value: unknown): value is SignalName {\n return typeof value === 'string' && (SIGNAL_NAMES as readonly string[]).includes(value);\n}\n\nfunction isSignalWeight(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0;\n}\n\n// Shape (an object of signal name -> weight) plus the one cross-signal prerequisite that doesn't\n// need the top-level embed block: links is seeded by word-match rows, so declaring it without words is always empty.\nfunction validatePresetSignals(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value) || Object.keys(value).length === 0) {\n throw new SenseError(\n 'CONFIG_INVALID',\n `${configPath}: presets.${name}.signals must be a non-empty object of signal name -> weight (a finite number > 0), naming ${SIGNAL_NAMES.join(', ')} -- a preset that searches nothing is a mistake; name at least \"words\", or omit \"signals\" to use every signal whose prerequisites hold at weight 1`\n );\n }\n const entries = Object.entries(value as Record<string, unknown>);\n const unknown = entries.filter(([k]) => !isSignalName(k)).map(([k]) => k);\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals names unknown signal(s) ${unknown.join(', ')}; valid signals are ${SIGNAL_NAMES.join(', ')}`);\n }\n for (const [signal, weight] of entries) {\n if (!isSignalWeight(weight)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals.${signal} must be a finite number > 0`);\n }\n }\n const names = new Set(entries.map(([k]) => k));\n for (const [signal, requires] of Object.entries(SIGNAL_PREREQUISITES) as Array<[SignalName, SignalName]>) {\n if (names.has(signal) && !names.has(requires)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals has \"${signal}\" without \"${requires}\"; add \"${requires}\" to presets.${name}.signals, or drop \"${signal}\"`);\n }\n }\n}\n\nfunction validatePreset(name: string, value: unknown, configPath: string): void {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} must be an object`);\n }\n const preset = value as Record<string, unknown>;\n const unknown = Object.keys(preset).filter((k) => !KNOWN_PRESET_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name} has unknown key(s) ${unknown.join(', ')}; a preset takes include, exclude, k, signals, where`);\n }\n if (!isNonEmptyStringArray(preset.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.include must be a non-empty array of glob strings`);\n }\n if (preset.exclude !== undefined && !isNonEmptyStringArray(preset.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (preset.signals !== undefined) validatePresetSignals(name, preset.signals, configPath);\n if (preset.k !== undefined && (typeof preset.k !== 'number' || !Number.isInteger(preset.k) || preset.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.k must be a positive integer`);\n }\n if (preset.where !== undefined && typeof preset.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.where must be a SQL condition string`);\n }\n}\n\n// A queries.<name> entry: { sql } or a saved search { search, preset?, include?, exclude?, where?, k? }.\nfunction validateSavedQuery(name: string, value: unknown, configPath: string): void {\n // A bare string is rejected rather than inferred to be SQL: an entry says which of the two\n // verbs it runs, the same choice the CLI makes explicit.\n if (typeof value === 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must say which verb it runs: { \"sql\": ${JSON.stringify(value.length > 40 ? `${value.slice(0, 37)}...` : value)} } to run it as SQL, or { \"search\": \"...\" } for a ranked search`);\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n }\n const entry = value as Record<string, unknown>;\n if ('sql' in entry) {\n const unknown = Object.keys(entry).filter((k) => k !== 'sql');\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a { sql } query takes only sql`);\n }\n if (typeof entry.sql !== 'string' || entry.sql.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.sql must be a non-empty string`);\n }\n return;\n }\n if ('search' in entry) {\n const unknown = Object.keys(entry).filter((k) => !SAVED_SEARCH_KEYS.has(k));\n if (unknown.length > 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} has unknown key(s) ${unknown.join(', ')}; a saved search takes search, preset, include, exclude, where, k`);\n }\n // A saved entry saves a question; a scope without a question is just flags.\n if (typeof entry.search !== 'string' || entry.search.trim() === '') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.search must be non-empty text`);\n }\n if (entry.preset !== undefined && (typeof entry.preset !== 'string' || entry.preset.length === 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.preset must be a preset name`);\n }\n if (entry.include !== undefined && !isNonEmptyStringArray(entry.include)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.include must be a non-empty array of glob strings`);\n }\n if (entry.exclude !== undefined && !isNonEmptyStringArray(entry.exclude)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.exclude must be a non-empty array of glob strings`);\n }\n if (entry.where !== undefined && typeof entry.where !== 'string') {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.where must be a SQL condition string`);\n }\n if (entry.k !== undefined && (typeof entry.k !== 'number' || !Number.isInteger(entry.k) || entry.k <= 0)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name}.k must be a positive integer`);\n }\n return;\n }\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries.${name} must be { sql } or { search, preset?, include?, exclude?, where?, k? }`);\n}\n\nexport function validateConfig(parsed: unknown, configPath: string): Config {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: config must be a JSON object`);\n }\n const cfg = parsed as Record<string, unknown>;\n\n // `checks` is rejected by name, not warned: silence would hide that assertions are gone.\n if (cfg.checks !== undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: checks was removed in v3 -- sense check no longer asserts on saved queries; a returned row set is the reader's judgment`);\n }\n\n const presets = cfg.presets as Record<string, unknown> | undefined;\n if (!presets || typeof presets !== 'object' || Array.isArray(presets) || Object.keys(presets).length === 0) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must be a non-empty object of preset name -> { include, exclude?, k?, signals?, where? }`);\n }\n for (const [name, value] of Object.entries(presets)) {\n validatePreset(name, value, configPath);\n }\n if (presets.default === undefined) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets must include a \"default\" preset`);\n }\n\n if (cfg.queries === undefined) cfg.queries = {};\n if (typeof cfg.queries !== 'object' || cfg.queries === null || Array.isArray(cfg.queries)) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: queries must be an object of name -> { sql } or { search }`);\n }\n const queries = cfg.queries as Record<string, unknown>;\n for (const [name, value] of Object.entries(queries)) {\n validateSavedQuery(name, value, configPath);\n }\n\n if (cfg.features !== undefined) {\n validateFeaturesBlock(cfg.features, configPath);\n }\n if (cfg.embed !== undefined) {\n validateEmbedBlock(cfg.embed, configPath);\n }\n if (cfg.content !== undefined) {\n validateContentBlock(cfg.content, configPath);\n }\n if (cfg.store !== undefined) {\n validateStoreKey(cfg.store, configPath);\n }\n\n // vectors' prerequisite is a named model, checked here once embed is known valid (or absent).\n if (cfg.embed === undefined) {\n for (const [name, value] of Object.entries(presets)) {\n const signals = (value as Record<string, unknown>).signals as Record<string, unknown> | undefined;\n if (signals && 'vectors' in signals) {\n throw new SenseError('CONFIG_INVALID', `${configPath}: presets.${name}.signals includes \"vectors\", but no \"embed\" block names a model; add one, or drop \"vectors\" from presets.${name}.signals`);\n }\n }\n }\n\n return cfg as unknown as Config;\n}\n"],"names":["KNOWN_EMBED_KEYS","unknownConfigKeys","validateConfig","validateLegacyScan","KNOWN_KEYS","Set","KNOWN_PRESET_KEYS","KNOWN_FEATURE_KEYS","KNOWN_CONTENT_KEYS","SAVED_SEARCH_KEYS","cfg","Object","keys","filter","k","has","isNonEmptyStringArray","value","Array","isArray","length","every","g","parsed","configPath","scan","include","SenseError","validateContentBlock","block","unknown","join","tokenize","undefined","trim","validateStoreKey","STORE_NAMES","includes","map","name","validateFeaturesBlock","entries","v","validateEmbedBlock","embed","model","provider","url","key","languages","some","l","chunkTokens","Number","isInteger","isSignalName","SIGNAL_NAMES","isSignalWeight","isFinite","validatePresetSignals","signal","weight","names","SIGNAL_PREREQUISITES","requires","validatePreset","preset","exclude","signals","where","validateSavedQuery","JSON","stringify","slice","entry","sql","search","checks","presets","default","queries","features","content","store"],"mappings":"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"}
@@ -169,8 +169,8 @@ function cohereProvider(model, url, keyEnv) {
169
169
  ];
170
170
  case 2:
171
171
  body = _state.sent();
172
- // Cohere returns one vector per text in order, with no index field to re-sort by; the
173
- // count is what a caller mapping positionally still has to be able to trust.
172
+ // Cohere returns one vector per text in order, with no index field to re-sort by; a caller
173
+ // maps them positionally, so the count below must be trustworthy.
174
174
  vectors = body.embeddings.float;
175
175
  if (vectors.length !== texts.length) throw new _errorsts.SenseError('EMBED_MODEL', "".concat(base, "/v2/embed returned ").concat(vectors.length, " embeddings for ").concat(texts.length, " inputs"));
176
176
  return [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/cohere.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { fetchWithRetry } from './http.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 96;\n\n// Cohere's native /v2/embed: input_type distinguishes doc vs query embeddings, which the\n// OpenAI-compatible shape cannot express.\nexport async function cohereProvider(model: string, url: string | undefined, keyEnv: string | undefined): Promise<EmbedProvider> {\n const base = (url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n const key = keyEnv ? process.env[keyEnv] : undefined;\n if (key) headers.authorization = `Bearer ${key}`;\n\n async function post(texts: string[], inputType: 'search_document' | 'search_query'): Promise<Float32Array[]> {\n const res = await fetchWithRetry(`${base}/v2/embed`, { method: 'POST', headers, body: JSON.stringify({ model, input_type: inputType, texts, embedding_types: ['float'] }) });\n if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/v2/embed -> HTTP ${res.status}`);\n const body = (await res.json()) as { embeddings: { float: number[][] } };\n // Cohere returns one vector per text in order, with no index field to re-sort by; the\n // count is what a caller mapping positionally still has to be able to trust.\n const vectors = body.embeddings.float;\n if (vectors.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/v2/embed returned ${vectors.length} embeddings for ${texts.length} inputs`);\n return vectors.map((v) => Float32Array.from(v));\n }\n\n const dims = (await post(['dimension probe'], 'search_query'))[0].length;\n return { id: `cohere:${base}:${model}`, dims, batchCap: BATCH_CAP, embedDocuments: (texts) => post(texts, 'search_document'), embedQuery: async (text) => (await post([text], 'search_query'))[0] };\n}\n"],"names":["cohereProvider","BATCH_CAP","model","url","keyEnv","base","headers","key","dims","post","texts","inputType","res","body","vectors","fetchWithRetry","method","JSON","stringify","input_type","embedding_types","ok","SenseError","status","json","embeddings","float","length","map","v","Float32Array","from","replace","process","env","undefined","authorization","id","batchCap","embedDocuments","embedQuery","text"],"mappings":";;;;+BAQsBA;;;eAAAA;;;wBARK;sBACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG/B,IAAMC,YAAY;AAIX,SAAeD,eAAeE,KAAa,EAAEC,GAAuB,EAAEC,MAA0B;;YAC/FC,MACAC,SACAC,KAcAC;QAXN,SAAeC,KAAKC,KAAe,EAAEC,SAA6C;;oBAC1EC,KAEAC,MAGAC;;;;4BALM;;gCAAMC,IAAAA,sBAAc,EAAC,AAAC,GAAO,OAALV,MAAK,cAAY;oCAAEW,QAAQ;oCAAQV,SAAAA;oCAASO,MAAMI,KAAKC,SAAS,CAAC;wCAAEhB,OAAAA;wCAAOiB,YAAYR;wCAAWD,OAAAA;wCAAOU,eAAe;4CAAG;;oCAAS;gCAAG;;;4BAApKR,MAAM;4BACZ,IAAI,CAACA,IAAIS,EAAE,EAAE,MAAM,IAAIC,oBAAU,CAAC,eAAe,AAAC,GAA2BV,OAAzBP,MAAK,sBAA+B,OAAXO,IAAIW,MAAM;4BACzE;;gCAAMX,IAAIY,IAAI;;;4BAAtBX,OAAQ;4BACd,sFAAsF;4BACtF,6EAA6E;4BACvEC,UAAUD,KAAKY,UAAU,CAACC,KAAK;4BACrC,IAAIZ,QAAQa,MAAM,KAAKjB,MAAMiB,MAAM,EAAE,MAAM,IAAIL,oBAAU,CAAC,eAAe,AAAC,GAA4BR,OAA1BT,MAAK,uBAAsDK,OAAjCI,QAAQa,MAAM,EAAC,oBAA+B,OAAbjB,MAAMiB,MAAM,EAAC;4BACpJ;;gCAAOb,QAAQc,GAAG,CAAC,SAACC;2CAAMC,aAAaC,IAAI,CAACF;;;;;YAC9C;;;;;oBAdMxB,OAAO,CAACF,gBAAAA,iBAAAA,MAAO,0BAA0B6B,OAAO,CAAC,QAAQ;oBACzD1B,UAAkC;wBAAE,gBAAgB;oBAAmB;oBACvEC,MAAMH,SAAS6B,QAAQC,GAAG,CAAC9B,OAAO,GAAG+B;oBAC3C,IAAI5B,KAAKD,QAAQ8B,aAAa,GAAG,AAAC,UAAa,OAAJ7B;oBAa7B;;wBAAME;4BAAM;2BAAoB;;;oBAAxCD,OAAO,AAAC,aAAgD,CAAC,EAAE,CAACmB,MAAM;oBACxE;;wBAAO;4BAAEU,IAAI,AAAC,UAAiBnC,OAARG,MAAK,KAAS,OAANH;4BAASM,MAAAA;4BAAM8B,UAAUrC;4BAAWsC,gBAAgB,SAAhBA,eAAiB7B;uCAAUD,KAAKC,OAAO;;4BAAoB8B,YAAY,SAAZA,WAAmBC;;;;;gDAAU;;oDAAMhC;wDAAMgC;uDAAO;;;;;oDAAnB,aAAmC,CAAC,EAAE;;;;;;wBAAC;;;;IACpM"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/cohere.ts"],"sourcesContent":["import { SenseError } from '../errors.ts';\nimport { fetchWithRetry } from './http.ts';\nimport type { EmbedProvider } from './types.ts';\n\nconst BATCH_CAP = 96;\n\n// Cohere's native /v2/embed: input_type distinguishes doc vs query embeddings, which the\n// OpenAI-compatible shape cannot express.\nexport async function cohereProvider(model: string, url: string | undefined, keyEnv: string | undefined): Promise<EmbedProvider> {\n const base = (url ?? 'https://api.cohere.com').replace(/\\/+$/, '');\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n const key = keyEnv ? process.env[keyEnv] : undefined;\n if (key) headers.authorization = `Bearer ${key}`;\n\n async function post(texts: string[], inputType: 'search_document' | 'search_query'): Promise<Float32Array[]> {\n const res = await fetchWithRetry(`${base}/v2/embed`, { method: 'POST', headers, body: JSON.stringify({ model, input_type: inputType, texts, embedding_types: ['float'] }) });\n if (!res.ok) throw new SenseError('EMBED_MODEL', `${base}/v2/embed -> HTTP ${res.status}`);\n const body = (await res.json()) as { embeddings: { float: number[][] } };\n // Cohere returns one vector per text in order, with no index field to re-sort by; a caller\n // maps them positionally, so the count below must be trustworthy.\n const vectors = body.embeddings.float;\n if (vectors.length !== texts.length) throw new SenseError('EMBED_MODEL', `${base}/v2/embed returned ${vectors.length} embeddings for ${texts.length} inputs`);\n return vectors.map((v) => Float32Array.from(v));\n }\n\n const dims = (await post(['dimension probe'], 'search_query'))[0].length;\n return { id: `cohere:${base}:${model}`, dims, batchCap: BATCH_CAP, embedDocuments: (texts) => post(texts, 'search_document'), embedQuery: async (text) => (await post([text], 'search_query'))[0] };\n}\n"],"names":["cohereProvider","BATCH_CAP","model","url","keyEnv","base","headers","key","dims","post","texts","inputType","res","body","vectors","fetchWithRetry","method","JSON","stringify","input_type","embedding_types","ok","SenseError","status","json","embeddings","float","length","map","v","Float32Array","from","replace","process","env","undefined","authorization","id","batchCap","embedDocuments","embedQuery","text"],"mappings":";;;;+BAQsBA;;;eAAAA;;;wBARK;sBACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAG/B,IAAMC,YAAY;AAIX,SAAeD,eAAeE,KAAa,EAAEC,GAAuB,EAAEC,MAA0B;;YAC/FC,MACAC,SACAC,KAcAC;QAXN,SAAeC,KAAKC,KAAe,EAAEC,SAA6C;;oBAC1EC,KAEAC,MAGAC;;;;4BALM;;gCAAMC,IAAAA,sBAAc,EAAC,AAAC,GAAO,OAALV,MAAK,cAAY;oCAAEW,QAAQ;oCAAQV,SAAAA;oCAASO,MAAMI,KAAKC,SAAS,CAAC;wCAAEhB,OAAAA;wCAAOiB,YAAYR;wCAAWD,OAAAA;wCAAOU,eAAe;4CAAG;;oCAAS;gCAAG;;;4BAApKR,MAAM;4BACZ,IAAI,CAACA,IAAIS,EAAE,EAAE,MAAM,IAAIC,oBAAU,CAAC,eAAe,AAAC,GAA2BV,OAAzBP,MAAK,sBAA+B,OAAXO,IAAIW,MAAM;4BACzE;;gCAAMX,IAAIY,IAAI;;;4BAAtBX,OAAQ;4BACd,2FAA2F;4BAC3F,kEAAkE;4BAC5DC,UAAUD,KAAKY,UAAU,CAACC,KAAK;4BACrC,IAAIZ,QAAQa,MAAM,KAAKjB,MAAMiB,MAAM,EAAE,MAAM,IAAIL,oBAAU,CAAC,eAAe,AAAC,GAA4BR,OAA1BT,MAAK,uBAAsDK,OAAjCI,QAAQa,MAAM,EAAC,oBAA+B,OAAbjB,MAAMiB,MAAM,EAAC;4BACpJ;;gCAAOb,QAAQc,GAAG,CAAC,SAACC;2CAAMC,aAAaC,IAAI,CAACF;;;;;YAC9C;;;;;oBAdMxB,OAAO,CAACF,gBAAAA,iBAAAA,MAAO,0BAA0B6B,OAAO,CAAC,QAAQ;oBACzD1B,UAAkC;wBAAE,gBAAgB;oBAAmB;oBACvEC,MAAMH,SAAS6B,QAAQC,GAAG,CAAC9B,OAAO,GAAG+B;oBAC3C,IAAI5B,KAAKD,QAAQ8B,aAAa,GAAG,AAAC,UAAa,OAAJ7B;oBAa7B;;wBAAME;4BAAM;2BAAoB;;;oBAAxCD,OAAO,AAAC,aAAgD,CAAC,EAAE,CAACmB,MAAM;oBACxE;;wBAAO;4BAAEU,IAAI,AAAC,UAAiBnC,OAARG,MAAK,KAAS,OAANH;4BAASM,MAAAA;4BAAM8B,UAAUrC;4BAAWsC,gBAAgB,SAAhBA,eAAiB7B;uCAAUD,KAAKC,OAAO;;4BAAoB8B,YAAY,SAAZA,WAAmBC;;;;;gDAAU;;oDAAMhC;wDAAMgC;uDAAO;;;;;oDAAnB,aAAmC,CAAC,EAAE;;;;;;wBAAC;;;;IACpM"}
@@ -16,7 +16,7 @@ _export(exports, {
16
16
  return saveLanguageDistribution;
17
17
  }
18
18
  });
19
- var _metats = require("../store/meta.js");
19
+ var _sharedts = require("../store/shared.js");
20
20
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
21
21
  try {
22
22
  var info = gen[key](arg);
@@ -153,7 +153,7 @@ function languageDistribution(store) {
153
153
  case 0:
154
154
  return [
155
155
  4,
156
- (0, _metats.getMeta)(store, META_KEY)
156
+ (0, _sharedts.getMeta)(store, META_KEY)
157
157
  ];
158
158
  case 1:
159
159
  raw = _state.sent();
@@ -172,7 +172,7 @@ function saveLanguageDistribution(store, counts) {
172
172
  case 0:
173
173
  return [
174
174
  4,
175
- (0, _metats.setMeta)(store, META_KEY, JSON.stringify(counts))
175
+ (0, _sharedts.setMeta)(store, META_KEY, JSON.stringify(counts))
176
176
  ];
177
177
  case 1:
178
178
  _state.sent();
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/distribution.ts"],"sourcesContent":["import { getMeta, setMeta } from '../store/meta.ts';\nimport type { Store } from '../store/types.ts';\n\n// Split out of langfit.ts so reading the persisted counts (status.ts's only need) never pulls in\n// franc-min; only checkLanguageFit's classify path needs the classifier itself.\nconst META_KEY = 'embed_languages';\n\nexport type LangCounts = Record<string, number>;\n\nexport async function languageDistribution(store: Store): Promise<LangCounts | undefined> {\n const raw = await getMeta(store, META_KEY);\n return raw ? JSON.parse(raw) : undefined;\n}\n\nexport async function saveLanguageDistribution(store: Store, counts: LangCounts): Promise<void> {\n await setMeta(store, META_KEY, JSON.stringify(counts));\n}\n"],"names":["languageDistribution","saveLanguageDistribution","META_KEY","store","raw","getMeta","JSON","parse","undefined","counts","setMeta","stringify"],"mappings":";;;;;;;;;;;QASsBA;eAAAA;;QAKAC;eAAAA;;;sBAdW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjC,iGAAiG;AACjG,gFAAgF;AAChF,IAAMC,WAAW;AAIV,SAAeF,qBAAqBG,KAAY;;YAC/CC;;;;oBAAM;;wBAAMC,IAAAA,eAAO,EAACF,OAAOD;;;oBAA3BE,MAAM;oBACZ;;wBAAOA,MAAME,KAAKC,KAAK,CAACH,OAAOI;;;;IACjC;;AAEO,SAAeP,yBAAyBE,KAAY,EAAEM,MAAkB;;;;;oBAC7E;;wBAAMC,IAAAA,eAAO,EAACP,OAAOD,UAAUI,KAAKK,SAAS,CAACF;;;oBAA9C;;;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/distribution.ts"],"sourcesContent":["import { getMeta, setMeta } from '../store/shared.ts';\nimport type { Store } from '../store/types.ts';\n\n// Split out of langfit.ts so reading the persisted counts (status.ts's only need) never pulls in\n// franc-min; only checkLanguageFit's classify path needs the classifier itself.\nconst META_KEY = 'embed_languages';\n\nexport type LangCounts = Record<string, number>;\n\nexport async function languageDistribution(store: Store): Promise<LangCounts | undefined> {\n const raw = await getMeta(store, META_KEY);\n return raw ? JSON.parse(raw) : undefined;\n}\n\nexport async function saveLanguageDistribution(store: Store, counts: LangCounts): Promise<void> {\n await setMeta(store, META_KEY, JSON.stringify(counts));\n}\n"],"names":["languageDistribution","saveLanguageDistribution","META_KEY","store","raw","getMeta","JSON","parse","undefined","counts","setMeta","stringify"],"mappings":";;;;;;;;;;;QASsBA;eAAAA;;QAKAC;eAAAA;;;wBAdW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGjC,iGAAiG;AACjG,gFAAgF;AAChF,IAAMC,WAAW;AAIV,SAAeF,qBAAqBG,KAAY;;YAC/CC;;;;oBAAM;;wBAAMC,IAAAA,iBAAO,EAACF,OAAOD;;;oBAA3BE,MAAM;oBACZ;;wBAAOA,MAAME,KAAKC,KAAK,CAACH,OAAOI;;;;IACjC;;AAEO,SAAeP,yBAAyBE,KAAY,EAAEM,MAAkB;;;;;oBAC7E;;wBAAMC,IAAAA,iBAAO,EAACP,OAAOD,UAAUI,KAAKK,SAAS,CAACF;;;oBAA9C;;;;;;IACF"}
@@ -0,0 +1,3 @@
1
+ export declare function stashChunkText(key: object, texts: Map<string, string[]>): void;
2
+ export declare function rekeyChunkText(from: object, to: object): void;
3
+ export declare function takeChunkText(key: object): Map<string, string[]> | undefined;
@@ -0,0 +1,3 @@
1
+ export declare function stashChunkText(key: object, texts: Map<string, string[]>): void;
2
+ export declare function rekeyChunkText(from: object, to: object): void;
3
+ export declare function takeChunkText(key: object): Map<string, string[]> | undefined;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get rekeyChunkText () {
13
+ return rekeyChunkText;
14
+ },
15
+ get stashChunkText () {
16
+ return stashChunkText;
17
+ },
18
+ get takeChunkText () {
19
+ return takeChunkText;
20
+ }
21
+ });
22
+ // Chunk text from reconcile to embed time when both run in one process, so the text is neither
23
+ // re-derived nor persisted. Keyed by the store it was stashed against, so an entry dies with it.
24
+ var HANDOFF = new WeakMap();
25
+ function stashChunkText(key, texts) {
26
+ if (texts.size > 0) HANDOFF.set(key, texts);
27
+ }
28
+ function rekeyChunkText(from, to) {
29
+ var texts = HANDOFF.get(from);
30
+ if (!texts) return;
31
+ HANDOFF.delete(from);
32
+ HANDOFF.set(to, texts);
33
+ }
34
+ function takeChunkText(key) {
35
+ var texts = HANDOFF.get(key);
36
+ HANDOFF.delete(key);
37
+ return texts;
38
+ }
39
+ /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/handoff.ts"],"sourcesContent":["// Chunk text from reconcile to embed time when both run in one process, so the text is neither\n// re-derived nor persisted. Keyed by the store it was stashed against, so an entry dies with it.\nconst HANDOFF = new WeakMap<object, Map<string, string[]>>();\n\nexport function stashChunkText(key: object, texts: Map<string, string[]>): void {\n if (texts.size > 0) HANDOFF.set(key, texts);\n}\n\n// Moves an entry from one key to another: reconcile runs before the store object exists, so it\n// stashes against the connection and open() re-keys onto the store it then builds.\nexport function rekeyChunkText(from: object, to: object): void {\n const texts = HANDOFF.get(from);\n if (!texts) return;\n HANDOFF.delete(from);\n HANDOFF.set(to, texts);\n}\n\n// Read once and drop: the text is consumed by the embed pass that follows reconcile, and holding\n// it past that would pin the tree's prose in memory for the life of the store.\nexport function takeChunkText(key: object): Map<string, string[]> | undefined {\n const texts = HANDOFF.get(key);\n HANDOFF.delete(key);\n return texts;\n}\n"],"names":["rekeyChunkText","stashChunkText","takeChunkText","HANDOFF","WeakMap","key","texts","size","set","from","to","get","delete"],"mappings":";;;;;;;;;;;QAUgBA;eAAAA;;QANAC;eAAAA;;QAeAC;eAAAA;;;AAnBhB,+FAA+F;AAC/F,iGAAiG;AACjG,IAAMC,UAAU,IAAIC;AAEb,SAASH,eAAeI,GAAW,EAAEC,KAA4B;IACtE,IAAIA,MAAMC,IAAI,GAAG,GAAGJ,QAAQK,GAAG,CAACH,KAAKC;AACvC;AAIO,SAASN,eAAeS,IAAY,EAAEC,EAAU;IACrD,IAAMJ,QAAQH,QAAQQ,GAAG,CAACF;IAC1B,IAAI,CAACH,OAAO;IACZH,QAAQS,MAAM,CAACH;IACfN,QAAQK,GAAG,CAACE,IAAIJ;AAClB;AAIO,SAASJ,cAAcG,GAAW;IACvC,IAAMC,QAAQH,QAAQQ,GAAG,CAACN;IAC1BF,QAAQS,MAAM,CAACP;IACf,OAAOC;AACT"}
@@ -1,13 +1,14 @@
1
1
  export declare const MODEL_FILES: string[];
2
2
  export declare const MODEL_FILENAMES: string;
3
3
  export declare function isDownloadable(model: string): boolean;
4
- export declare function refsMainPath(model: string): string;
5
- export declare function readRef(model: string): string | undefined;
6
- export declare function writeRef(model: string, sha: string): void;
7
- export declare function snapshotDir(model: string, sha: string): string;
8
- export declare function languagesPath(model: string): string;
9
- export declare function readLanguages(model: string): string[] | undefined;
10
- export declare function writeLanguages(model: string, languages: string[]): void;
11
- export declare function modelDir(model: string): string;
12
- export declare function hasModelFiles(model: string): boolean;
13
- export declare function modelIdentity(model: string): string | undefined;
4
+ export declare const DEFAULT_MODELS_ROOT: string;
5
+ export declare function refsMainPath(model: string, root?: string): string;
6
+ export declare function readRef(model: string, root?: string): string | undefined;
7
+ export declare function writeRef(model: string, sha: string, root?: string): void;
8
+ export declare function snapshotDir(model: string, sha: string, root?: string): string;
9
+ export declare function languagesPath(model: string, root?: string): string;
10
+ export declare function readLanguages(model: string, root?: string): string[] | undefined;
11
+ export declare function writeLanguages(model: string, languages: string[], root?: string): void;
12
+ export declare function modelDir(model: string, root?: string): string;
13
+ export declare function hasModelFiles(model: string, root?: string): boolean;
14
+ export declare function modelIdentity(model: string, root?: string): string | undefined;
@@ -1,13 +1,14 @@
1
1
  export declare const MODEL_FILES: string[];
2
2
  export declare const MODEL_FILENAMES: string;
3
3
  export declare function isDownloadable(model: string): boolean;
4
- export declare function refsMainPath(model: string): string;
5
- export declare function readRef(model: string): string | undefined;
6
- export declare function writeRef(model: string, sha: string): void;
7
- export declare function snapshotDir(model: string, sha: string): string;
8
- export declare function languagesPath(model: string): string;
9
- export declare function readLanguages(model: string): string[] | undefined;
10
- export declare function writeLanguages(model: string, languages: string[]): void;
11
- export declare function modelDir(model: string): string;
12
- export declare function hasModelFiles(model: string): boolean;
13
- export declare function modelIdentity(model: string): string | undefined;
4
+ export declare const DEFAULT_MODELS_ROOT: string;
5
+ export declare function refsMainPath(model: string, root?: string): string;
6
+ export declare function readRef(model: string, root?: string): string | undefined;
7
+ export declare function writeRef(model: string, sha: string, root?: string): void;
8
+ export declare function snapshotDir(model: string, sha: string, root?: string): string;
9
+ export declare function languagesPath(model: string, root?: string): string;
10
+ export declare function readLanguages(model: string, root?: string): string[] | undefined;
11
+ export declare function writeLanguages(model: string, languages: string[], root?: string): void;
12
+ export declare function modelDir(model: string, root?: string): string;
13
+ export declare function hasModelFiles(model: string, root?: string): boolean;
14
+ export declare function modelIdentity(model: string, root?: string): string | undefined;
@@ -9,6 +9,9 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
+ get DEFAULT_MODELS_ROOT () {
13
+ return DEFAULT_MODELS_ROOT;
14
+ },
12
15
  get MODEL_FILENAMES () {
13
16
  return MODEL_FILENAMES;
14
17
  },
@@ -110,33 +113,41 @@ var HF_MODEL_ID = /^[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*$/;
110
113
  function isDownloadable(model) {
111
114
  return HF_MODEL_ID.test(model);
112
115
  }
116
+ var DEFAULT_MODELS_ROOT = (0, _nodepath.join)((0, _nodeos.homedir)(), '.sense', 'models');
113
117
  function repoDir(model) {
118
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
114
119
  var _model_split = _sliced_to_array(model.split('/'), 2), org = _model_split[0], name = _model_split[1];
115
- return (0, _nodepath.join)((0, _nodeos.homedir)(), '.sense', 'models', "models--".concat(org, "--").concat(name));
120
+ return (0, _nodepath.join)(root, "models--".concat(org, "--").concat(name));
116
121
  }
117
122
  function refsMainPath(model) {
118
- return (0, _nodepath.join)(repoDir(model), 'refs', 'main');
123
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
124
+ return (0, _nodepath.join)(repoDir(model, root), 'refs', 'main');
119
125
  }
120
126
  function readRef(model) {
121
- var p = refsMainPath(model);
127
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
128
+ var p = refsMainPath(model, root);
122
129
  if (!(0, _nodefs.existsSync)(p)) return undefined;
123
130
  var sha = (0, _nodefs.readFileSync)(p, 'utf8').trim();
124
131
  return sha.length > 0 ? sha : undefined;
125
132
  }
126
133
  function writeRef(model, sha) {
127
- (0, _nodefs.mkdirSync)((0, _nodepath.join)(repoDir(model), 'refs'), {
134
+ var root = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : DEFAULT_MODELS_ROOT;
135
+ (0, _nodefs.mkdirSync)((0, _nodepath.join)(repoDir(model, root), 'refs'), {
128
136
  recursive: true
129
137
  });
130
- (0, _atomicwritets.writeFileAtomic)(refsMainPath(model), sha);
138
+ (0, _atomicwritets.writeFileAtomic)(refsMainPath(model, root), sha);
131
139
  }
132
140
  function snapshotDir(model, sha) {
133
- return (0, _nodepath.join)(repoDir(model), 'snapshots', sha);
141
+ var root = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : DEFAULT_MODELS_ROOT;
142
+ return (0, _nodepath.join)(repoDir(model, root), 'snapshots', sha);
134
143
  }
135
144
  function languagesPath(model) {
136
- return (0, _nodepath.join)(repoDir(model), 'languages.json');
145
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
146
+ return (0, _nodepath.join)(repoDir(model, root), 'languages.json');
137
147
  }
138
148
  function readLanguages(model) {
139
- var p = languagesPath(model);
149
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
150
+ var p = languagesPath(model, root);
140
151
  if (!(0, _nodefs.existsSync)(p)) return undefined;
141
152
  try {
142
153
  return JSON.parse((0, _nodefs.readFileSync)(p, 'utf8'));
@@ -145,25 +156,29 @@ function readLanguages(model) {
145
156
  }
146
157
  }
147
158
  function writeLanguages(model, languages) {
148
- (0, _nodefs.mkdirSync)(repoDir(model), {
159
+ var root = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : DEFAULT_MODELS_ROOT;
160
+ (0, _nodefs.mkdirSync)(repoDir(model, root), {
149
161
  recursive: true
150
162
  });
151
- (0, _atomicwritets.writeFileAtomic)(languagesPath(model), JSON.stringify(languages));
163
+ (0, _atomicwritets.writeFileAtomic)(languagesPath(model, root), JSON.stringify(languages));
152
164
  }
153
165
  function modelDir(model) {
166
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
154
167
  if (!isDownloadable(model)) return model;
155
- var sha = readRef(model);
156
- return sha ? snapshotDir(model, sha) : repoDir(model);
168
+ var sha = readRef(model, root);
169
+ return sha ? snapshotDir(model, sha, root) : repoDir(model, root);
157
170
  }
158
171
  function hasModelFiles(model) {
159
- var dir = modelDir(model);
172
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
173
+ var dir = modelDir(model, root);
160
174
  return MODEL_FILES.every(function(file) {
161
175
  return (0, _nodefs.existsSync)((0, _nodepath.join)(dir, file));
162
176
  });
163
177
  }
164
178
  function modelIdentity(model) {
165
- if (isDownloadable(model)) return readRef(model);
166
- var dir = modelDir(model);
179
+ var root = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_MODELS_ROOT;
180
+ if (isDownloadable(model)) return readRef(model, root);
181
+ var dir = modelDir(model, root);
167
182
  var parts = [];
168
183
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
169
184
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/identity.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { writeFileAtomic } from '../lib/atomic-write.ts';\n\n// HF cache layout, no network: ~/.sense/models/models--<org>--<name>/{refs/main,\n// snapshots/<sha>/<files>}, files stored directly (hf_hub's blobs/symlinks layer skipped).\n\nexport const MODEL_FILES = ['model.safetensors', 'tokenizer.json'];\n// The pair, for messages that name what a local model directory must contain.\nexport const MODEL_FILENAMES = MODEL_FILES.join(' and ');\n\n// A Hugging Face repo id: one slash, HF's charset. Anything else is a path, used as one rather\n// than resolved against the HF cache.\nconst HF_MODEL_ID = /^[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*$/;\n\nexport function isDownloadable(model: string): boolean {\n return HF_MODEL_ID.test(model);\n}\n\nfunction repoDir(model: string): string {\n const [org, name] = model.split('/');\n return join(homedir(), '.sense', 'models', `models--${org}--${name}`);\n}\n\nexport function refsMainPath(model: string): string {\n return join(repoDir(model), 'refs', 'main');\n}\n\n// What `main` last resolved to, read from disk -- no network. Undefined until some resolution\n// (a download or an explicit prefetch) has written it once.\nexport function readRef(model: string): string | undefined {\n const p = refsMainPath(model);\n if (!existsSync(p)) return undefined;\n const sha = readFileSync(p, 'utf8').trim();\n return sha.length > 0 ? sha : undefined;\n}\n\n// A recorded ref pins until its snapshot directory is removed; nothing here re-resolves it.\nexport function writeRef(model: string, sha: string): void {\n mkdirSync(join(repoDir(model), 'refs'), { recursive: true });\n writeFileAtomic(refsMainPath(model), sha);\n}\n\nexport function snapshotDir(model: string, sha: string): string {\n return join(repoDir(model), 'snapshots', sha);\n}\n\n// Languages describe the repo, not one weight snapshot, so this lives beside refs/, not\n// inside a snapshot dir.\nexport function languagesPath(model: string): string {\n return join(repoDir(model), 'languages.json');\n}\n\n// undefined means \"never resolved yet\"; an empty array is a resolved fact (checked, none\n// declared) and is cached the same way, so a languageless model is not re-fetched every run.\nexport function readLanguages(model: string): string[] | undefined {\n const p = languagesPath(model);\n if (!existsSync(p)) return undefined;\n try {\n return JSON.parse(readFileSync(p, 'utf8'));\n } catch {\n return undefined;\n }\n}\n\nexport function writeLanguages(model: string, languages: string[]): void {\n mkdirSync(repoDir(model), { recursive: true });\n writeFileAtomic(languagesPath(model), JSON.stringify(languages));\n}\n\n// A local directory the caller manages, or the resolved snapshot once `main` is pinned; an HF\n// id with no recorded ref yet has no known snapshot, so this names the (fileless) repo dir.\nexport function modelDir(model: string): string {\n if (!isDownloadable(model)) return model;\n const sha = readRef(model);\n return sha ? snapshotDir(model, sha) : repoDir(model);\n}\n\n// Both files, so an interrupted download reads as absent and the next one resumes it.\nexport function hasModelFiles(model: string): boolean {\n const dir = modelDir(model);\n return MODEL_FILES.every((file) => existsSync(join(dir, file)));\n}\n\n// Weight identity for the signature, no network: a HF id's resolved sha, or the two\n// files' size+mtime for a local path. Undefined when nothing is known yet.\nexport function modelIdentity(model: string): string | undefined {\n if (isDownloadable(model)) return readRef(model);\n const dir = modelDir(model);\n const parts: string[] = [];\n for (const file of MODEL_FILES) {\n try {\n const st = statSync(join(dir, file));\n parts.push(`${st.size}:${Math.trunc(st.mtimeMs)}`);\n } catch {\n return undefined;\n }\n }\n return parts.join(',');\n}\n"],"names":["MODEL_FILENAMES","MODEL_FILES","hasModelFiles","isDownloadable","languagesPath","modelDir","modelIdentity","readLanguages","readRef","refsMainPath","snapshotDir","writeLanguages","writeRef","join","HF_MODEL_ID","model","test","repoDir","split","org","name","homedir","p","existsSync","undefined","sha","readFileSync","trim","length","mkdirSync","recursive","writeFileAtomic","JSON","parse","languages","stringify","dir","every","file","parts","st","statSync","push","Math","size","trunc","mtimeMs"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/identity.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { writeFileAtomic } from '../lib/atomic-write.ts';\n\n// HF cache layout, no network: ~/.sense/models/models--<org>--<name>/{refs/main,\n// snapshots/<sha>/<files>}, files stored directly (hf_hub's blobs/symlinks layer skipped).\n\nexport const MODEL_FILES = ['model.safetensors', 'tokenizer.json'];\n// The pair, for messages that name what a local model directory must contain.\nexport const MODEL_FILENAMES = MODEL_FILES.join(' and ');\n\n// A Hugging Face repo id: one slash, HF's charset. Anything else is a path, used as one rather\n// than resolved against the HF cache.\nconst HF_MODEL_ID = /^[A-Za-z0-9][\\w.-]*\\/[A-Za-z0-9][\\w.-]*$/;\n\nexport function isDownloadable(model: string): boolean {\n return HF_MODEL_ID.test(model);\n}\n\n// The one production default; tests pass their own scratch root through the same parameter\n// rather than seeding fixture ids into this machine cache.\nexport const DEFAULT_MODELS_ROOT = join(homedir(), '.sense', 'models');\n\nfunction repoDir(model: string, root: string = DEFAULT_MODELS_ROOT): string {\n const [org, name] = model.split('/');\n return join(root, `models--${org}--${name}`);\n}\n\nexport function refsMainPath(model: string, root: string = DEFAULT_MODELS_ROOT): string {\n return join(repoDir(model, root), 'refs', 'main');\n}\n\n// What `main` last resolved to, read from disk -- no network. Undefined until some resolution\n// (a download or an explicit prefetch) has written it once.\nexport function readRef(model: string, root: string = DEFAULT_MODELS_ROOT): string | undefined {\n const p = refsMainPath(model, root);\n if (!existsSync(p)) return undefined;\n const sha = readFileSync(p, 'utf8').trim();\n return sha.length > 0 ? sha : undefined;\n}\n\n// A recorded ref pins until its snapshot directory is removed; nothing here re-resolves it.\nexport function writeRef(model: string, sha: string, root: string = DEFAULT_MODELS_ROOT): void {\n mkdirSync(join(repoDir(model, root), 'refs'), { recursive: true });\n writeFileAtomic(refsMainPath(model, root), sha);\n}\n\nexport function snapshotDir(model: string, sha: string, root: string = DEFAULT_MODELS_ROOT): string {\n return join(repoDir(model, root), 'snapshots', sha);\n}\n\n// Languages describe the repo, not one weight snapshot, so this lives beside refs/, not\n// inside a snapshot dir.\nexport function languagesPath(model: string, root: string = DEFAULT_MODELS_ROOT): string {\n return join(repoDir(model, root), 'languages.json');\n}\n\n// undefined means \"never resolved yet\"; an empty array is a resolved fact (checked, none\n// declared) and is cached the same way, so a languageless model is not re-fetched every run.\nexport function readLanguages(model: string, root: string = DEFAULT_MODELS_ROOT): string[] | undefined {\n const p = languagesPath(model, root);\n if (!existsSync(p)) return undefined;\n try {\n return JSON.parse(readFileSync(p, 'utf8'));\n } catch {\n return undefined;\n }\n}\n\nexport function writeLanguages(model: string, languages: string[], root: string = DEFAULT_MODELS_ROOT): void {\n mkdirSync(repoDir(model, root), { recursive: true });\n writeFileAtomic(languagesPath(model, root), JSON.stringify(languages));\n}\n\n// A local directory the caller manages, or the resolved snapshot once `main` is pinned; an HF\n// id with no recorded ref yet has no known snapshot, so this names the (fileless) repo dir.\nexport function modelDir(model: string, root: string = DEFAULT_MODELS_ROOT): string {\n if (!isDownloadable(model)) return model;\n const sha = readRef(model, root);\n return sha ? snapshotDir(model, sha, root) : repoDir(model, root);\n}\n\n// Both files, so an interrupted download reads as absent and the next one resumes it.\nexport function hasModelFiles(model: string, root: string = DEFAULT_MODELS_ROOT): boolean {\n const dir = modelDir(model, root);\n return MODEL_FILES.every((file) => existsSync(join(dir, file)));\n}\n\n// Weight identity for the signature, no network: a HF id's resolved sha, or the two\n// files' size+mtime for a local path. Undefined when nothing is known yet.\nexport function modelIdentity(model: string, root: string = DEFAULT_MODELS_ROOT): string | undefined {\n if (isDownloadable(model)) return readRef(model, root);\n const dir = modelDir(model, root);\n const parts: string[] = [];\n for (const file of MODEL_FILES) {\n try {\n const st = statSync(join(dir, file));\n parts.push(`${st.size}:${Math.trunc(st.mtimeMs)}`);\n } catch {\n return undefined;\n }\n }\n return parts.join(',');\n}\n"],"names":["DEFAULT_MODELS_ROOT","MODEL_FILENAMES","MODEL_FILES","hasModelFiles","isDownloadable","languagesPath","modelDir","modelIdentity","readLanguages","readRef","refsMainPath","snapshotDir","writeLanguages","writeRef","join","HF_MODEL_ID","model","test","homedir","repoDir","root","split","org","name","p","existsSync","undefined","sha","readFileSync","trim","length","mkdirSync","recursive","writeFileAtomic","JSON","parse","languages","stringify","dir","every","file","parts","st","statSync","push","Math","size","trunc","mtimeMs"],"mappings":"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"}
@@ -220,9 +220,8 @@ var SAMPLE_CHARS = 300;
220
220
  // A majority computed over a handful of chunks is noise, not a corpus signal; the decision rule
221
221
  // never fires below this many classified chunks, declared languages or not.
222
222
  var MIN_CLASSIFIED = 10;
223
- // franc-min is a 148K static import; a top-level import put it on every command's module graph
224
- // regardless of whether the semantic path ran, shipped as the 0.17.0 regression. Deferred to a
225
- // synchronous require inside checkLanguageFit so only the embed path pays for it.
223
+ // franc-min is a 148K static import; a top-level import would put it on every command's module
224
+ // graph regardless of whether the semantic path ran, so it's deferred to a synchronous require.
226
225
  var _require = typeof require === 'undefined' ? _nodemodule.default.createRequire(require("url").pathToFileURL(__filename).toString()) : require;
227
226
  function classify(franc, text) {
228
227
  var code = franc(text.slice(0, SAMPLE_CHARS));
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/langfit.ts"],"sourcesContent":["import Module from 'node:module';\nimport { SenseError } from '../errors.ts';\nimport type { Store } from '../store/types.ts';\nimport { languageDistribution, saveLanguageDistribution } from './distribution.ts';\nimport { toIso3 } from './languages.ts';\nimport type { EmbedProvider } from './types.ts';\n\n// franc's own reliability floor is a handful of characters; this is a cheap upper bound, not a\n// per-chunk minimum -- a short chunk is simply more likely to come back 'und' (unclassified).\nconst SAMPLE_CHARS = 300;\n// A majority computed over a handful of chunks is noise, not a corpus signal; the decision rule\n// never fires below this many classified chunks, declared languages or not.\nconst MIN_CLASSIFIED = 10;\n\n// franc-min is a 148K static import; a top-level import put it on every command's module graph\n// regardless of whether the semantic path ran, shipped as the 0.17.0 regression. Deferred to a\n// synchronous require inside checkLanguageFit so only the embed path pays for it.\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\nfunction classify(franc: typeof import('franc-min')['franc'], text: string): string | undefined {\n const code = franc(text.slice(0, SAMPLE_CHARS));\n return code === 'und' ? undefined : code;\n}\n\n// Merges chunk texts into the persisted distribution; throws (without persisting) if the model\n// declares languages and a clear majority classified so far is not among them.\nexport async function checkLanguageFit(store: Store, provider: EmbedProvider, texts: string[]): Promise<void> {\n if (texts.length === 0) return;\n const { franc } = _require('franc-min') as typeof import('franc-min');\n const persisted = (await languageDistribution(store)) ?? {};\n const merged = { ...persisted };\n for (const text of texts) {\n const code = classify(franc, text);\n if (code) merged[code] = (merged[code] ?? 0) + 1;\n }\n\n const total = Object.values(merged).reduce((a, b) => a + b, 0);\n if (provider.languages && provider.languages.length > 0 && total >= MIN_CLASSIFIED) {\n const declared = new Set(provider.languages.map(toIso3));\n const [majorityLang, majorityCount] = Object.entries(merged).sort((a, b) => b[1] - a[1])[0];\n if (majorityCount / total >= 0.5 && !declared.has(majorityLang)) {\n const pct = Math.round((majorityCount / total) * 100);\n throw new SenseError(\n 'EMBED_MODEL_MISMATCH',\n `embed model ${provider.id} declares languages [${provider.languages.join(', ')}], but ${pct}% of this tree's classified text is \"${majorityLang}\" (ISO 639-3), which is not among them; choose a model for this tree's languages: \\`sense init --model ...\\`, see the sense-setup skill or INTEGRATIONS.md`\n );\n }\n }\n await saveLanguageDistribution(store, merged);\n}\n"],"names":["checkLanguageFit","SAMPLE_CHARS","MIN_CLASSIFIED","_require","require","Module","createRequire","classify","franc","text","code","slice","undefined","store","provider","texts","persisted","merged","total","declared","Object","majorityLang","majorityCount","pct","length","languageDistribution","values","reduce","a","b","languages","Set","map","toIso3","entries","sort","has","Math","round","SenseError","id","join","saveLanguageDistribution"],"mappings":";;;;+BA0BsBA;;;eAAAA;;;iEA1BH;wBACQ;8BAEoC;2BACxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGvB,+FAA+F;AAC/F,8FAA8F;AAC9F,IAAMC,eAAe;AACrB,gGAAgG;AAChG,4EAA4E;AAC5E,IAAMC,iBAAiB;AAEvB,+FAA+F;AAC/F,+FAA+F;AAC/F,kFAAkF;AAClF,IAAMC,WAAW,OAAOC,YAAY,cAAcC,mBAAM,CAACC,aAAa,CAAC,uDAAmBF;AAE1F,SAASG,SAASC,KAA0C,EAAEC,IAAY;IACxE,IAAMC,OAAOF,MAAMC,KAAKE,KAAK,CAAC,GAAGV;IACjC,OAAOS,SAAS,QAAQE,YAAYF;AACtC;AAIO,SAAeV,iBAAiBa,KAAY,EAAEC,QAAuB,EAAEC,KAAe;;YAGxE,MADXP,OACFQ,WACAC,QACD,2BAAA,mBAAA,gBAAA,WAAA,OAAMR,MAEiBQ,cADpBP,MAIFQ,OAEEC,UACgCC,uBAA/BC,cAAcC,eAEbC;;;;oBAdV,IAAIR,MAAMS,MAAM,KAAK,GAAG;;;oBAChBhB,QAAUL,SAAS,aAAnBK;oBACW;;wBAAMiB,IAAAA,oCAAoB,EAACZ;;;oBAAxCG,aAAa,OAAA,2BAAA,kBAAA,OAAsC,CAAC;oBACpDC,SAAS,mBAAKD;oBACf,kCAAA,2BAAA;;wBAAL,IAAK,YAAcD,4BAAd,6BAAA,QAAA,yBAAA,iCAAqB;4BAAfN,OAAN;;4BACGC,OAAOH,SAASC,OAAOC;4BAC7B,IAAIC,MAAMO,MAAM,CAACP,KAAK,GAAG,EAACO,eAAAA,MAAM,CAACP,KAAK,cAAZO,0BAAAA,eAAgB,KAAK;wBACjD;;wBAHK;wBAAA;;;iCAAA,6BAAA;gCAAA;;;gCAAA;sCAAA;;;;oBAKCC,QAAQE,OAAOM,MAAM,CAACT,QAAQU,MAAM,CAAC,SAACC,GAAGC;+BAAMD,IAAIC;uBAAG;oBAC5D,IAAIf,SAASgB,SAAS,IAAIhB,SAASgB,SAAS,CAACN,MAAM,GAAG,KAAKN,SAAShB,gBAAgB;wBAC5EiB,WAAW,IAAIY,IAAIjB,SAASgB,SAAS,CAACE,GAAG,CAACC,mBAAM;wBAChBb,yCAAAA,OAAOc,OAAO,CAACjB,QAAQkB,IAAI,CAAC,SAACP,GAAGC;mCAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE;0BAAC,CAAC,EAAE,MAApFP,eAA+BD,0BAAjBE,gBAAiBF;wBACtC,IAAIE,gBAAgBJ,SAAS,OAAO,CAACC,SAASiB,GAAG,CAACf,eAAe;4BACzDE,MAAMc,KAAKC,KAAK,CAAC,AAAChB,gBAAgBJ,QAAS;4BACjD,MAAM,IAAIqB,oBAAU,CAClB,wBACA,AAAC,eAAiDzB,OAAnCA,SAAS0B,EAAE,EAAC,yBAA8DjB,OAAvCT,SAASgB,SAAS,CAACW,IAAI,CAAC,OAAM,WAAoDpB,OAA3CE,KAAI,0CAAoD,OAAbF,cAAa;wBAErJ;oBACF;oBACA;;wBAAMqB,IAAAA,wCAAwB,EAAC7B,OAAOI;;;oBAAtC;;;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/langfit.ts"],"sourcesContent":["import Module from 'node:module';\nimport { SenseError } from '../errors.ts';\nimport type { Store } from '../store/types.ts';\nimport { languageDistribution, saveLanguageDistribution } from './distribution.ts';\nimport { toIso3 } from './languages.ts';\nimport type { EmbedProvider } from './types.ts';\n\n// franc's own reliability floor is a handful of characters; this is a cheap upper bound, not a\n// per-chunk minimum -- a short chunk is simply more likely to come back 'und' (unclassified).\nconst SAMPLE_CHARS = 300;\n// A majority computed over a handful of chunks is noise, not a corpus signal; the decision rule\n// never fires below this many classified chunks, declared languages or not.\nconst MIN_CLASSIFIED = 10;\n\n// franc-min is a 148K static import; a top-level import would put it on every command's module\n// graph regardless of whether the semantic path ran, so it's deferred to a synchronous require.\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\n\nfunction classify(franc: typeof import('franc-min')['franc'], text: string): string | undefined {\n const code = franc(text.slice(0, SAMPLE_CHARS));\n return code === 'und' ? undefined : code;\n}\n\n// Merges chunk texts into the persisted distribution; throws (without persisting) if the model\n// declares languages and a clear majority classified so far is not among them.\nexport async function checkLanguageFit(store: Store, provider: EmbedProvider, texts: string[]): Promise<void> {\n if (texts.length === 0) return;\n const { franc } = _require('franc-min') as typeof import('franc-min');\n const persisted = (await languageDistribution(store)) ?? {};\n const merged = { ...persisted };\n for (const text of texts) {\n const code = classify(franc, text);\n if (code) merged[code] = (merged[code] ?? 0) + 1;\n }\n\n const total = Object.values(merged).reduce((a, b) => a + b, 0);\n if (provider.languages && provider.languages.length > 0 && total >= MIN_CLASSIFIED) {\n const declared = new Set(provider.languages.map(toIso3));\n const [majorityLang, majorityCount] = Object.entries(merged).sort((a, b) => b[1] - a[1])[0];\n if (majorityCount / total >= 0.5 && !declared.has(majorityLang)) {\n const pct = Math.round((majorityCount / total) * 100);\n throw new SenseError(\n 'EMBED_MODEL_MISMATCH',\n `embed model ${provider.id} declares languages [${provider.languages.join(', ')}], but ${pct}% of this tree's classified text is \"${majorityLang}\" (ISO 639-3), which is not among them; choose a model for this tree's languages: \\`sense init --model ...\\`, see the sense-setup skill or INTEGRATIONS.md`\n );\n }\n }\n await saveLanguageDistribution(store, merged);\n}\n"],"names":["checkLanguageFit","SAMPLE_CHARS","MIN_CLASSIFIED","_require","require","Module","createRequire","classify","franc","text","code","slice","undefined","store","provider","texts","persisted","merged","total","declared","Object","majorityLang","majorityCount","pct","length","languageDistribution","values","reduce","a","b","languages","Set","map","toIso3","entries","sort","has","Math","round","SenseError","id","join","saveLanguageDistribution"],"mappings":";;;;+BAyBsBA;;;eAAAA;;;iEAzBH;wBACQ;8BAEoC;2BACxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGvB,+FAA+F;AAC/F,8FAA8F;AAC9F,IAAMC,eAAe;AACrB,gGAAgG;AAChG,4EAA4E;AAC5E,IAAMC,iBAAiB;AAEvB,+FAA+F;AAC/F,gGAAgG;AAChG,IAAMC,WAAW,OAAOC,YAAY,cAAcC,mBAAM,CAACC,aAAa,CAAC,uDAAmBF;AAE1F,SAASG,SAASC,KAA0C,EAAEC,IAAY;IACxE,IAAMC,OAAOF,MAAMC,KAAKE,KAAK,CAAC,GAAGV;IACjC,OAAOS,SAAS,QAAQE,YAAYF;AACtC;AAIO,SAAeV,iBAAiBa,KAAY,EAAEC,QAAuB,EAAEC,KAAe;;YAGxE,MADXP,OACFQ,WACAC,QACD,2BAAA,mBAAA,gBAAA,WAAA,OAAMR,MAEiBQ,cADpBP,MAIFQ,OAEEC,UACgCC,uBAA/BC,cAAcC,eAEbC;;;;oBAdV,IAAIR,MAAMS,MAAM,KAAK,GAAG;;;oBAChBhB,QAAUL,SAAS,aAAnBK;oBACW;;wBAAMiB,IAAAA,oCAAoB,EAACZ;;;oBAAxCG,aAAa,OAAA,2BAAA,kBAAA,OAAsC,CAAC;oBACpDC,SAAS,mBAAKD;oBACf,kCAAA,2BAAA;;wBAAL,IAAK,YAAcD,4BAAd,6BAAA,QAAA,yBAAA,iCAAqB;4BAAfN,OAAN;;4BACGC,OAAOH,SAASC,OAAOC;4BAC7B,IAAIC,MAAMO,MAAM,CAACP,KAAK,GAAG,EAACO,eAAAA,MAAM,CAACP,KAAK,cAAZO,0BAAAA,eAAgB,KAAK;wBACjD;;wBAHK;wBAAA;;;iCAAA,6BAAA;gCAAA;;;gCAAA;sCAAA;;;;oBAKCC,QAAQE,OAAOM,MAAM,CAACT,QAAQU,MAAM,CAAC,SAACC,GAAGC;+BAAMD,IAAIC;uBAAG;oBAC5D,IAAIf,SAASgB,SAAS,IAAIhB,SAASgB,SAAS,CAACN,MAAM,GAAG,KAAKN,SAAShB,gBAAgB;wBAC5EiB,WAAW,IAAIY,IAAIjB,SAASgB,SAAS,CAACE,GAAG,CAACC,mBAAM;wBAChBb,yCAAAA,OAAOc,OAAO,CAACjB,QAAQkB,IAAI,CAAC,SAACP,GAAGC;mCAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE;0BAAC,CAAC,EAAE,MAApFP,eAA+BD,0BAAjBE,gBAAiBF;wBACtC,IAAIE,gBAAgBJ,SAAS,OAAO,CAACC,SAASiB,GAAG,CAACf,eAAe;4BACzDE,MAAMc,KAAKC,KAAK,CAAC,AAAChB,gBAAgBJ,QAAS;4BACjD,MAAM,IAAIqB,oBAAU,CAClB,wBACA,AAAC,eAAiDzB,OAAnCA,SAAS0B,EAAE,EAAC,yBAA8DjB,OAAvCT,SAASgB,SAAS,CAACW,IAAI,CAAC,OAAM,WAAoDpB,OAA3CE,KAAI,0CAAoD,OAAbF,cAAa;wBAErJ;oBACF;oBACA;;wBAAMqB,IAAAA,wCAAwB,EAAC7B,OAAOI;;;oBAAtC;;;;;;IACF"}
@@ -19,10 +19,8 @@ _export(exports, {
19
19
  return toIso3;
20
20
  }
21
21
  });
22
- // HF model cards declare languages as ISO 639-1 tags (e.g. "zh"); franc-min reports ISO 639-3
23
- // (e.g. "cmn"). This bridges the two for comparison. Limited to franc-min's own 82-language
24
- // data (README table, https://github.com/wooorm/franc), so every value here is a code franc-min
25
- // can actually emit; a declared tag with no entry here is compared as-is.
22
+ // HF model cards declare languages as ISO 639-1 (e.g. "zh"); franc-min reports ISO 639-3 (e.g.
23
+ // "cmn"). Limited to franc-min's own 82-language table (https://github.com/wooorm/franc).
26
24
  var ISO_639_1_TO_3 = {
27
25
  ar: 'arb',
28
26
  am: 'amh',
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/languages.ts"],"sourcesContent":["// HF model cards declare languages as ISO 639-1 tags (e.g. \"zh\"); franc-min reports ISO 639-3\n// (e.g. \"cmn\"). This bridges the two for comparison. Limited to franc-min's own 82-language\n// data (README table, https://github.com/wooorm/franc), so every value here is a code franc-min\n// can actually emit; a declared tag with no entry here is compared as-is.\nconst ISO_639_1_TO_3: Record<string, string> = {\n ar: 'arb',\n am: 'amh',\n az: 'azj',\n be: 'bel',\n bg: 'bul',\n bn: 'ben',\n bs: 'bos',\n cs: 'ces',\n de: 'deu',\n el: 'ell',\n en: 'eng',\n es: 'spa',\n fa: 'pes',\n fr: 'fra',\n gu: 'guj',\n ha: 'hau',\n hi: 'hin',\n hr: 'hrv',\n hu: 'hun',\n id: 'ind',\n ig: 'ibo',\n it: 'ita',\n ja: 'jpn',\n jv: 'jav',\n kk: 'kaz',\n kn: 'kan',\n ko: 'kor',\n ml: 'mal',\n mr: 'mar',\n ms: 'zlm',\n my: 'mya',\n ne: 'npi',\n nl: 'nld',\n pa: 'pan',\n pl: 'pol',\n pt: 'por',\n ro: 'ron',\n ru: 'rus',\n rw: 'kin',\n si: 'sin',\n so: 'som',\n sr: 'srp',\n su: 'sun',\n sv: 'swe',\n sw: 'swh',\n ta: 'tam',\n te: 'tel',\n th: 'tha',\n tl: 'tgl',\n tr: 'tur',\n uk: 'ukr',\n ur: 'urd',\n uz: 'uzn',\n vi: 'vie',\n yo: 'yor',\n zh: 'cmn',\n zu: 'zul',\n};\n\nexport function isKnownLanguageTag(tag: string): boolean {\n return tag in ISO_639_1_TO_3;\n}\n\n// Normalizes a declared or detected language code to ISO 639-3 for comparison; strips a\n// region/script subtag (\"zh-cn\" -> \"zh\") first. Codes already in 639-3 form pass through.\nexport function toIso3(code: string): string {\n const base = code.toLowerCase().split('-')[0];\n return ISO_639_1_TO_3[base] ?? base;\n}\n\n// Languages this project measured for models whose cards declare none, so the fit check is\n// not silently off for the shipped default; asserted here because it was measured, not attested.\nexport const MEASURED_MODEL_LANGUAGES: Record<string, string[]> = {\n 'minishlab/potion-retrieval-32M': ['en'], // benchmark/reports/2026-08-27-embedding-model-selection.md\n};\n"],"names":["MEASURED_MODEL_LANGUAGES","isKnownLanguageTag","toIso3","ISO_639_1_TO_3","ar","am","az","be","bg","bn","bs","cs","de","el","en","es","fa","fr","gu","ha","hi","hr","hu","id","ig","it","ja","jv","kk","kn","ko","ml","mr","ms","my","ne","nl","pa","pl","pt","ro","ru","rw","si","so","sr","su","sv","sw","ta","te","th","tl","tr","uk","ur","uz","vi","yo","zh","zu","tag","code","base","toLowerCase","split"],"mappings":";;;;;;;;;;;QA6EaA;eAAAA;;QAbGC;eAAAA;;QAMAC;eAAAA;;;AAtEhB,8FAA8F;AAC9F,4FAA4F;AAC5F,gGAAgG;AAChG,0EAA0E;AAC1E,IAAMC,iBAAyC;IAC7CC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;AACN;AAEO,SAAS3D,mBAAmB4D,GAAW;IAC5C,OAAOA,OAAO1D;AAChB;AAIO,SAASD,OAAO4D,IAAY;QAE1B3D;IADP,IAAM4D,OAAOD,KAAKE,WAAW,GAAGC,KAAK,CAAC,IAAI,CAAC,EAAE;IAC7C,QAAO9D,uBAAAA,cAAc,CAAC4D,KAAK,cAApB5D,kCAAAA,uBAAwB4D;AACjC;AAIO,IAAM/D,2BAAqD;IAChE,kCAAkC;QAAC;KAAK;AAC1C"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/embed/languages.ts"],"sourcesContent":["// HF model cards declare languages as ISO 639-1 (e.g. \"zh\"); franc-min reports ISO 639-3 (e.g.\n// \"cmn\"). Limited to franc-min's own 82-language table (https://github.com/wooorm/franc).\nconst ISO_639_1_TO_3: Record<string, string> = {\n ar: 'arb',\n am: 'amh',\n az: 'azj',\n be: 'bel',\n bg: 'bul',\n bn: 'ben',\n bs: 'bos',\n cs: 'ces',\n de: 'deu',\n el: 'ell',\n en: 'eng',\n es: 'spa',\n fa: 'pes',\n fr: 'fra',\n gu: 'guj',\n ha: 'hau',\n hi: 'hin',\n hr: 'hrv',\n hu: 'hun',\n id: 'ind',\n ig: 'ibo',\n it: 'ita',\n ja: 'jpn',\n jv: 'jav',\n kk: 'kaz',\n kn: 'kan',\n ko: 'kor',\n ml: 'mal',\n mr: 'mar',\n ms: 'zlm',\n my: 'mya',\n ne: 'npi',\n nl: 'nld',\n pa: 'pan',\n pl: 'pol',\n pt: 'por',\n ro: 'ron',\n ru: 'rus',\n rw: 'kin',\n si: 'sin',\n so: 'som',\n sr: 'srp',\n su: 'sun',\n sv: 'swe',\n sw: 'swh',\n ta: 'tam',\n te: 'tel',\n th: 'tha',\n tl: 'tgl',\n tr: 'tur',\n uk: 'ukr',\n ur: 'urd',\n uz: 'uzn',\n vi: 'vie',\n yo: 'yor',\n zh: 'cmn',\n zu: 'zul',\n};\n\nexport function isKnownLanguageTag(tag: string): boolean {\n return tag in ISO_639_1_TO_3;\n}\n\n// Normalizes a declared or detected language code to ISO 639-3 for comparison; strips a\n// region/script subtag (\"zh-cn\" -> \"zh\") first. Codes already in 639-3 form pass through.\nexport function toIso3(code: string): string {\n const base = code.toLowerCase().split('-')[0];\n return ISO_639_1_TO_3[base] ?? base;\n}\n\n// Languages this project measured for models whose cards declare none, so the fit check is\n// not silently off for the shipped default; asserted here because it was measured, not attested.\nexport const MEASURED_MODEL_LANGUAGES: Record<string, string[]> = {\n 'minishlab/potion-retrieval-32M': ['en'], // benchmark/reports/2026-08-27-embedding-model-selection.md\n};\n"],"names":["MEASURED_MODEL_LANGUAGES","isKnownLanguageTag","toIso3","ISO_639_1_TO_3","ar","am","az","be","bg","bn","bs","cs","de","el","en","es","fa","fr","gu","ha","hi","hr","hu","id","ig","it","ja","jv","kk","kn","ko","ml","mr","ms","my","ne","nl","pa","pl","pt","ro","ru","rw","si","so","sr","su","sv","sw","ta","te","th","tl","tr","uk","ur","uz","vi","yo","zh","zu","tag","code","base","toLowerCase","split"],"mappings":";;;;;;;;;;;QA2EaA;eAAAA;;QAbGC;eAAAA;;QAMAC;eAAAA;;;AApEhB,+FAA+F;AAC/F,0FAA0F;AAC1F,IAAMC,iBAAyC;IAC7CC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;IACJC,IAAI;AACN;AAEO,SAAS3D,mBAAmB4D,GAAW;IAC5C,OAAOA,OAAO1D;AAChB;AAIO,SAASD,OAAO4D,IAAY;QAE1B3D;IADP,IAAM4D,OAAOD,KAAKE,WAAW,GAAGC,KAAK,CAAC,IAAI,CAAC,EAAE;IAC7C,QAAO9D,uBAAAA,cAAc,CAAC4D,KAAK,cAApB5D,kCAAAA,uBAAwB4D;AACjC;AAIO,IAAM/D,2BAAqD;IAChE,kCAAkC;QAAC;KAAK;AAC1C"}
@@ -230,8 +230,8 @@ function openaiProvider(model, url, keyEnv) {
230
230
  ];
231
231
  case 1:
232
232
  dims = _state.sent()[0].length;
233
- // Symmetric for now: per-model prefixes (nomic's search_document/search_query) are a
234
- // later workstream.
233
+ // embedQuery and embedDocuments are symmetric here; per-model prefixes like nomic's
234
+ // search_document/search_query are not applied.
235
235
  return [
236
236
  2,
237
237
  {