sensemaking 0.17.0 → 0.18.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 (429) hide show
  1. package/README.md +15 -13
  2. package/dist/cjs/cli/map.js +147 -6
  3. package/dist/cjs/cli/map.js.map +1 -1
  4. package/dist/cjs/cli/named.js +20 -12
  5. package/dist/cjs/cli/named.js.map +1 -1
  6. package/dist/cjs/cli/path.js +190 -29
  7. package/dist/cjs/cli/path.js.map +1 -1
  8. package/dist/cjs/cli/peek.js +148 -7
  9. package/dist/cjs/cli/peek.js.map +1 -1
  10. package/dist/cjs/cli/related.js +5 -5
  11. package/dist/cjs/cli/related.js.map +1 -1
  12. package/dist/cjs/cli/search.js +5 -5
  13. package/dist/cjs/cli/search.js.map +1 -1
  14. package/dist/cjs/cli/shared.d.cts +3 -3
  15. package/dist/cjs/cli/shared.d.ts +3 -3
  16. package/dist/cjs/cli/shared.js +156 -58
  17. package/dist/cjs/cli/shared.js.map +1 -1
  18. package/dist/cjs/cli/sql.js +151 -10
  19. package/dist/cjs/cli/sql.js.map +1 -1
  20. package/dist/cjs/cli/status.js +187 -62
  21. package/dist/cjs/cli/status.js.map +1 -1
  22. package/dist/cjs/commands/map.d.cts +2 -2
  23. package/dist/cjs/commands/map.d.ts +2 -2
  24. package/dist/cjs/commands/map.js +373 -67
  25. package/dist/cjs/commands/map.js.map +1 -1
  26. package/dist/cjs/commands/peek.d.cts +2 -2
  27. package/dist/cjs/commands/peek.d.ts +2 -2
  28. package/dist/cjs/commands/peek.js +321 -79
  29. package/dist/cjs/commands/peek.js.map +1 -1
  30. package/dist/cjs/commands/related.d.cts +2 -2
  31. package/dist/cjs/commands/related.d.ts +2 -2
  32. package/dist/cjs/commands/related.js +55 -10
  33. package/dist/cjs/commands/related.js.map +1 -1
  34. package/dist/cjs/commands/scope.d.cts +7 -7
  35. package/dist/cjs/commands/scope.d.ts +7 -7
  36. package/dist/cjs/commands/scope.js +382 -45
  37. package/dist/cjs/commands/scope.js.map +1 -1
  38. package/dist/cjs/commands/search.d.cts +2 -2
  39. package/dist/cjs/commands/search.d.ts +2 -2
  40. package/dist/cjs/commands/search.js +292 -99
  41. package/dist/cjs/commands/search.js.map +1 -1
  42. package/dist/cjs/commands/signals.d.cts +4 -10
  43. package/dist/cjs/commands/signals.d.ts +4 -10
  44. package/dist/cjs/commands/signals.js +91 -45
  45. package/dist/cjs/commands/signals.js.map +1 -1
  46. package/dist/cjs/commands/status.d.cts +2 -2
  47. package/dist/cjs/commands/status.d.ts +2 -2
  48. package/dist/cjs/commands/status.js +247 -10
  49. package/dist/cjs/commands/status.js.map +1 -1
  50. package/dist/cjs/config/access.d.cts +1 -0
  51. package/dist/cjs/config/access.d.ts +1 -0
  52. package/dist/cjs/config/access.js +7 -0
  53. package/dist/cjs/config/access.js.map +1 -1
  54. package/dist/cjs/config/index.d.cts +1 -1
  55. package/dist/cjs/config/index.d.ts +1 -1
  56. package/dist/cjs/config/index.js +3 -0
  57. package/dist/cjs/config/index.js.map +1 -1
  58. package/dist/cjs/config/types.d.cts +1 -0
  59. package/dist/cjs/config/types.d.ts +1 -0
  60. package/dist/cjs/config/types.js.map +1 -1
  61. package/dist/cjs/config/validate.js +10 -1
  62. package/dist/cjs/config/validate.js.map +1 -1
  63. package/dist/cjs/embed/distribution.d.cts +4 -0
  64. package/dist/cjs/embed/distribution.d.ts +4 -0
  65. package/dist/cjs/embed/distribution.js +186 -0
  66. package/dist/cjs/embed/distribution.js.map +1 -0
  67. package/dist/cjs/embed/langfit.d.cts +2 -5
  68. package/dist/cjs/embed/langfit.d.ts +2 -5
  69. package/dist/cjs/embed/langfit.js +73 -63
  70. package/dist/cjs/embed/langfit.js.map +1 -1
  71. package/dist/cjs/embed/query.d.cts +6 -13
  72. package/dist/cjs/embed/query.d.ts +6 -13
  73. package/dist/cjs/embed/query.js +56 -168
  74. package/dist/cjs/embed/query.js.map +1 -1
  75. package/dist/cjs/embed/static.js +13 -3
  76. package/dist/cjs/embed/static.js.map +1 -1
  77. package/dist/cjs/embed/types.d.cts +1 -0
  78. package/dist/cjs/embed/types.d.ts +1 -0
  79. package/dist/cjs/embed/types.js +11 -2
  80. package/dist/cjs/embed/types.js.map +1 -1
  81. package/dist/cjs/errors.d.cts +1 -1
  82. package/dist/cjs/errors.d.ts +1 -1
  83. package/dist/cjs/errors.js.map +1 -1
  84. package/dist/cjs/features/embed.js +218 -11
  85. package/dist/cjs/features/embed.js.map +1 -1
  86. package/dist/cjs/features/index.d.cts +1 -1
  87. package/dist/cjs/features/index.d.ts +1 -1
  88. package/dist/cjs/features/index.js +6 -0
  89. package/dist/cjs/features/index.js.map +1 -1
  90. package/dist/cjs/features/links.d.cts +7 -2
  91. package/dist/cjs/features/links.d.ts +7 -2
  92. package/dist/cjs/features/links.js +710 -198
  93. package/dist/cjs/features/links.js.map +1 -1
  94. package/dist/cjs/features/rank.js +231 -31
  95. package/dist/cjs/features/rank.js.map +1 -1
  96. package/dist/cjs/features/sections.js +218 -8
  97. package/dist/cjs/features/sections.js.map +1 -1
  98. package/dist/cjs/features/tags.js +236 -40
  99. package/dist/cjs/features/tags.js.map +1 -1
  100. package/dist/cjs/features/types.d.cts +9 -5
  101. package/dist/cjs/features/types.d.ts +9 -5
  102. package/dist/cjs/graph/traverse.d.cts +2 -2
  103. package/dist/cjs/graph/traverse.d.ts +2 -2
  104. package/dist/cjs/graph/traverse.js +321 -42
  105. package/dist/cjs/graph/traverse.js.map +1 -1
  106. package/dist/cjs/index.d.cts +3 -2
  107. package/dist/cjs/index.d.ts +3 -2
  108. package/dist/cjs/index.js +2 -2
  109. package/dist/cjs/index.js.map +1 -1
  110. package/dist/cjs/lib/guarded-tick.d.cts +1 -0
  111. package/dist/cjs/lib/guarded-tick.d.ts +1 -0
  112. package/dist/cjs/lib/guarded-tick.js +19 -0
  113. package/dist/cjs/lib/guarded-tick.js.map +1 -0
  114. package/dist/cjs/output/column-hint.d.cts +1 -2
  115. package/dist/cjs/output/column-hint.d.ts +1 -2
  116. package/dist/cjs/output/column-hint.js +1 -4
  117. package/dist/cjs/output/column-hint.js.map +1 -1
  118. package/dist/cjs/output/output.d.cts +2 -1
  119. package/dist/cjs/output/output.d.ts +2 -1
  120. package/dist/cjs/output/output.js +515 -97
  121. package/dist/cjs/output/output.js.map +1 -1
  122. package/dist/cjs/scan/frontmatter.js.map +1 -1
  123. package/dist/cjs/scan/index.js.map +1 -1
  124. package/dist/cjs/scan/reparse.d.cts +10 -0
  125. package/dist/cjs/scan/reparse.d.ts +10 -0
  126. package/dist/cjs/scan/reparse.js +104 -0
  127. package/dist/cjs/scan/reparse.js.map +1 -0
  128. package/dist/cjs/store/duckdb/batch.d.cts +16 -0
  129. package/dist/cjs/store/duckdb/batch.d.ts +16 -0
  130. package/dist/cjs/store/duckdb/batch.js +208 -0
  131. package/dist/cjs/store/duckdb/batch.js.map +1 -0
  132. package/dist/cjs/store/duckdb/connection.d.cts +5 -0
  133. package/dist/cjs/store/duckdb/connection.d.ts +5 -0
  134. package/dist/cjs/store/duckdb/connection.js +772 -0
  135. package/dist/cjs/store/duckdb/connection.js.map +1 -0
  136. package/dist/cjs/store/duckdb/lexical.d.cts +10 -0
  137. package/dist/cjs/store/duckdb/lexical.d.ts +10 -0
  138. package/dist/cjs/store/duckdb/lexical.js +502 -0
  139. package/dist/cjs/store/duckdb/lexical.js.map +1 -0
  140. package/dist/cjs/store/duckdb/native.d.cts +3 -0
  141. package/dist/cjs/store/duckdb/native.d.ts +3 -0
  142. package/dist/cjs/store/duckdb/native.js +360 -0
  143. package/dist/cjs/store/duckdb/native.js.map +1 -0
  144. package/dist/cjs/store/duckdb/open.d.cts +12 -0
  145. package/dist/cjs/store/duckdb/open.d.ts +12 -0
  146. package/dist/cjs/store/duckdb/open.js +501 -0
  147. package/dist/cjs/store/duckdb/open.js.map +1 -0
  148. package/dist/cjs/store/duckdb/reconcile.d.cts +6 -0
  149. package/dist/cjs/store/duckdb/reconcile.d.ts +6 -0
  150. package/dist/cjs/store/duckdb/reconcile.js +753 -0
  151. package/dist/cjs/store/duckdb/reconcile.js.map +1 -0
  152. package/dist/cjs/store/duckdb/sql-functions.d.cts +2 -0
  153. package/dist/cjs/store/duckdb/sql-functions.d.ts +2 -0
  154. package/dist/cjs/store/duckdb/sql-functions.js +203 -0
  155. package/dist/cjs/store/duckdb/sql-functions.js.map +1 -0
  156. package/dist/cjs/store/duckdb/store.d.cts +5 -0
  157. package/dist/cjs/store/duckdb/store.d.ts +5 -0
  158. package/dist/cjs/store/duckdb/store.js +504 -0
  159. package/dist/cjs/store/duckdb/store.js.map +1 -0
  160. package/dist/cjs/store/duckdb/vectors.d.cts +9 -0
  161. package/dist/cjs/store/duckdb/vectors.d.ts +9 -0
  162. package/dist/cjs/store/duckdb/vectors.js +502 -0
  163. package/dist/cjs/store/duckdb/vectors.js.map +1 -0
  164. package/dist/cjs/store/index.d.cts +7 -0
  165. package/dist/cjs/store/index.d.ts +7 -0
  166. package/dist/cjs/store/index.js +201 -0
  167. package/dist/cjs/store/index.js.map +1 -0
  168. package/dist/cjs/store/meta.d.cts +3 -0
  169. package/dist/cjs/store/meta.d.ts +3 -0
  170. package/dist/cjs/store/meta.js +215 -0
  171. package/dist/cjs/store/meta.js.map +1 -0
  172. package/dist/cjs/store/shared.d.cts +5 -0
  173. package/dist/cjs/store/shared.d.ts +5 -0
  174. package/dist/cjs/store/shared.js +252 -0
  175. package/dist/cjs/store/shared.js.map +1 -0
  176. package/dist/cjs/store/sql-functions.d.cts +2 -0
  177. package/dist/cjs/store/sql-functions.d.ts +2 -0
  178. package/dist/cjs/store/sql-functions.js +48 -0
  179. package/dist/cjs/store/sql-functions.js.map +1 -0
  180. package/dist/cjs/store/sqlite/connection.d.cts +3 -0
  181. package/dist/cjs/store/sqlite/connection.d.ts +3 -0
  182. package/dist/cjs/store/sqlite/connection.js +516 -0
  183. package/dist/cjs/store/sqlite/connection.js.map +1 -0
  184. package/dist/cjs/store/sqlite/lexical.d.cts +2 -0
  185. package/dist/cjs/store/sqlite/lexical.d.ts +2 -0
  186. package/dist/cjs/store/sqlite/lexical.js +177 -0
  187. package/dist/cjs/store/sqlite/lexical.js.map +1 -0
  188. package/dist/cjs/{db → store/sqlite}/open.d.cts +5 -5
  189. package/dist/cjs/{db → store/sqlite}/open.d.ts +5 -5
  190. package/dist/cjs/store/sqlite/open.js +604 -0
  191. package/dist/cjs/store/sqlite/open.js.map +1 -0
  192. package/dist/cjs/store/sqlite/reconcile.d.cts +10 -0
  193. package/dist/cjs/store/sqlite/reconcile.d.ts +10 -0
  194. package/dist/cjs/store/sqlite/reconcile.js +1020 -0
  195. package/dist/cjs/store/sqlite/reconcile.js.map +1 -0
  196. package/dist/cjs/store/sqlite/sql-functions.js +34 -0
  197. package/dist/cjs/store/sqlite/sql-functions.js.map +1 -0
  198. package/dist/cjs/store/sqlite/store.d.cts +5 -0
  199. package/dist/cjs/store/sqlite/store.d.ts +5 -0
  200. package/dist/cjs/store/sqlite/store.js +588 -0
  201. package/dist/cjs/store/sqlite/store.js.map +1 -0
  202. package/dist/cjs/store/sqlite/vectors.d.cts +13 -0
  203. package/dist/cjs/store/sqlite/vectors.d.ts +13 -0
  204. package/dist/cjs/store/sqlite/vectors.js +470 -0
  205. package/dist/cjs/store/sqlite/vectors.js.map +1 -0
  206. package/dist/cjs/store/transaction.d.cts +5 -0
  207. package/dist/cjs/store/transaction.d.ts +5 -0
  208. package/dist/cjs/store/transaction.js +297 -0
  209. package/dist/cjs/store/transaction.js.map +1 -0
  210. package/dist/cjs/store/types.d.cts +91 -0
  211. package/dist/cjs/store/types.d.ts +91 -0
  212. package/dist/cjs/store/types.js +15 -0
  213. package/dist/cjs/store/types.js.map +1 -0
  214. package/dist/cjs/watch.d.cts +3 -0
  215. package/dist/cjs/watch.d.ts +3 -0
  216. package/dist/cjs/watch.js +215 -73
  217. package/dist/cjs/watch.js.map +1 -1
  218. package/dist/esm/cli/map.js +5 -5
  219. package/dist/esm/cli/map.js.map +1 -1
  220. package/dist/esm/cli/named.js +3 -3
  221. package/dist/esm/cli/named.js.map +1 -1
  222. package/dist/esm/cli/path.js +6 -5
  223. package/dist/esm/cli/path.js.map +1 -1
  224. package/dist/esm/cli/peek.js +5 -5
  225. package/dist/esm/cli/peek.js.map +1 -1
  226. package/dist/esm/cli/related.js +3 -3
  227. package/dist/esm/cli/related.js.map +1 -1
  228. package/dist/esm/cli/search.js +2 -2
  229. package/dist/esm/cli/search.js.map +1 -1
  230. package/dist/esm/cli/shared.d.ts +3 -3
  231. package/dist/esm/cli/shared.js +17 -17
  232. package/dist/esm/cli/shared.js.map +1 -1
  233. package/dist/esm/cli/sql.js +2 -2
  234. package/dist/esm/cli/sql.js.map +1 -1
  235. package/dist/esm/cli/status.js +25 -24
  236. package/dist/esm/cli/status.js.map +1 -1
  237. package/dist/esm/commands/map.d.ts +2 -2
  238. package/dist/esm/commands/map.js +51 -41
  239. package/dist/esm/commands/map.js.map +1 -1
  240. package/dist/esm/commands/peek.d.ts +2 -2
  241. package/dist/esm/commands/peek.js +58 -50
  242. package/dist/esm/commands/peek.js.map +1 -1
  243. package/dist/esm/commands/related.d.ts +2 -2
  244. package/dist/esm/commands/related.js +9 -9
  245. package/dist/esm/commands/related.js.map +1 -1
  246. package/dist/esm/commands/scope.d.ts +7 -7
  247. package/dist/esm/commands/scope.js +28 -17
  248. package/dist/esm/commands/scope.js.map +1 -1
  249. package/dist/esm/commands/search.d.ts +2 -2
  250. package/dist/esm/commands/search.js +42 -35
  251. package/dist/esm/commands/search.js.map +1 -1
  252. package/dist/esm/commands/signals.d.ts +4 -10
  253. package/dist/esm/commands/signals.js +17 -13
  254. package/dist/esm/commands/signals.js.map +1 -1
  255. package/dist/esm/commands/status.d.ts +2 -2
  256. package/dist/esm/commands/status.js +13 -7
  257. package/dist/esm/commands/status.js.map +1 -1
  258. package/dist/esm/config/access.d.ts +1 -0
  259. package/dist/esm/config/access.js +5 -0
  260. package/dist/esm/config/access.js.map +1 -1
  261. package/dist/esm/config/index.d.ts +1 -1
  262. package/dist/esm/config/index.js +1 -1
  263. package/dist/esm/config/index.js.map +1 -1
  264. package/dist/esm/config/types.d.ts +1 -0
  265. package/dist/esm/config/types.js.map +1 -1
  266. package/dist/esm/config/validate.js +10 -1
  267. package/dist/esm/config/validate.js.map +1 -1
  268. package/dist/esm/embed/distribution.d.ts +4 -0
  269. package/dist/esm/embed/distribution.js +11 -0
  270. package/dist/esm/embed/distribution.js.map +1 -0
  271. package/dist/esm/embed/langfit.d.ts +2 -5
  272. package/dist/esm/embed/langfit.js +13 -13
  273. package/dist/esm/embed/langfit.js.map +1 -1
  274. package/dist/esm/embed/query.d.ts +6 -13
  275. package/dist/esm/embed/query.js +23 -84
  276. package/dist/esm/embed/query.js.map +1 -1
  277. package/dist/esm/embed/static.js +6 -1
  278. package/dist/esm/embed/static.js.map +1 -1
  279. package/dist/esm/embed/types.d.ts +1 -0
  280. package/dist/esm/embed/types.js +5 -3
  281. package/dist/esm/embed/types.js.map +1 -1
  282. package/dist/esm/errors.d.ts +1 -1
  283. package/dist/esm/errors.js.map +1 -1
  284. package/dist/esm/features/embed.js +22 -10
  285. package/dist/esm/features/embed.js.map +1 -1
  286. package/dist/esm/features/index.d.ts +1 -1
  287. package/dist/esm/features/index.js +1 -1
  288. package/dist/esm/features/index.js.map +1 -1
  289. package/dist/esm/features/links.d.ts +7 -2
  290. package/dist/esm/features/links.js +124 -97
  291. package/dist/esm/features/links.js.map +1 -1
  292. package/dist/esm/features/rank.js +15 -8
  293. package/dist/esm/features/rank.js.map +1 -1
  294. package/dist/esm/features/sections.js +20 -7
  295. package/dist/esm/features/sections.js.map +1 -1
  296. package/dist/esm/features/tags.js +19 -18
  297. package/dist/esm/features/tags.js.map +1 -1
  298. package/dist/esm/features/types.d.ts +9 -5
  299. package/dist/esm/features/types.js +1 -1
  300. package/dist/esm/features/types.js.map +1 -1
  301. package/dist/esm/graph/traverse.d.ts +2 -2
  302. package/dist/esm/graph/traverse.js +26 -23
  303. package/dist/esm/graph/traverse.js.map +1 -1
  304. package/dist/esm/index.d.ts +3 -2
  305. package/dist/esm/index.js +1 -1
  306. package/dist/esm/index.js.map +1 -1
  307. package/dist/esm/lib/guarded-tick.d.ts +1 -0
  308. package/dist/esm/lib/guarded-tick.js +8 -0
  309. package/dist/esm/lib/guarded-tick.js.map +1 -0
  310. package/dist/esm/output/column-hint.d.ts +1 -2
  311. package/dist/esm/output/column-hint.js +3 -2
  312. package/dist/esm/output/column-hint.js.map +1 -1
  313. package/dist/esm/output/output.d.ts +2 -1
  314. package/dist/esm/output/output.js +27 -23
  315. package/dist/esm/output/output.js.map +1 -1
  316. package/dist/esm/scan/frontmatter.js +1 -1
  317. package/dist/esm/scan/frontmatter.js.map +1 -1
  318. package/dist/esm/scan/index.js +1 -1
  319. package/dist/esm/scan/index.js.map +1 -1
  320. package/dist/esm/scan/reparse.d.ts +10 -0
  321. package/dist/esm/scan/reparse.js +30 -0
  322. package/dist/esm/scan/reparse.js.map +1 -0
  323. package/dist/esm/store/duckdb/batch.d.ts +16 -0
  324. package/dist/esm/store/duckdb/batch.js +96 -0
  325. package/dist/esm/store/duckdb/batch.js.map +1 -0
  326. package/dist/esm/store/duckdb/connection.d.ts +5 -0
  327. package/dist/esm/store/duckdb/connection.js +97 -0
  328. package/dist/esm/store/duckdb/connection.js.map +1 -0
  329. package/dist/esm/store/duckdb/lexical.d.ts +10 -0
  330. package/dist/esm/store/duckdb/lexical.js +163 -0
  331. package/dist/esm/store/duckdb/lexical.js.map +1 -0
  332. package/dist/esm/store/duckdb/native.d.ts +3 -0
  333. package/dist/esm/store/duckdb/native.js +96 -0
  334. package/dist/esm/store/duckdb/native.js.map +1 -0
  335. package/dist/esm/store/duckdb/open.d.ts +12 -0
  336. package/dist/esm/store/duckdb/open.js +127 -0
  337. package/dist/esm/store/duckdb/open.js.map +1 -0
  338. package/dist/esm/store/duckdb/reconcile.d.ts +6 -0
  339. package/dist/esm/store/duckdb/reconcile.js +141 -0
  340. package/dist/esm/store/duckdb/reconcile.js.map +1 -0
  341. package/dist/esm/store/duckdb/sql-functions.d.ts +2 -0
  342. package/dist/esm/store/duckdb/sql-functions.js +56 -0
  343. package/dist/esm/store/duckdb/sql-functions.js.map +1 -0
  344. package/dist/esm/store/duckdb/store.d.ts +5 -0
  345. package/dist/esm/store/duckdb/store.js +104 -0
  346. package/dist/esm/store/duckdb/store.js.map +1 -0
  347. package/dist/esm/store/duckdb/vectors.d.ts +9 -0
  348. package/dist/esm/store/duckdb/vectors.js +171 -0
  349. package/dist/esm/store/duckdb/vectors.js.map +1 -0
  350. package/dist/esm/store/index.d.ts +7 -0
  351. package/dist/esm/store/index.js +35 -0
  352. package/dist/esm/store/index.js.map +1 -0
  353. package/dist/esm/store/meta.d.ts +3 -0
  354. package/dist/esm/store/meta.js +17 -0
  355. package/dist/esm/store/meta.js.map +1 -0
  356. package/dist/esm/store/shared.d.ts +5 -0
  357. package/dist/esm/store/shared.js +24 -0
  358. package/dist/esm/store/shared.js.map +1 -0
  359. package/dist/esm/store/sql-functions.d.ts +2 -0
  360. package/dist/esm/store/sql-functions.js +29 -0
  361. package/dist/esm/store/sql-functions.js.map +1 -0
  362. package/dist/esm/store/sqlite/connection.d.ts +3 -0
  363. package/dist/esm/store/sqlite/connection.js +50 -0
  364. package/dist/esm/store/sqlite/connection.js.map +1 -0
  365. package/dist/esm/store/sqlite/lexical.d.ts +2 -0
  366. package/dist/esm/store/sqlite/lexical.js +21 -0
  367. package/dist/esm/store/sqlite/lexical.js.map +1 -0
  368. package/dist/esm/{db → store/sqlite}/open.d.ts +5 -5
  369. package/dist/esm/store/sqlite/open.js +193 -0
  370. package/dist/esm/store/sqlite/open.js.map +1 -0
  371. package/dist/esm/store/sqlite/reconcile.d.ts +10 -0
  372. package/dist/esm/{db → store/sqlite}/reconcile.js +101 -99
  373. package/dist/esm/store/sqlite/reconcile.js.map +1 -0
  374. package/dist/esm/store/sqlite/sql-functions.js +23 -0
  375. package/dist/esm/store/sqlite/sql-functions.js.map +1 -0
  376. package/dist/esm/store/sqlite/store.d.ts +5 -0
  377. package/dist/esm/store/sqlite/store.js +82 -0
  378. package/dist/esm/store/sqlite/store.js.map +1 -0
  379. package/dist/esm/store/sqlite/vectors.d.ts +13 -0
  380. package/dist/esm/store/sqlite/vectors.js +90 -0
  381. package/dist/esm/store/sqlite/vectors.js.map +1 -0
  382. package/dist/esm/store/transaction.d.ts +5 -0
  383. package/dist/esm/store/transaction.js +57 -0
  384. package/dist/esm/store/transaction.js.map +1 -0
  385. package/dist/esm/store/types.d.ts +91 -0
  386. package/dist/esm/store/types.js +11 -0
  387. package/dist/esm/store/types.js.map +1 -0
  388. package/dist/esm/watch.d.ts +3 -0
  389. package/dist/esm/watch.js +39 -22
  390. package/dist/esm/watch.js.map +1 -1
  391. package/package.json +3 -1
  392. package/schema.json +5 -0
  393. package/skills/sense/EXAMPLES.md +3 -3
  394. package/skills/sense/SKILL.md +16 -14
  395. package/skills/sense-bases/EXAMPLES.md +2 -2
  396. package/skills/sense-bases/SKILL.md +6 -6
  397. package/skills/sense-setup/EXAMPLES.md +1 -1
  398. package/skills/sense-setup/SKILL.md +5 -4
  399. package/dist/cjs/db/index.d.cts +0 -4
  400. package/dist/cjs/db/index.d.ts +0 -4
  401. package/dist/cjs/db/index.js +0 -42
  402. package/dist/cjs/db/index.js.map +0 -1
  403. package/dist/cjs/db/open.js +0 -230
  404. package/dist/cjs/db/open.js.map +0 -1
  405. package/dist/cjs/db/reconcile.d.cts +0 -10
  406. package/dist/cjs/db/reconcile.d.ts +0 -10
  407. package/dist/cjs/db/reconcile.js +0 -579
  408. package/dist/cjs/db/reconcile.js.map +0 -1
  409. package/dist/cjs/db/shared.d.cts +0 -5
  410. package/dist/cjs/db/shared.d.ts +0 -5
  411. package/dist/cjs/db/shared.js +0 -45
  412. package/dist/cjs/db/shared.js.map +0 -1
  413. package/dist/cjs/db/sql-functions.js +0 -64
  414. package/dist/cjs/db/sql-functions.js.map +0 -1
  415. package/dist/esm/db/index.d.ts +0 -4
  416. package/dist/esm/db/index.js +0 -5
  417. package/dist/esm/db/index.js.map +0 -1
  418. package/dist/esm/db/open.js +0 -165
  419. package/dist/esm/db/open.js.map +0 -1
  420. package/dist/esm/db/reconcile.d.ts +0 -10
  421. package/dist/esm/db/reconcile.js.map +0 -1
  422. package/dist/esm/db/shared.d.ts +0 -5
  423. package/dist/esm/db/shared.js +0 -20
  424. package/dist/esm/db/shared.js.map +0 -1
  425. package/dist/esm/db/sql-functions.js +0 -47
  426. package/dist/esm/db/sql-functions.js.map +0 -1
  427. /package/dist/cjs/{db → store/sqlite}/sql-functions.d.cts +0 -0
  428. /package/dist/cjs/{db → store/sqlite}/sql-functions.d.ts +0 -0
  429. /package/dist/esm/{db → store/sqlite}/sql-functions.d.ts +0 -0
@@ -1,10 +1,12 @@
1
- import { contentTokenize } from '../config/index.js';
2
- import { SenseError } from '../errors.js';
3
- import { activeFeatures } from '../features/index.js';
4
- import { progress } from '../output/progress.js';
5
- import { listFiles, parseFile, RESERVED_COLUMNS } from '../scan/index.js';
6
- import { segmentField } from '../text/segment.js';
7
- import { getColumns, getMeta, quoteIdent, setMeta } from './shared.js';
1
+ import { contentTokenize } from '../../config/index.js';
2
+ import { SenseError } from '../../errors.js';
3
+ import { activeFeatures } from '../../features/index.js';
4
+ import { progress } from '../../output/progress.js';
5
+ import { listFiles, parseFile, RESERVED_COLUMNS } from '../../scan/index.js';
6
+ import { reparseFiles } from '../../scan/reparse.js';
7
+ import { segmentField } from '../../text/segment.js';
8
+ import { getColumns, getMeta, quoteIdent, setMeta } from '../shared.js';
9
+ import { withTransaction } from '../transaction.js';
8
10
  // Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last
9
11
  // computed value on every touch, not just the reconciles that recompute it.
10
12
  const CORE_FRONTMATTER_COLUMNS = new Set([
@@ -19,15 +21,25 @@ const MAX_FRONTMATTER_COLUMNS = 2000;
19
21
  // Shared by reconcile() and the tokenize-only rebuild in open(), so both prepare the same
20
22
  // literal instead of two copies drifting apart.
21
23
  const INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, "path", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE "path" = ?), ?, ?, ?, ?, ?, ?, ?)`;
22
- // Shared by reconcile() and the tokenize-only rebuild in open(), so content population is
23
- // defined once. Assumes the frontmatter row for doc.relPath already exists (rowid lookup).
24
- function insertContentRow(insertBody, doc, segmenting) {
25
- insertBody.run(doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmenting ? segmentField(doc.search.title) : '', segmenting ? segmentField(doc.search.summary) : '', segmenting ? segmentField(doc.search.text) : '');
24
+ // A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.
25
+ // Assumes the frontmatter row for doc.relPath already exists (rowid subquery).
26
+ function contentRow(doc, segmenting) {
27
+ return [
28
+ doc.relPath,
29
+ doc.search.title,
30
+ doc.search.summary,
31
+ doc.search.text,
32
+ doc.relPath,
33
+ segmenting ? segmentField(doc.search.title) : '',
34
+ segmenting ? segmentField(doc.search.summary) : '',
35
+ segmenting ? segmentField(doc.search.text) : ''
36
+ ];
26
37
  }
27
- export function reconcile(db, cfg, baseDir) {
38
+ export async function reconcile(conn, cfg, baseDir) {
28
39
  const files = listFiles(cfg, baseDir);
29
40
  const currentSet = new Set(files.map((f)=>f.relPath));
30
- const existingRows = db.prepare(`SELECT "path", "_mtime", "_size" FROM frontmatter`).all();
41
+ const existingStmt = await conn.prepare('SELECT "path", "_mtime", "_size" FROM frontmatter');
42
+ const existingRows = await existingStmt.all();
31
43
  const existing = new Map(existingRows.map((r)=>[
32
44
  r.path,
33
45
  r
@@ -42,30 +54,13 @@ export function reconcile(db, cfg, baseDir) {
42
54
  warnings: []
43
55
  };
44
56
  const features = activeFeatures(cfg);
45
- const seenColumns = getColumns(db);
46
- const newColumns = [];
47
- const parsedDocs = [];
48
- const warnings = [];
57
+ const seenColumns = await getColumns(conn);
49
58
  // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short
50
59
  // reconciles stay silent (progress() has a threshold).
51
60
  const report = progress('reparsing files', toReparse.length);
52
- let parsedCount = 0;
53
- for (const file of toReparse){
54
- // A doc only gets extract/store from features that apply to it (currently: embed, via
55
- // FileStat.embed -- true iff the config names an embedding model).
56
- const fileFeatures = features.filter((feature)=>!feature.enabledForFile || feature.enabledForFile(cfg, file));
57
- const { doc, warnings: fileWarnings } = parseFile(file, fileFeatures, cfg);
58
- report.tick(++parsedCount);
59
- warnings.push(...fileWarnings);
60
- for (const key of Object.keys(doc.data)){
61
- if (!seenColumns.has(key)) {
62
- seenColumns.add(key);
63
- newColumns.push(key);
64
- }
65
- }
66
- parsedDocs.push(doc);
67
- }
61
+ const { docs: parsedDocs, warnings, newColumns } = reparseFiles(toReparse, features, cfg, seenColumns, report.tick);
68
62
  report.finish();
63
+ for (const col of newColumns)seenColumns.add(col);
69
64
  const allColumns = [
70
65
  ...seenColumns
71
66
  ];
@@ -87,38 +82,31 @@ export function reconcile(db, cfg, baseDir) {
87
82
  added,
88
83
  vanished
89
84
  };
85
+ const addedSet = new Set(added);
86
+ const reparsedExisting = parsedDocs.map((d)=>d.relPath).filter((p)=>!addedSet.has(p));
90
87
  const txStart = Date.now();
91
- db.exec('BEGIN');
92
- try {
93
- var _feature_afterReconcile;
94
- for (const col of newColumns)db.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);
95
- // FTS5 has no upsert, so delete-before-insert into `content`; coupled to the
96
- // frontmatter rowid (indexed via its PRIMARY KEY) instead of the UNINDEXED `path`
97
- // column, which a per-row DELETE would otherwise scan the whole table to find.
98
- const delBody = db.prepare(`DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE "path" = ?)`);
99
- const delPresetFiles = db.prepare(`DELETE FROM preset_files WHERE "path" = ?`);
100
- const insertPresetFile = db.prepare(`INSERT INTO preset_files ("path", preset) VALUES (?, ?)`);
88
+ await withTransaction(conn, async ()=>{
89
+ var _feature_remove, _feature_afterReconcile;
90
+ for (const col of newColumns)await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);
91
+ // content's rowid lookup depends on the frontmatter row it's coupled to, so every content
92
+ // delete/insert below must run after that row exists (vanished paths still have their
93
+ // frontmatter row at this point) and before it is removed (vanished frontmatter delete
94
+ // comes last).
101
95
  if (vanished.length > 0) {
102
- const del = db.prepare(`DELETE FROM frontmatter WHERE "path" = ?`);
103
- for (const path of vanished){
104
- var _feature_remove;
105
- // content delete must run first: it looks up the frontmatter rowid by path, which
106
- // the frontmatter delete below would otherwise have already removed.
107
- delBody.run(path);
108
- del.run(path);
109
- delPresetFiles.run(path);
110
- for (const feature of features)(_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, db, path, delta);
111
- }
96
+ await conn.runBatch('DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE "path" = ?)', vanished.map((p)=>[
97
+ p
98
+ ]));
99
+ }
100
+ if (reparsedExisting.length > 0) {
101
+ // FTS5 has no upsert, so delete-before-insert into `content`, coupled to the frontmatter
102
+ // rowid (indexed via its PRIMARY KEY) rather than the UNINDEXED `path` column, which a
103
+ // per-row DELETE would otherwise scan the whole table to find.
104
+ await conn.runBatch('DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE "path" = ?)', reparsedExisting.map((p)=>[
105
+ p
106
+ ]));
112
107
  }
113
108
  if (parsedDocs.length > 0) {
114
- const insert = db.prepare(insertSql);
115
- const insertBody = db.prepare(INSERT_CONTENT_SQL);
116
- // A non-default tokenizer means the tree has chosen its own scheme; a phrase query over
117
- // grapheme runs would be nonsense against trigram, so the sidecars stay empty.
118
- const segmenting = contentTokenize(cfg) === undefined;
119
- for (const doc of parsedDocs){
120
- var _feature_store;
121
- const values = writableColumns.map((col)=>{
109
+ const rows = parsedDocs.map((doc)=>writableColumns.map((col)=>{
122
110
  var _doc_data_col;
123
111
  if (col === 'path') return doc.relPath;
124
112
  if (col === '_mtime') return doc.mtimeMs;
@@ -127,37 +115,54 @@ export function reconcile(db, cfg, baseDir) {
127
115
  // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.
128
116
  if (col === '_parse_error') return doc.parseError;
129
117
  return (_doc_data_col = doc.data[col]) !== null && _doc_data_col !== void 0 ? _doc_data_col : null;
130
- });
131
- // Frontmatter upsert first: content's rowid lookup below depends on this row existing.
132
- insert.run(...values);
133
- // Only for docs that have rows: an unconditional delete made cold crawls quadratic.
134
- if (existing.has(doc.relPath)) {
135
- var _feature_remove1;
136
- delBody.run(doc.relPath);
137
- for (const feature of features)(_feature_remove1 = feature.remove) === null || _feature_remove1 === void 0 ? void 0 : _feature_remove1.call(feature, db, doc.relPath, delta);
138
- }
139
- insertContentRow(insertBody, doc, segmenting);
140
- // A preset edit forces a full rebuild, so an unchanged doc's coverage is already
141
- // correct; new docs have nothing to clear, which keeps cold builds linear.
142
- if (existing.has(doc.relPath)) delPresetFiles.run(doc.relPath);
143
- for (const presetName of doc.presets)insertPresetFile.run(doc.relPath, presetName);
144
- for (const feature of features)(_feature_store = feature.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature, db, doc.relPath, doc.extracted[feature.name], delta);
145
- }
118
+ }));
119
+ await conn.runBatch(insertSql, rows);
120
+ // A non-default tokenizer means the tree has chosen its own scheme; a phrase query over
121
+ // grapheme runs would be nonsense against trigram, so the sidecars stay empty.
122
+ const segmenting = contentTokenize(cfg) === undefined;
123
+ await conn.runBatch(INSERT_CONTENT_SQL, parsedDocs.map((doc)=>contentRow(doc, segmenting)));
146
124
  }
147
- for (const feature of features)(_feature_afterReconcile = feature.afterReconcile) === null || _feature_afterReconcile === void 0 ? void 0 : _feature_afterReconcile.call(feature, db, delta);
148
- db.exec('COMMIT');
149
- } catch (err) {
150
- db.exec('ROLLBACK');
151
- throw err;
152
- }
125
+ if (vanished.length > 0) await conn.runBatch('DELETE FROM frontmatter WHERE "path" = ?', vanished.map((p)=>[
126
+ p
127
+ ]));
128
+ // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;
129
+ // new docs have nothing to clear, which keeps cold builds linear.
130
+ const presetTouched = [
131
+ ...vanished,
132
+ ...reparsedExisting
133
+ ];
134
+ if (presetTouched.length > 0) await conn.runBatch('DELETE FROM preset_files WHERE "path" = ?', presetTouched.map((p)=>[
135
+ p
136
+ ]));
137
+ const presetRows = [];
138
+ for (const doc of parsedDocs)for (const presetName of doc.presets)presetRows.push([
139
+ doc.relPath,
140
+ presetName
141
+ ]);
142
+ if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files ("path", preset) VALUES (?, ?)', presetRows);
143
+ const removedPaths = [
144
+ ...vanished,
145
+ ...reparsedExisting
146
+ ];
147
+ if (removedPaths.length > 0) for (const feature of features)await ((_feature_remove = feature.remove) === null || _feature_remove === void 0 ? void 0 : _feature_remove.call(feature, conn, removedPaths, delta));
148
+ for (const feature of features){
149
+ var _feature_store;
150
+ const docsForFeature = parsedDocs.map((doc)=>({
151
+ path: doc.relPath,
152
+ extracted: doc.extracted[feature.name]
153
+ }));
154
+ await ((_feature_store = feature.store) === null || _feature_store === void 0 ? void 0 : _feature_store.call(feature, conn, docsForFeature, delta));
155
+ }
156
+ for (const feature of features)await ((_feature_afterReconcile = feature.afterReconcile) === null || _feature_afterReconcile === void 0 ? void 0 : _feature_afterReconcile.call(feature, conn, delta));
157
+ });
153
158
  // Reconcile's own write-transaction duration, for open()'s derived busy_timeout (F):
154
159
  // keep the observed max so a big watcher reconcile's lock hold is what the next open bounds its wait against.
155
160
  const durationMs = Date.now() - txStart;
156
- const prevRaw = getMeta(db, 'reconcile_max_ms');
161
+ const prevRaw = await getMeta(conn, 'reconcile_max_ms');
157
162
  // -1, not 0, so a genuinely 0ms first reconcile (sub-millisecond, common on a tiny tree)
158
163
  // still gets recorded instead of losing to the "nothing recorded yet" default.
159
164
  const prevMax = prevRaw === null ? -1 : Number(prevRaw);
160
- if (durationMs > prevMax) setMeta(db, 'reconcile_max_ms', String(durationMs));
165
+ if (durationMs > prevMax) await setMeta(conn, 'reconcile_max_ms', String(durationMs));
161
166
  return {
162
167
  parsed: parsedDocs.length,
163
168
  warnings
@@ -202,23 +207,20 @@ export function signatureDiff(before, after) {
202
207
  // run here -- doc.search (title/summary/text) is all content population needs. Returns
203
208
  // parseFile's per-file warnings (e.g. a bad date) so open() can surface them -- mtimes are
204
209
  // untouched, so reconcile() never reparses these files and would otherwise never emit them again.
205
- export function rebuildContentTable(db, cfg, baseDir) {
206
- const known = new Set(db.prepare('SELECT "path" FROM frontmatter').all().map((r)=>r.path));
210
+ export async function rebuildContentTable(conn, cfg, baseDir) {
211
+ const stmt = await conn.prepare('SELECT "path" FROM frontmatter');
212
+ const known = new Set((await stmt.all()).map((r)=>r.path));
207
213
  const files = listFiles(cfg, baseDir).filter((f)=>known.has(f.relPath));
208
214
  const segmenting = contentTokenize(cfg) === undefined;
209
- const insertBody = db.prepare(INSERT_CONTENT_SQL);
210
215
  const warnings = [];
211
- db.exec('BEGIN');
212
- try {
213
- for (const file of files){
214
- const { doc, warnings: fileWarnings } = parseFile(file);
215
- warnings.push(...fileWarnings);
216
- insertContentRow(insertBody, doc, segmenting);
217
- }
218
- db.exec('COMMIT');
219
- } catch (err) {
220
- db.exec('ROLLBACK');
221
- throw err;
216
+ const rows = [];
217
+ for (const file of files){
218
+ const { doc, warnings: fileWarnings } = parseFile(file);
219
+ warnings.push(...fileWarnings);
220
+ rows.push(contentRow(doc, segmenting));
222
221
  }
222
+ await withTransaction(conn, async ()=>{
223
+ if (rows.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, rows);
224
+ });
223
225
  return warnings;
224
226
  }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/reconcile.ts"],"sourcesContent":["import type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { SenseError } from '../../errors.ts';\nimport { activeFeatures } from '../../features/index.ts';\nimport type { ExtractedDoc, ReconcileDelta } from '../../features/types.ts';\nimport { progress } from '../../output/progress.ts';\nimport type { ParsedDoc } from '../../scan/index.ts';\nimport { listFiles, parseFile, RESERVED_COLUMNS } from '../../scan/index.ts';\nimport { reparseFiles } from '../../scan/reparse.ts';\nimport { segmentField } from '../../text/segment.ts';\nimport { getColumns, getMeta, quoteIdent, setMeta } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Connection } from '../types.ts';\n\n// Feature-owned columns (`_rank`) must stay out of the upsert: a reparse would null the last\n// computed value on every touch, not just the reconciles that recompute it.\nconst CORE_FRONTMATTER_COLUMNS = new Set(['path', '_mtime', '_ctime', '_size', '_parse_error']);\n\n// SQLite's compile-time SQLITE_MAX_COLUMN, default 2000 (https://www.sqlite.org/limits.html).\nconst MAX_FRONTMATTER_COLUMNS = 2000;\n\n// Shared by reconcile() and the tokenize-only rebuild in open(), so both prepare the same\n// literal instead of two copies drifting apart.\nconst INSERT_CONTENT_SQL = `INSERT INTO content (rowid, title, summary, text, \"path\", title_seg, summary_seg, text_seg) VALUES ((SELECT rowid FROM frontmatter WHERE \"path\" = ?), ?, ?, ?, ?, ?, ?, ?)`;\n\n// A single content row's param tuple, matching INSERT_CONTENT_SQL's placeholder order.\n// Assumes the frontmatter row for doc.relPath already exists (rowid subquery).\nfunction contentRow(doc: ParsedDoc, segmenting: boolean): unknown[] {\n return [doc.relPath, doc.search.title, doc.search.summary, doc.search.text, doc.relPath, segmenting ? segmentField(doc.search.title) : '', segmenting ? segmentField(doc.search.summary) : '', segmenting ? segmentField(doc.search.text) : ''];\n}\n\nexport async function reconcile(conn: Connection, cfg: Config, baseDir: string): Promise<{ parsed: number; warnings: string[] }> {\n const files = listFiles(cfg, baseDir);\n const currentSet = new Set(files.map((f) => f.relPath));\n\n const existingStmt = await conn.prepare('SELECT \"path\", \"_mtime\", \"_size\" FROM frontmatter');\n const existingRows = (await existingStmt.all()) as Array<{ path: string; _mtime: number; _size: number }>;\n const existing = new Map(existingRows.map((r) => [r.path, r]));\n const vanished = existingRows.filter((r) => !currentSet.has(r.path)).map((r) => r.path);\n\n const toReparse = files.filter((f) => {\n const row = existing.get(f.relPath);\n return !row || row._mtime !== f.mtimeMs || row._size !== f.size;\n });\n\n if (vanished.length === 0 && toReparse.length === 0) return { parsed: 0, warnings: [] };\n\n const features = activeFeatures(cfg);\n const seenColumns = await getColumns(conn);\n\n // Bulk reparses (a sync, a cold build) are the long silences a query can hit; short\n // reconciles stay silent (progress() has a threshold).\n const report = progress('reparsing files', toReparse.length);\n const { docs: parsedDocs, warnings, newColumns } = reparseFiles(toReparse, features, cfg, seenColumns, report.tick);\n report.finish();\n for (const col of newColumns) seenColumns.add(col);\n\n const allColumns = [...seenColumns];\n // Fence before ALTERing: SQLite's own failure past this point is a raw\n // \"too many columns on sqlite_altertab_frontmatter\" with no indication of the boundary or the levers.\n if (allColumns.length > MAX_FRONTMATTER_COLUMNS) {\n throw new SenseError(\n 'COLUMN_LIMIT',\n `frontmatter would need ${allColumns.length} columns, crossing SQLite's compile-time SQLITE_MAX_COLUMN limit (default ${MAX_FRONTMATTER_COLUMNS}; see https://www.sqlite.org/limits.html). Narrow the presets' include globs so fewer/other files are indexed, or fix whatever is generating unbounded frontmatter keys.`\n );\n }\n // Columns the frontmatter upsert actually writes: core + parsed frontmatter keys, never a\n // feature-owned reserved column (see CORE_FRONTMATTER_COLUMNS above).\n const writableColumns = allColumns.filter((c) => CORE_FRONTMATTER_COLUMNS.has(c) || !RESERVED_COLUMNS.has(c));\n // ON CONFLICT UPDATE (not OR REPLACE) keeps the row's rowid stable across reparses --\n // content rows are coupled to that rowid below.\n const insertSql = `INSERT INTO frontmatter (${writableColumns.map(quoteIdent).join(', ')}) VALUES (${writableColumns.map(() => '?').join(', ')}) ON CONFLICT(\"path\") DO UPDATE SET ${writableColumns\n .filter((c) => c !== 'path')\n .map((c) => `${quoteIdent(c)} = excluded.${quoteIdent(c)}`)\n .join(', ')}`;\n\n const added = toReparse.filter((f) => !existing.has(f.relPath)).map((f) => f.relPath);\n const delta: ReconcileDelta = { files, reparsed: parsedDocs.map((d) => d.relPath), added, vanished };\n const addedSet = new Set(added);\n const reparsedExisting = parsedDocs.map((d) => d.relPath).filter((p) => !addedSet.has(p));\n\n const txStart = Date.now();\n await withTransaction(conn, async () => {\n for (const col of newColumns) await conn.exec(`ALTER TABLE frontmatter ADD COLUMN ${quoteIdent(col)}`);\n\n // content's rowid lookup depends on the frontmatter row it's coupled to, so every content\n // delete/insert below must run after that row exists (vanished paths still have their\n // frontmatter row at this point) and before it is removed (vanished frontmatter delete\n // comes last).\n if (vanished.length > 0) {\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n vanished.map((p) => [p])\n );\n }\n if (reparsedExisting.length > 0) {\n // FTS5 has no upsert, so delete-before-insert into `content`, coupled to the frontmatter\n // rowid (indexed via its PRIMARY KEY) rather than the UNINDEXED `path` column, which a\n // per-row DELETE would otherwise scan the whole table to find.\n await conn.runBatch(\n 'DELETE FROM content WHERE rowid = (SELECT rowid FROM frontmatter WHERE \"path\" = ?)',\n reparsedExisting.map((p) => [p])\n );\n }\n\n if (parsedDocs.length > 0) {\n const rows = parsedDocs.map((doc) =>\n writableColumns.map((col) => {\n if (col === 'path') return doc.relPath;\n if (col === '_mtime') return doc.mtimeMs;\n if (col === '_ctime') return doc.ctimeMs;\n if (col === '_size') return doc.size;\n // Written per parse, unlike _rank, which a feature pass owns and the upsert skips.\n if (col === '_parse_error') return doc.parseError;\n return doc.data[col] ?? null;\n })\n );\n await conn.runBatch(insertSql, rows);\n\n // A non-default tokenizer means the tree has chosen its own scheme; a phrase query over\n // grapheme runs would be nonsense against trigram, so the sidecars stay empty.\n const segmenting = contentTokenize(cfg) === undefined;\n await conn.runBatch(\n INSERT_CONTENT_SQL,\n parsedDocs.map((doc) => contentRow(doc, segmenting))\n );\n }\n\n if (vanished.length > 0)\n await conn.runBatch(\n 'DELETE FROM frontmatter WHERE \"path\" = ?',\n vanished.map((p) => [p])\n );\n\n // A preset edit forces a full rebuild, so an unchanged doc's coverage is already correct;\n // new docs have nothing to clear, which keeps cold builds linear.\n const presetTouched = [...vanished, ...reparsedExisting];\n if (presetTouched.length > 0)\n await conn.runBatch(\n 'DELETE FROM preset_files WHERE \"path\" = ?',\n presetTouched.map((p) => [p])\n );\n const presetRows: unknown[][] = [];\n for (const doc of parsedDocs) for (const presetName of doc.presets) presetRows.push([doc.relPath, presetName]);\n if (presetRows.length > 0) await conn.runBatch('INSERT INTO preset_files (\"path\", preset) VALUES (?, ?)', presetRows);\n\n const removedPaths = [...vanished, ...reparsedExisting];\n if (removedPaths.length > 0) for (const feature of features) await feature.remove?.(conn, removedPaths, delta);\n for (const feature of features) {\n const docsForFeature: ExtractedDoc[] = parsedDocs.map((doc) => ({ path: doc.relPath, extracted: doc.extracted[feature.name] }));\n await feature.store?.(conn, docsForFeature, delta);\n }\n for (const feature of features) await feature.afterReconcile?.(conn, delta);\n });\n\n // Reconcile's own write-transaction duration, for open()'s derived busy_timeout (F):\n // keep the observed max so a big watcher reconcile's lock hold is what the next open bounds its wait against.\n const durationMs = Date.now() - txStart;\n const prevRaw = await getMeta(conn, 'reconcile_max_ms');\n // -1, not 0, so a genuinely 0ms first reconcile (sub-millisecond, common on a tiny tree)\n // still gets recorded instead of losing to the \"nothing recorded yet\" default.\n const prevMax = prevRaw === null ? -1 : Number(prevRaw);\n if (durationMs > prevMax) await setMeta(conn, 'reconcile_max_ms', String(durationMs));\n\n return { parsed: parsedDocs.length, warnings };\n}\n\n// Segment keys that moved between two feature signatures (see config.featureSignature's\n// format: global features, embed provider, tokenize, then one segment per preset).\nexport function changedSignatureKeys(before: string, after: string): Set<string> {\n const keyOf = (part: string) => (part.startsWith('preset:') ? part.split(':').slice(0, 2).join(':') : part.split(':')[0]);\n const parse = (sig: string) => new Map(sig.split('|').map((part) => [keyOf(part), part]));\n const a = parse(before);\n const b = parse(after);\n const changed = new Set<string>();\n for (const [key, val] of b) if (a.get(key) !== val) changed.add(key);\n for (const key of a.keys()) if (!b.has(key)) changed.add(key);\n return changed;\n}\n\n// Whether the embed segment only gained its resolved weight identity: same provider and\n// model, no identity recorded before, one now -- adopted into meta without a rebuild.\nexport function embedIdentityAdopted(before: string, after: string): boolean {\n const embedPart = (sig: string) => sig.split('|').find((p) => p.startsWith('embed:'));\n const b = embedPart(before);\n const a = embedPart(after);\n if (b === undefined || a === undefined) return false;\n const at = a.indexOf('@');\n return b.indexOf('@') === -1 && at !== -1 && a.slice(0, at) === b;\n}\n\n// Names what moved, for the rebuild notice.\nexport function signatureDiff(before: string, after: string): string {\n const changed = changedSignatureKeys(before, after);\n const label = (key: string) => (key === 'embed' ? 'embed settings' : key === 'tokenize' ? 'content tokenizer' : key.startsWith('preset:') ? `preset \"${key.slice(7)}\"` : 'features');\n return changed.size === 0 ? 'features' : [...changed].map(label).join(', ');\n}\n\n// The tokenize-only rebuild in open(): content is dropped and repopulated from files already\n// listed in frontmatter, which itself is untouched. Frontmatter, links, sections, and\n// embeddings are file-derived and tokenizer-independent, so they survive. No feature extractors\n// run here -- doc.search (title/summary/text) is all content population needs. Returns\n// parseFile's per-file warnings (e.g. a bad date) so open() can surface them -- mtimes are\n// untouched, so reconcile() never reparses these files and would otherwise never emit them again.\nexport async function rebuildContentTable(conn: Connection, cfg: Config, baseDir: string): Promise<string[]> {\n const stmt = await conn.prepare('SELECT \"path\" FROM frontmatter');\n const known = new Set(((await stmt.all()) as Array<{ path: string }>).map((r) => r.path));\n const files = listFiles(cfg, baseDir).filter((f) => known.has(f.relPath));\n const segmenting = contentTokenize(cfg) === undefined;\n const warnings: string[] = [];\n const rows: unknown[][] = [];\n for (const file of files) {\n const { doc, warnings: fileWarnings } = parseFile(file);\n warnings.push(...fileWarnings);\n rows.push(contentRow(doc, segmenting));\n }\n await withTransaction(conn, async () => {\n if (rows.length > 0) await conn.runBatch(INSERT_CONTENT_SQL, rows);\n });\n return warnings;\n}\n"],"names":["contentTokenize","SenseError","activeFeatures","progress","listFiles","parseFile","RESERVED_COLUMNS","reparseFiles","segmentField","getColumns","getMeta","quoteIdent","setMeta","withTransaction","CORE_FRONTMATTER_COLUMNS","Set","MAX_FRONTMATTER_COLUMNS","INSERT_CONTENT_SQL","contentRow","doc","segmenting","relPath","search","title","summary","text","reconcile","conn","cfg","baseDir","files","currentSet","map","f","existingStmt","prepare","existingRows","all","existing","Map","r","path","vanished","filter","has","toReparse","row","get","_mtime","mtimeMs","_size","size","length","parsed","warnings","features","seenColumns","report","docs","parsedDocs","newColumns","tick","finish","col","add","allColumns","writableColumns","c","insertSql","join","added","delta","reparsed","d","addedSet","reparsedExisting","p","txStart","Date","now","feature","exec","runBatch","rows","ctimeMs","parseError","data","undefined","presetTouched","presetRows","presetName","presets","push","removedPaths","remove","docsForFeature","extracted","name","store","afterReconcile","durationMs","prevRaw","prevMax","Number","String","changedSignatureKeys","before","after","keyOf","part","startsWith","split","slice","parse","sig","a","b","changed","key","val","keys","embedIdentityAdopted","embedPart","find","at","indexOf","signatureDiff","label","rebuildContentTable","stmt","known","file","fileWarnings"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { segmentMatch } from '../../text/segment.js';
2
+ import { basenameImpl, hasImpl } from '../sql-functions.js';
3
+ export function registerFunctions(db, segmenting) {
4
+ db.function('has', {
5
+ deterministic: true,
6
+ varargs: false
7
+ }, hasImpl);
8
+ db.function('basename', {
9
+ deterministic: true,
10
+ varargs: true
11
+ }, basenameImpl);
12
+ // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back, and
13
+ // matches nothing, so the same transform is available to hand-written SQL by name. `segmenting`
14
+ // is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) ===
15
+ // undefined), passed in by open() rather than read back from persisted state.
16
+ db.function('segment', {
17
+ deterministic: true,
18
+ varargs: false
19
+ }, (terms)=>{
20
+ const text = terms === null || terms === undefined ? '' : String(terms);
21
+ return segmenting ? segmentMatch(text) : text;
22
+ });
23
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/sql-functions.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport { segmentMatch } from '../../text/segment.ts';\nimport { basenameImpl, hasImpl } from '../sql-functions.ts';\n\nexport function registerFunctions(db: DatabaseSync, segmenting: boolean): void {\n db.function('has', { deterministic: true, varargs: false }, hasImpl);\n\n db.function('basename', { deterministic: true, varargs: true }, basenameImpl);\n\n // Raw `content MATCH '<unspaced text>'` cannot be rewritten behind the author's back, and\n // matches nothing, so the same transform is available to hand-written SQL by name. `segmenting`\n // is the same config predicate commands.ts's search() gates on (contentTokenize(cfg) ===\n // undefined), passed in by open() rather than read back from persisted state.\n db.function('segment', { deterministic: true, varargs: false }, (terms: unknown): string => {\n const text = terms === null || terms === undefined ? '' : String(terms);\n return segmenting ? segmentMatch(text) : text;\n });\n}\n"],"names":["segmentMatch","basenameImpl","hasImpl","registerFunctions","db","segmenting","function","deterministic","varargs","terms","text","undefined","String"],"mappings":"AACA,SAASA,YAAY,QAAQ,wBAAwB;AACrD,SAASC,YAAY,EAAEC,OAAO,QAAQ,sBAAsB;AAE5D,OAAO,SAASC,kBAAkBC,EAAgB,EAAEC,UAAmB;IACrED,GAAGE,QAAQ,CAAC,OAAO;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAGN;IAE5DE,GAAGE,QAAQ,CAAC,YAAY;QAAEC,eAAe;QAAMC,SAAS;IAAK,GAAGP;IAEhE,0FAA0F;IAC1F,gGAAgG;IAChG,yFAAyF;IACzF,8EAA8E;IAC9EG,GAAGE,QAAQ,CAAC,WAAW;QAAEC,eAAe;QAAMC,SAAS;IAAM,GAAG,CAACC;QAC/D,MAAMC,OAAOD,UAAU,QAAQA,UAAUE,YAAY,KAAKC,OAAOH;QACjE,OAAOJ,aAAaL,aAAaU,QAAQA;IAC3C;AACF"}
@@ -0,0 +1,5 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { Config } from '../../config/index.js';
3
+ import type { Capability, Connection, Store } from '../types.js';
4
+ export declare const CAPABILITIES: ReadonlySet<Capability>;
5
+ export declare function createStore(db: DatabaseSync, conn: Connection, cfg: Config, baseDir: string): Store;
@@ -0,0 +1,82 @@
1
+ import { contentTokenize } from '../../config/index.js';
2
+ import { getColumns } from '../shared.js';
3
+ import { withTransaction } from '../transaction.js';
4
+ import { queryLexical } from './lexical.js';
5
+ import { reconcile } from './reconcile.js';
6
+ import { hasVectorRow, pendingRows, scanCandidates, scanSimilar, writeVectorBatch } from './vectors.js';
7
+ export const CAPABILITIES = new Set([
8
+ 'phrases',
9
+ 'snippets',
10
+ 'watch-concurrency',
11
+ 'lexical',
12
+ 'vectors'
13
+ ]);
14
+ // Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one
15
+ // Connection instance (conn) with open()'s own reconcile call so transaction depth (see
16
+ // transaction.ts) is tracked against the same object everywhere.
17
+ export function createStore(db, conn, cfg, baseDir) {
18
+ return {
19
+ name: 'sqlite',
20
+ capabilities: CAPABILITIES,
21
+ async exec (sql) {
22
+ await conn.exec(sql);
23
+ },
24
+ async prepare (sql) {
25
+ return conn.prepare(sql);
26
+ },
27
+ async runBatch (sql, paramRows) {
28
+ await conn.runBatch(sql, paramRows);
29
+ },
30
+ async transaction (fn) {
31
+ return withTransaction(conn, fn);
32
+ },
33
+ async reconcile () {
34
+ return reconcile(conn, cfg, baseDir);
35
+ },
36
+ docs: {
37
+ async columns () {
38
+ return [
39
+ ...await getColumns(conn)
40
+ ];
41
+ }
42
+ },
43
+ lexical: {
44
+ async query (terms, opts) {
45
+ return queryLexical(conn, terms, opts, contentTokenize(cfg) === undefined);
46
+ }
47
+ },
48
+ vectors: {
49
+ async pending () {
50
+ return pendingRows(conn);
51
+ },
52
+ async writeVectors (rows) {
53
+ await writeVectorBatch(conn, rows);
54
+ },
55
+ async candidates (qv, storeDims, fetch, allowed) {
56
+ return scanCandidates(conn, qv, storeDims, fetch, allowed);
57
+ },
58
+ async similar (path, opts) {
59
+ return scanSimilar(conn, path, opts);
60
+ },
61
+ async hasVector (path) {
62
+ return hasVectorRow(conn, path);
63
+ }
64
+ },
65
+ raw: {
66
+ async prepare (sql) {
67
+ const stmt = db.prepare(sql);
68
+ stmt.setReadBigInts(true); // int64 past 2^53 arrives as BigInt instead of throwing at step time
69
+ return {
70
+ columns: ()=>stmt.columns(),
71
+ // sense sql streams through an async iterator wrapping the sync iterate().
72
+ iterate: async function*(...params) {
73
+ yield* stmt.iterate(...params);
74
+ }
75
+ };
76
+ }
77
+ },
78
+ async close () {
79
+ db.close();
80
+ }
81
+ };
82
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/store.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite';\nimport type { Config } from '../../config/index.ts';\nimport { contentTokenize } from '../../config/index.ts';\nimport { getColumns } from '../shared.ts';\nimport { withTransaction } from '../transaction.ts';\nimport type { Capability, Connection, Statement, Store, VectorWriteRow } from '../types.ts';\nimport { queryLexical } from './lexical.ts';\nimport { reconcile } from './reconcile.ts';\nimport { hasVectorRow, pendingRows, scanCandidates, scanSimilar, writeVectorBatch } from './vectors.ts';\n\nexport const CAPABILITIES: ReadonlySet<Capability> = new Set(['phrases', 'snippets', 'watch-concurrency', 'lexical', 'vectors']);\n\n// Wraps the synchronous DatabaseSync connection in the async Store interface, sharing one\n// Connection instance (conn) with open()'s own reconcile call so transaction depth (see\n// transaction.ts) is tracked against the same object everywhere.\nexport function createStore(db: DatabaseSync, conn: Connection, cfg: Config, baseDir: string): Store {\n return {\n name: 'sqlite',\n capabilities: CAPABILITIES,\n async exec(sql: string): Promise<void> {\n await conn.exec(sql);\n },\n async prepare(sql: string): Promise<Statement> {\n return conn.prepare(sql);\n },\n async runBatch(sql: string, paramRows: unknown[][]): Promise<void> {\n await conn.runBatch(sql, paramRows);\n },\n async transaction<T>(fn: () => Promise<T>): Promise<T> {\n return withTransaction(conn, fn);\n },\n async reconcile() {\n return reconcile(conn, cfg, baseDir);\n },\n docs: {\n async columns() {\n return [...(await getColumns(conn))];\n },\n },\n lexical: {\n async query(terms, opts) {\n return queryLexical(conn, terms, opts, contentTokenize(cfg) === undefined);\n },\n },\n vectors: {\n async pending() {\n return pendingRows(conn);\n },\n async writeVectors(rows: VectorWriteRow[]) {\n await writeVectorBatch(conn, rows);\n },\n async candidates(qv, storeDims, fetch, allowed) {\n return scanCandidates(conn, qv, storeDims, fetch, allowed);\n },\n async similar(path, opts) {\n return scanSimilar(conn, path, opts);\n },\n async hasVector(path) {\n return hasVectorRow(conn, path);\n },\n },\n raw: {\n async prepare(sql: string) {\n const stmt = db.prepare(sql);\n stmt.setReadBigInts(true); // int64 past 2^53 arrives as BigInt instead of throwing at step time\n return {\n columns: () => stmt.columns(),\n // sense sql streams through an async iterator wrapping the sync iterate().\n iterate: async function* (...params: unknown[]) {\n yield* stmt.iterate(...(params as Parameters<typeof stmt.iterate>));\n },\n };\n },\n },\n async close() {\n db.close();\n },\n };\n}\n"],"names":["contentTokenize","getColumns","withTransaction","queryLexical","reconcile","hasVectorRow","pendingRows","scanCandidates","scanSimilar","writeVectorBatch","CAPABILITIES","Set","createStore","db","conn","cfg","baseDir","name","capabilities","exec","sql","prepare","runBatch","paramRows","transaction","fn","docs","columns","lexical","query","terms","opts","undefined","vectors","pending","writeVectors","rows","candidates","qv","storeDims","fetch","allowed","similar","path","hasVector","raw","stmt","setReadBigInts","iterate","params","close"],"mappings":"AAEA,SAASA,eAAe,QAAQ,wBAAwB;AACxD,SAASC,UAAU,QAAQ,eAAe;AAC1C,SAASC,eAAe,QAAQ,oBAAoB;AAEpD,SAASC,YAAY,QAAQ,eAAe;AAC5C,SAASC,SAAS,QAAQ,iBAAiB;AAC3C,SAASC,YAAY,EAAEC,WAAW,EAAEC,cAAc,EAAEC,WAAW,EAAEC,gBAAgB,QAAQ,eAAe;AAExG,OAAO,MAAMC,eAAwC,IAAIC,IAAI;IAAC;IAAW;IAAY;IAAqB;IAAW;CAAU,EAAE;AAEjI,0FAA0F;AAC1F,wFAAwF;AACxF,iEAAiE;AACjE,OAAO,SAASC,YAAYC,EAAgB,EAAEC,IAAgB,EAAEC,GAAW,EAAEC,OAAe;IAC1F,OAAO;QACLC,MAAM;QACNC,cAAcR;QACd,MAAMS,MAAKC,GAAW;YACpB,MAAMN,KAAKK,IAAI,CAACC;QAClB;QACA,MAAMC,SAAQD,GAAW;YACvB,OAAON,KAAKO,OAAO,CAACD;QACtB;QACA,MAAME,UAASF,GAAW,EAAEG,SAAsB;YAChD,MAAMT,KAAKQ,QAAQ,CAACF,KAAKG;QAC3B;QACA,MAAMC,aAAeC,EAAoB;YACvC,OAAOvB,gBAAgBY,MAAMW;QAC/B;QACA,MAAMrB;YACJ,OAAOA,UAAUU,MAAMC,KAAKC;QAC9B;QACAU,MAAM;YACJ,MAAMC;gBACJ,OAAO;uBAAK,MAAM1B,WAAWa;iBAAO;YACtC;QACF;QACAc,SAAS;YACP,MAAMC,OAAMC,KAAK,EAAEC,IAAI;gBACrB,OAAO5B,aAAaW,MAAMgB,OAAOC,MAAM/B,gBAAgBe,SAASiB;YAClE;QACF;QACAC,SAAS;YACP,MAAMC;gBACJ,OAAO5B,YAAYQ;YACrB;YACA,MAAMqB,cAAaC,IAAsB;gBACvC,MAAM3B,iBAAiBK,MAAMsB;YAC/B;YACA,MAAMC,YAAWC,EAAE,EAAEC,SAAS,EAAEC,KAAK,EAAEC,OAAO;gBAC5C,OAAOlC,eAAeO,MAAMwB,IAAIC,WAAWC,OAAOC;YACpD;YACA,MAAMC,SAAQC,IAAI,EAAEZ,IAAI;gBACtB,OAAOvB,YAAYM,MAAM6B,MAAMZ;YACjC;YACA,MAAMa,WAAUD,IAAI;gBAClB,OAAOtC,aAAaS,MAAM6B;YAC5B;QACF;QACAE,KAAK;YACH,MAAMxB,SAAQD,GAAW;gBACvB,MAAM0B,OAAOjC,GAAGQ,OAAO,CAACD;gBACxB0B,KAAKC,cAAc,CAAC,OAAO,qEAAqE;gBAChG,OAAO;oBACLpB,SAAS,IAAMmB,KAAKnB,OAAO;oBAC3B,2EAA2E;oBAC3EqB,SAAS,gBAAiB,GAAGC,MAAiB;wBAC5C,OAAOH,KAAKE,OAAO,IAAKC;oBAC1B;gBACF;YACF;QACF;QACA,MAAMC;YACJrC,GAAGqC,KAAK;QACV;IACF;AACF"}
@@ -0,0 +1,13 @@
1
+ import type { Connection, VectorCandidate, VectorSimilar, VectorWriteRow } from '../types.js';
2
+ export declare function pendingRows(conn: Connection): Promise<Array<{
3
+ path: string;
4
+ chunk: number;
5
+ }>>;
6
+ export declare function writeVectorBatch(conn: Connection, rows: VectorWriteRow[]): Promise<void>;
7
+ export declare function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>): Promise<VectorCandidate[]>;
8
+ export declare function scanSimilar(conn: Connection, path: string, opts: {
9
+ exclude: Set<string>;
10
+ allowed?: Set<string>;
11
+ k: number;
12
+ }): Promise<VectorSimilar[]>;
13
+ export declare function hasVectorRow(conn: Connection, path: string): Promise<boolean>;
@@ -0,0 +1,90 @@
1
+ // Seed chunks that participate in a `related` scan; see similar() for the measurement.
2
+ const TARGET_CHUNK_CAP = 16;
3
+ // Dequantised int8 dot products land a little either side of a true cosine, so an identical
4
+ // pair prints 1.001 and undermines a column whose whole job is being a bounded number.
5
+ function asCosine(score) {
6
+ return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;
7
+ }
8
+ export async function pendingRows(conn) {
9
+ const stmt = await conn.prepare('SELECT "path", chunk FROM embeddings WHERE vector IS NULL ORDER BY "path", chunk');
10
+ return await stmt.all();
11
+ }
12
+ // One runBatch call per provider batch: the caller (embed/query.ts) already batches by the
13
+ // provider's batchCap, so this is one crossing per batch, never per row.
14
+ export async function writeVectorBatch(conn, rows) {
15
+ if (rows.length === 0) return;
16
+ await conn.runBatch('UPDATE embeddings SET scale = ?, vector = ? WHERE "path" = ? AND chunk = ?', rows.map((row)=>[
17
+ row.scale,
18
+ row.vector,
19
+ row.path,
20
+ row.chunk
21
+ ]));
22
+ }
23
+ // Best chunk per file by cosine, its line range riding along: the JS loop over stored int8
24
+ // BLOBs this store's scan strategy is (a native store scans differently, same contract).
25
+ export async function scanCandidates(conn, qv, storeDims, fetch, allowed) {
26
+ const stmt = await conn.prepare('SELECT "path", start_line, end_line, scale, vector FROM embeddings WHERE vector IS NOT NULL');
27
+ const rows = await stmt.all();
28
+ const best = new Map();
29
+ for (const row of rows){
30
+ if (allowed && !allowed.has(row.path)) continue;
31
+ const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));
32
+ let dot = 0;
33
+ for(let d = 0; d < q.length; d++)dot += q[d] * qv[d];
34
+ const score = dot * row.scale;
35
+ const existing = best.get(row.path);
36
+ if (!existing || score > existing.score) best.set(row.path, {
37
+ score,
38
+ lines: `L${row.start_line}-${row.end_line}`
39
+ });
40
+ }
41
+ return [
42
+ ...best.entries()
43
+ ].sort((a, b)=>b[1].score - a[1].score).slice(0, fetch).map(([path, b])=>({
44
+ path,
45
+ lines: b.lines,
46
+ similarity: asCosine(b.score)
47
+ }));
48
+ }
49
+ // Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear
50
+ // scan of stored vectors.
51
+ export async function scanSimilar(conn, path, opts) {
52
+ // Cost is target_chunks x stored_chunks, so a heading-dense seed multiplies a full-corpus
53
+ // scan (12.7s at 201 chunks/note). Sample evenly, so late sections still get a vote.
54
+ const targetStmt = await conn.prepare('SELECT scale, vector FROM embeddings WHERE "path" = ? AND vector IS NOT NULL ORDER BY chunk');
55
+ const targetRows = await targetStmt.all(path);
56
+ if (targetRows.length === 0) return [];
57
+ const step = Math.max(1, Math.ceil(targetRows.length / TARGET_CHUNK_CAP));
58
+ const target = targetRows.filter((_, i)=>i % step === 0).map((row)=>({
59
+ v: new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength),
60
+ scale: row.scale
61
+ }));
62
+ const stmt = await conn.prepare('SELECT "path", scale, vector FROM embeddings WHERE vector IS NOT NULL');
63
+ const rows = await stmt.all();
64
+ const best = new Map();
65
+ for (const row of rows){
66
+ if (row.path === path || opts.exclude.has(row.path) || opts.allowed && !opts.allowed.has(row.path)) continue;
67
+ const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);
68
+ for (const t of target){
69
+ let dot = 0;
70
+ const len = Math.min(t.v.length, other.length);
71
+ for(let d = 0; d < len; d++)dot += t.v[d] * other[d];
72
+ const score = dot * t.scale * row.scale;
73
+ const existing = best.get(row.path);
74
+ if (existing === undefined || score > existing) best.set(row.path, score);
75
+ }
76
+ }
77
+ return [
78
+ ...best.entries()
79
+ ].sort((a, b)=>b[1] - a[1]).slice(0, opts.k).map(([p, score])=>({
80
+ path: p,
81
+ similarity: asCosine(score)
82
+ }));
83
+ }
84
+ // Whether a note has any chunk with a vector. Distinguishes "nothing is near this note" from
85
+ // "this note has no text", which look the same in an empty result.
86
+ export async function hasVectorRow(conn, path) {
87
+ const stmt = await conn.prepare('SELECT 1 AS ok FROM embeddings WHERE "path" = ? AND vector IS NOT NULL LIMIT 1');
88
+ const row = await stmt.get(path);
89
+ return row !== undefined;
90
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/sensemaking/src/store/sqlite/vectors.ts"],"sourcesContent":["import type { Connection, VectorCandidate, VectorSimilar, VectorWriteRow } from '../types.ts';\n\n// Seed chunks that participate in a `related` scan; see similar() for the measurement.\nconst TARGET_CHUNK_CAP = 16;\n\n// Dequantised int8 dot products land a little either side of a true cosine, so an identical\n// pair prints 1.001 and undermines a column whose whole job is being a bounded number.\nfunction asCosine(score: number): number {\n return Math.round(Math.min(1, Math.max(-1, score)) * 1000) / 1000;\n}\n\nexport async function pendingRows(conn: Connection): Promise<Array<{ path: string; chunk: number }>> {\n const stmt = await conn.prepare('SELECT \"path\", chunk FROM embeddings WHERE vector IS NULL ORDER BY \"path\", chunk');\n return (await stmt.all()) as Array<{ path: string; chunk: number }>;\n}\n\n// One runBatch call per provider batch: the caller (embed/query.ts) already batches by the\n// provider's batchCap, so this is one crossing per batch, never per row.\nexport async function writeVectorBatch(conn: Connection, rows: VectorWriteRow[]): Promise<void> {\n if (rows.length === 0) return;\n await conn.runBatch(\n 'UPDATE embeddings SET scale = ?, vector = ? WHERE \"path\" = ? AND chunk = ?',\n rows.map((row) => [row.scale, row.vector, row.path, row.chunk])\n );\n}\n\n// Best chunk per file by cosine, its line range riding along: the JS loop over stored int8\n// BLOBs this store's scan strategy is (a native store scans differently, same contract).\nexport async function scanCandidates(conn: Connection, qv: Float32Array, storeDims: number, fetch: number, allowed?: Set<string>): Promise<VectorCandidate[]> {\n const stmt = await conn.prepare('SELECT \"path\", start_line, end_line, scale, vector FROM embeddings WHERE vector IS NOT NULL');\n const rows = (await stmt.all()) as Array<{\n path: string;\n start_line: number;\n end_line: number;\n scale: number;\n vector: Uint8Array;\n }>;\n\n const best = new Map<string, { score: number; lines: string }>();\n for (const row of rows) {\n if (allowed && !allowed.has(row.path)) continue;\n const q = new Int8Array(row.vector.buffer, row.vector.byteOffset, Math.min(storeDims, row.vector.byteLength));\n let dot = 0;\n for (let d = 0; d < q.length; d++) dot += q[d] * qv[d];\n const score = dot * row.scale;\n const existing = best.get(row.path);\n if (!existing || score > existing.score) best.set(row.path, { score, lines: `L${row.start_line}-${row.end_line}` });\n }\n return [...best.entries()]\n .sort((a, b) => b[1].score - a[1].score)\n .slice(0, fetch)\n .map(([path, b]) => ({ path, lines: b.lines, similarity: asCosine(b.score) }));\n}\n\n// Note-to-note similarity is the max cosine over (target chunk, other chunk) pairs, one linear\n// scan of stored vectors.\nexport async function scanSimilar(conn: Connection, path: string, opts: { exclude: Set<string>; allowed?: Set<string>; k: number }): Promise<VectorSimilar[]> {\n // Cost is target_chunks x stored_chunks, so a heading-dense seed multiplies a full-corpus\n // scan (12.7s at 201 chunks/note). Sample evenly, so late sections still get a vote.\n const targetStmt = await conn.prepare('SELECT scale, vector FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL ORDER BY chunk');\n const targetRows = (await targetStmt.all(path)) as Array<{ scale: number; vector: Uint8Array }>;\n if (targetRows.length === 0) return [];\n const step = Math.max(1, Math.ceil(targetRows.length / TARGET_CHUNK_CAP));\n const target = targetRows.filter((_, i) => i % step === 0).map((row) => ({ v: new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength), scale: row.scale }));\n\n const stmt = await conn.prepare('SELECT \"path\", scale, vector FROM embeddings WHERE vector IS NOT NULL');\n const rows = (await stmt.all()) as Array<{ path: string; scale: number; vector: Uint8Array }>;\n const best = new Map<string, number>();\n for (const row of rows) {\n if (row.path === path || opts.exclude.has(row.path) || (opts.allowed && !opts.allowed.has(row.path))) continue;\n const other = new Int8Array(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength);\n for (const t of target) {\n let dot = 0;\n const len = Math.min(t.v.length, other.length);\n for (let d = 0; d < len; d++) dot += t.v[d] * other[d];\n const score = dot * t.scale * row.scale;\n const existing = best.get(row.path);\n if (existing === undefined || score > existing) best.set(row.path, score);\n }\n }\n\n return [...best.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, opts.k)\n .map(([p, score]) => ({ path: p, similarity: asCosine(score) }));\n}\n\n// Whether a note has any chunk with a vector. Distinguishes \"nothing is near this note\" from\n// \"this note has no text\", which look the same in an empty result.\nexport async function hasVectorRow(conn: Connection, path: string): Promise<boolean> {\n const stmt = await conn.prepare('SELECT 1 AS ok FROM embeddings WHERE \"path\" = ? AND vector IS NOT NULL LIMIT 1');\n const row = (await stmt.get(path)) as { ok: number } | undefined;\n return row !== undefined;\n}\n"],"names":["TARGET_CHUNK_CAP","asCosine","score","Math","round","min","max","pendingRows","conn","stmt","prepare","all","writeVectorBatch","rows","length","runBatch","map","row","scale","vector","path","chunk","scanCandidates","qv","storeDims","fetch","allowed","best","Map","has","q","Int8Array","buffer","byteOffset","byteLength","dot","d","existing","get","set","lines","start_line","end_line","entries","sort","a","b","slice","similarity","scanSimilar","opts","targetStmt","targetRows","step","ceil","target","filter","_","i","v","exclude","other","t","len","undefined","k","p","hasVectorRow"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ interface ExecConnection {
2
+ exec(sql: string): Promise<void>;
3
+ }
4
+ export declare function withTransaction<T>(conn: ExecConnection, fn: () => Promise<T>): Promise<T>;
5
+ export {};