@tekmidian/pai 0.54.0 → 0.55.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 (251) hide show
  1. package/dist/{aibroker-client-CNfIBrl5.mjs → aibroker-client-B8Wtasez.mjs} +1 -1
  2. package/dist/{aibroker-client-DzkTpVjx.mjs → aibroker-client-BK1h--W_.mjs} +3 -3
  3. package/dist/{aibroker-client-DzkTpVjx.mjs.map → aibroker-client-BK1h--W_.mjs.map} +1 -1
  4. package/dist/{indexer-backend-CdvCJCkD.mjs → async-CLvuY7b-.mjs} +7 -7
  5. package/dist/async-CLvuY7b-.mjs.map +1 -0
  6. package/dist/auto-route-CF8WNaoy.mjs +100 -0
  7. package/dist/auto-route-CF8WNaoy.mjs.map +1 -0
  8. package/dist/{chain-BcBDJ1Id.mjs → chain-DogidBji.mjs} +4 -4
  9. package/dist/{chain-BcBDJ1Id.mjs.map → chain-DogidBji.mjs.map} +1 -1
  10. package/dist/cli/index.mjs +22 -23
  11. package/dist/cli/index.mjs.map +1 -1
  12. package/dist/cli/program.d.mts.map +1 -1
  13. package/dist/cli/program.mjs +22 -23
  14. package/dist/{clusters-Cw_P6lO-.mjs → clusters-Ci1mseh2.mjs} +4 -36
  15. package/dist/clusters-Ci1mseh2.mjs.map +1 -0
  16. package/dist/{config-B3EPzRjf.mjs → config-B93YS8tP.mjs} +3 -3
  17. package/dist/{config-B3EPzRjf.mjs.map → config-B93YS8tP.mjs.map} +1 -1
  18. package/dist/{config-Bn9jpblO.mjs → config-D1omG_UU.mjs} +3 -3
  19. package/dist/{config-Bn9jpblO.mjs.map → config-D1omG_UU.mjs.map} +1 -1
  20. package/dist/{config-RjeVGJ7K.mjs → config-DCYWFaEi.mjs} +1 -1
  21. package/dist/{context-handover-cache-CXyH6QsC.mjs → context-handover-cache-k2kWbE6G.mjs} +82 -99
  22. package/dist/context-handover-cache-k2kWbE6G.mjs.map +1 -0
  23. package/dist/daemon/index.mjs +18 -24
  24. package/dist/daemon/index.mjs.map +1 -1
  25. package/dist/daemon-B7Y8GQnt.mjs +19 -0
  26. package/dist/{daemon-BkJNFWr9.mjs → daemon-Cgrdvdo6.mjs} +97 -466
  27. package/dist/daemon-Cgrdvdo6.mjs.map +1 -0
  28. package/dist/daemon-mcp/index.mjs +10 -10
  29. package/dist/daemon-mcp/index.mjs.map +1 -1
  30. package/dist/{detector-DWXkeTlk.mjs → detector-3g7XuU1r.mjs} +5 -12
  31. package/dist/detector-3g7XuU1r.mjs.map +1 -0
  32. package/dist/detector-DgDn56j-.mjs +3 -0
  33. package/dist/{embeddings-ClqKtLZ8.mjs → embeddings-BNv3TQC5.mjs} +1 -1
  34. package/dist/{embeddings-DvRKcQyt.mjs → embeddings-DfrYNIng.mjs} +1 -1
  35. package/dist/{embeddings-DvRKcQyt.mjs.map → embeddings-DfrYNIng.mjs.map} +1 -1
  36. package/dist/factory-C9wGUe30.mjs +3 -0
  37. package/dist/factory-DiuntvwG.mjs +250 -0
  38. package/dist/factory-DiuntvwG.mjs.map +1 -0
  39. package/dist/{fallback-CPfyTSTx.mjs → fallback-DS6AEJsU.mjs} +190 -132
  40. package/dist/fallback-DS6AEJsU.mjs.map +1 -0
  41. package/dist/federation-db-CJDw_Pwq.mjs +3 -0
  42. package/dist/{db-DJ8rk0VM.mjs → federation-db-w8e-sKjp.mjs} +5 -5
  43. package/dist/federation-db-w8e-sKjp.mjs.map +1 -0
  44. package/dist/{helpers-crDEr6S2.mjs → helpers-D9WkIf9R.mjs} +1 -1
  45. package/dist/{helpers-crDEr6S2.mjs.map → helpers-D9WkIf9R.mjs.map} +1 -1
  46. package/dist/hooks/capture-all-events.mjs.map +1 -1
  47. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  48. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  49. package/dist/hooks/context-compression-hook.mjs.map +1 -1
  50. package/dist/hooks/load-core-context.mjs +5 -0
  51. package/dist/hooks/load-core-context.mjs.map +2 -2
  52. package/dist/hooks/load-project-context.mjs.map +1 -1
  53. package/dist/hooks/post-compact-inject.mjs.map +1 -1
  54. package/dist/hooks/route-agents-to-worker.mjs.map +1 -1
  55. package/dist/hooks/route-edits-to-worker.mjs +1 -1
  56. package/dist/hooks/route-edits-to-worker.mjs.map +2 -2
  57. package/dist/hooks/security-validator.mjs +8367 -19
  58. package/dist/hooks/security-validator.mjs.map +4 -4
  59. package/dist/hooks/stop-hook.mjs.map +2 -2
  60. package/dist/hooks/whisper-rules.mjs.map +2 -2
  61. package/dist/hooks/worker-proxy.mjs.map +1 -1
  62. package/dist/hooks/worker-status-line.mjs.map +2 -2
  63. package/dist/hooks/worker-supervision.mjs.map +1 -1
  64. package/dist/index.d.mts +327 -100
  65. package/dist/index.d.mts.map +1 -1
  66. package/dist/index.mjs +7 -11
  67. package/dist/indexer-backend-Cc-_NK3q.mjs +1 -0
  68. package/dist/indexer-backend-DcDtin1O.mjs +5 -0
  69. package/dist/{ipc-client-C6NryMMb.mjs → ipc-client-Dasm515c.mjs} +2 -2
  70. package/dist/{ipc-client-C6NryMMb.mjs.map → ipc-client-Dasm515c.mjs.map} +1 -1
  71. package/dist/kg-entity-Cj8Q6YmU.mjs +29 -0
  72. package/dist/kg-entity-Cj8Q6YmU.mjs.map +1 -0
  73. package/dist/{latent-ideas-BfL4nCQ3.mjs → latent-ideas-BRy6HNwg.mjs} +4 -4
  74. package/dist/{latent-ideas-BfL4nCQ3.mjs.map → latent-ideas-BRy6HNwg.mjs.map} +1 -1
  75. package/dist/{link-boost-C5PaNdzm.mjs → link-boost-BptbAPuv.mjs} +1 -1
  76. package/dist/{link-boost-C5PaNdzm.mjs.map → link-boost-BptbAPuv.mjs.map} +1 -1
  77. package/dist/{main-resolver-Bt1f6jJc.mjs → main-resolver-Dx-pxic1.mjs} +56 -31
  78. package/dist/main-resolver-Dx-pxic1.mjs.map +1 -0
  79. package/dist/main-resolver-uDtK9p6e.mjs +7 -0
  80. package/dist/merge-BncjJTfb.mjs +3 -0
  81. package/dist/merge-xoyfZQ4W.mjs +6 -0
  82. package/dist/merge-xoyfZQ4W.mjs.map +1 -0
  83. package/dist/{migrate-CfERwXhY.mjs → migrate-BD7D8EEh.mjs} +21 -54
  84. package/dist/migrate-BD7D8EEh.mjs.map +1 -0
  85. package/dist/{neighborhood-CETR_P4y.mjs → neighborhood-D8uscnGy.mjs} +4 -25
  86. package/dist/neighborhood-D8uscnGy.mjs.map +1 -0
  87. package/dist/{note-context-DCcRWBoN.mjs → note-context-CgffaUMh.mjs} +3 -23
  88. package/dist/note-context-CgffaUMh.mjs.map +1 -0
  89. package/dist/{pai-home-ChAOupZc.mjs → pai-home-CeOIYltV.mjs} +2 -2
  90. package/dist/pai-home-CeOIYltV.mjs.map +1 -0
  91. package/dist/{pai-marker-B20KqhA8.mjs → pai-marker-D1MMswkz.mjs} +1 -1
  92. package/dist/{pai-marker-B20KqhA8.mjs.map → pai-marker-D1MMswkz.mjs.map} +1 -1
  93. package/dist/{planner-BLbCtBUa.mjs → planner-C2wWnZrZ.mjs} +7 -7
  94. package/dist/{planner-BLbCtBUa.mjs.map → planner-C2wWnZrZ.mjs.map} +1 -1
  95. package/dist/{postgres-CUSXWgTG.mjs → postgres-nvU5pShF.mjs} +863 -4
  96. package/dist/postgres-nvU5pShF.mjs.map +1 -0
  97. package/dist/{program-DYux0CW2.mjs → program-Cb-9J5Ie.mjs} +2260 -1399
  98. package/dist/program-Cb-9J5Ie.mjs.map +1 -0
  99. package/dist/query-feedback-COYCnvHQ.mjs +3 -0
  100. package/dist/{query-feedback-CTs_jx6i.mjs → query-feedback-xz5axTj3.mjs} +3 -3
  101. package/dist/query-feedback-xz5axTj3.mjs.map +1 -0
  102. package/dist/{db-Ddns80yf.mjs → registry-db-Bkpbp7Gf.mjs} +5 -5
  103. package/dist/registry-db-Bkpbp7Gf.mjs.map +1 -0
  104. package/dist/registry-db-DwXpf7yF.mjs +3 -0
  105. package/dist/registry-postgres-B07-hmn8.mjs +795 -0
  106. package/dist/registry-postgres-B07-hmn8.mjs.map +1 -0
  107. package/dist/registry-sqlite-CkGND_tm.mjs +593 -0
  108. package/dist/registry-sqlite-CkGND_tm.mjs.map +1 -0
  109. package/dist/{reranker-B5qUTamW.mjs → reranker-BS5gqGeF.mjs} +1 -1
  110. package/dist/{reranker-HHmhZk6v.mjs → reranker-CRVawoEh.mjs} +1 -1
  111. package/dist/{reranker-HHmhZk6v.mjs.map → reranker-CRVawoEh.mjs.map} +1 -1
  112. package/dist/router-BXOf_e9e.mjs +3 -0
  113. package/dist/{router-DBAZfE7X.mjs → router-BdxdsAvs.mjs} +2 -2
  114. package/dist/{router-DBAZfE7X.mjs.map → router-BdxdsAvs.mjs.map} +1 -1
  115. package/dist/{run-C7EDdVAB.mjs → run-DkTrZejF.mjs} +268 -53
  116. package/dist/run-DkTrZejF.mjs.map +1 -0
  117. package/dist/{runtime-paths-B0P1TvUr.mjs → runtime-paths-BRwDeBEg.mjs} +1 -1
  118. package/dist/{runtime-paths-B0P1TvUr.mjs.map → runtime-paths-BRwDeBEg.mjs.map} +1 -1
  119. package/dist/search-BQHL5wDr.mjs +3 -0
  120. package/dist/search-BaTbyWjK.mjs +107 -0
  121. package/dist/search-BaTbyWjK.mjs.map +1 -0
  122. package/dist/{server-CGiHdaUp.mjs → server-CGuZFOeR.mjs} +4 -4
  123. package/dist/{server-CGiHdaUp.mjs.map → server-CGuZFOeR.mjs.map} +1 -1
  124. package/dist/{session-keepalive-DC2JGggK.mjs → session-keepalive-C5_69Av6.mjs} +7 -7
  125. package/dist/{session-keepalive-DC2JGggK.mjs.map → session-keepalive-C5_69Av6.mjs.map} +1 -1
  126. package/dist/skills/Plan/SKILL.md +1 -1
  127. package/dist/{sources-KBGEA_E6.mjs → sources-BBe3AZ-W.mjs} +8 -27
  128. package/dist/sources-BBe3AZ-W.mjs.map +1 -0
  129. package/dist/sqlite-D9p5a4qe.mjs +935 -0
  130. package/dist/sqlite-D9p5a4qe.mjs.map +1 -0
  131. package/dist/state-CwlGAKgF.mjs +3 -0
  132. package/dist/{state-DwrsOoMi.mjs → state-n13uPK5B.mjs} +6 -5
  133. package/dist/state-n13uPK5B.mjs.map +1 -0
  134. package/dist/{stop-words-BaMEGVeY.mjs → stop-words-ClCwO8mZ.mjs} +1 -1
  135. package/dist/{stop-words-BaMEGVeY.mjs.map → stop-words-ClCwO8mZ.mjs.map} +1 -1
  136. package/dist/{themes-BVoAnLZj.mjs → themes-B2v7RNnA.mjs} +3 -3
  137. package/dist/{themes-BVoAnLZj.mjs.map → themes-B2v7RNnA.mjs.map} +1 -1
  138. package/dist/{tools-BEqiJt_j.mjs → tools-Diro2s8J.mjs} +157 -398
  139. package/dist/tools-Diro2s8J.mjs.map +1 -0
  140. package/dist/{tools-BwsOTmjh.mjs → tools-KgL1rkPD.mjs} +4 -4
  141. package/dist/{trace-Cy8quQTm.mjs → trace-BRflVyBi.mjs} +1 -1
  142. package/dist/{trace-Cy8quQTm.mjs.map → trace-BRflVyBi.mjs.map} +1 -1
  143. package/dist/{utils-BD8g8oSf.mjs → utils-BrX9FLeK.mjs} +1 -1
  144. package/dist/{utils-CJymHkYm.mjs → utils-C9HsYDpO.mjs} +1 -1
  145. package/dist/{utils-CJymHkYm.mjs.map → utils-C9HsYDpO.mjs.map} +1 -1
  146. package/dist/{vault-indexer-C5i-OAyr.mjs → vault-indexer-DHYBpRZ5.mjs} +2 -2
  147. package/dist/{vault-indexer-C5i-OAyr.mjs.map → vault-indexer-DHYBpRZ5.mjs.map} +1 -1
  148. package/dist/{wakeup-Yk5lQ0jb.mjs → wakeup-D0uvqLsE.mjs} +95 -4
  149. package/dist/wakeup-D0uvqLsE.mjs.map +1 -0
  150. package/dist/work-queue-worker-DEbWsrDQ.mjs +11 -0
  151. package/dist/{work-queue-worker-utPp4hUv.mjs → work-queue-worker-DufMOtrL.mjs} +9 -9
  152. package/dist/work-queue-worker-DufMOtrL.mjs.map +1 -0
  153. package/dist/{zettelkasten-DezbXghl.mjs → zettelkasten-BPxSOalU.mjs} +4 -4
  154. package/dist/{zettelkasten-DezbXghl.mjs.map → zettelkasten-BPxSOalU.mjs.map} +1 -1
  155. package/docker/init.sql +5 -1
  156. package/docs/commands/README.md +5 -0
  157. package/docs/commands/audit.md +1 -1
  158. package/docs/commands/backup.md +1 -1
  159. package/docs/commands/clear-names.md +1 -1
  160. package/docs/commands/config.md +4 -4
  161. package/docs/commands/daemon.md +1 -1
  162. package/docs/commands/db.md +15 -1
  163. package/docs/commands/end.md +1 -1
  164. package/docs/commands/help.md +1 -1
  165. package/docs/commands/hooks-db.md +47 -0
  166. package/docs/commands/identity.md +1 -1
  167. package/docs/commands/kg.md +1 -1
  168. package/docs/commands/launch.md +1 -1
  169. package/docs/commands/mcp.md +1 -1
  170. package/docs/commands/memory.md +1 -1
  171. package/docs/commands/notify.md +1 -1
  172. package/docs/commands/observation.md +1 -1
  173. package/docs/commands/obsidian.md +1 -1
  174. package/docs/commands/pause.md +1 -1
  175. package/docs/commands/project.md +1 -1
  176. package/docs/commands/projects.md +1 -1
  177. package/docs/commands/registry.md +1 -1
  178. package/docs/commands/restore.md +1 -1
  179. package/docs/commands/session.md +2 -2
  180. package/docs/commands/sessions.md +1 -1
  181. package/docs/commands/setup.md +1 -1
  182. package/docs/commands/shell-init.md +1 -1
  183. package/docs/commands/skill.md +1 -1
  184. package/docs/commands/task.md +1 -1
  185. package/docs/commands/topic.md +1 -1
  186. package/docs/commands/update.md +1 -1
  187. package/docs/commands/worker.md +1 -1
  188. package/docs/commands/zettel.md +1 -1
  189. package/docs/design/postgres-only.md +438 -0
  190. package/docs/design/registry-method-map.md +174 -0
  191. package/package.json +1 -1
  192. package/src/hooks/pre-compact.sh +2 -40
  193. package/src/hooks/session-hooks-db.test.ts +93 -0
  194. package/src/hooks/session-stop.sh +2 -26
  195. package/src/hooks/ts/lib/edit-gate.test.ts +9 -0
  196. package/src/hooks/ts/lib/edit-gate.ts +5 -2
  197. package/src/hooks/ts/lib/plutil-guard.test.ts +54 -0
  198. package/src/hooks/ts/lib/plutil-guard.ts +43 -0
  199. package/src/hooks/ts/lib/worker-git-guard.test.ts +134 -0
  200. package/src/hooks/ts/lib/worker-git-guard.ts +103 -0
  201. package/src/hooks/ts/pre-tool-use/security-validator.test.ts +68 -0
  202. package/src/hooks/ts/pre-tool-use/security-validator.ts +69 -21
  203. package/src/hooks/ts/session-start/load-core-context.ts +13 -0
  204. package/src/hooks/ts/session-start/session-start-worker-guard.test.ts +13 -0
  205. package/dist/auto-route-CcD0OWWB.mjs +0 -86
  206. package/dist/auto-route-CcD0OWWB.mjs.map +0 -1
  207. package/dist/clusters-Cw_P6lO-.mjs.map +0 -1
  208. package/dist/context-handover-cache-CXyH6QsC.mjs.map +0 -1
  209. package/dist/daemon-BkJNFWr9.mjs.map +0 -1
  210. package/dist/daemon-CiythMfR.mjs +0 -25
  211. package/dist/db-BOBfewFH.mjs +0 -3
  212. package/dist/db-DJ8rk0VM.mjs.map +0 -1
  213. package/dist/db-DbY2O8QO.mjs +0 -3
  214. package/dist/db-Ddns80yf.mjs.map +0 -1
  215. package/dist/detect-Bf2z-oKB.mjs +0 -86
  216. package/dist/detect-Bf2z-oKB.mjs.map +0 -1
  217. package/dist/detector-CkwkXuGh.mjs +0 -5
  218. package/dist/detector-DWXkeTlk.mjs.map +0 -1
  219. package/dist/factory-Ba8j9K7q.mjs +0 -3
  220. package/dist/factory-Bt7TWQje.mjs +0 -150
  221. package/dist/factory-Bt7TWQje.mjs.map +0 -1
  222. package/dist/fallback-CPfyTSTx.mjs.map +0 -1
  223. package/dist/indexer-backend-CdvCJCkD.mjs.map +0 -1
  224. package/dist/kg-entity-BSW7ra8i.mjs +0 -176
  225. package/dist/kg-entity-BSW7ra8i.mjs.map +0 -1
  226. package/dist/main-resolver-Bt1f6jJc.mjs.map +0 -1
  227. package/dist/main-resolver-DYHhw5Ig.mjs +0 -7
  228. package/dist/migrate-CfERwXhY.mjs.map +0 -1
  229. package/dist/neighborhood-CETR_P4y.mjs.map +0 -1
  230. package/dist/note-context-DCcRWBoN.mjs.map +0 -1
  231. package/dist/pai-home-ChAOupZc.mjs.map +0 -1
  232. package/dist/postgres-CUSXWgTG.mjs.map +0 -1
  233. package/dist/program-DYux0CW2.mjs.map +0 -1
  234. package/dist/query-feedback-Bm6XvCmg.mjs +0 -3
  235. package/dist/query-feedback-CTs_jx6i.mjs.map +0 -1
  236. package/dist/router-CremHKTI.mjs +0 -3
  237. package/dist/run-C7EDdVAB.mjs.map +0 -1
  238. package/dist/search-B69cQwDB.mjs +0 -303
  239. package/dist/search-B69cQwDB.mjs.map +0 -1
  240. package/dist/search-fb-5ovj4.mjs +0 -4
  241. package/dist/sources-KBGEA_E6.mjs.map +0 -1
  242. package/dist/sqlite-By6r2YhZ.mjs +0 -271
  243. package/dist/sqlite-By6r2YhZ.mjs.map +0 -1
  244. package/dist/state-Ca0ltLgI.mjs +0 -3
  245. package/dist/state-DwrsOoMi.mjs.map +0 -1
  246. package/dist/sync-Btcb6xA4.mjs +0 -310
  247. package/dist/sync-Btcb6xA4.mjs.map +0 -1
  248. package/dist/tools-BEqiJt_j.mjs.map +0 -1
  249. package/dist/wakeup-Yk5lQ0jb.mjs.map +0 -1
  250. package/dist/work-queue-worker-DsdJx-jn.mjs +0 -14
  251. package/dist/work-queue-worker-utPp4hUv.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"sqlite-By6r2YhZ.mjs","names":[],"sources":["../src/storage/sqlite.ts"],"sourcesContent":["/**\n * SQLiteBackend — wraps the existing better-sqlite3 federation.db\n * behind the StorageBackend interface.\n *\n * This is a thin adapter. The heavy lifting is all in the existing\n * memory/indexer.ts and memory/search.ts code; we just provide a\n * backend-agnostic surface so the daemon and tools can call either\n * SQLite or Postgres transparently.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { StorageBackend, ChunkRow, FileRow, FederationStats } from \"./interface.js\";\nimport type { SearchResult, SearchOptions } from \"../memory/search.js\";\nimport { searchMemory, searchMemorySemantic } from \"../memory/search.js\";\n\nexport class SQLiteBackend implements StorageBackend {\n readonly backendType = \"sqlite\" as const;\n\n private db: Database;\n\n constructor(db: Database) {\n this.db = db;\n }\n\n /**\n * Expose the raw better-sqlite3 Database handle.\n * Used by the daemon to pass to indexAll() which still uses the synchronous API directly.\n */\n getRawDb(): Database {\n return this.db;\n }\n\n /**\n * Alias for getRawDb() — used by the dispatcher for operations that require\n * direct SQLite access (e.g. memory_feedback, touchChunksLastAccessed).\n */\n getSqliteDb(): Database {\n return this.db;\n }\n\n // -------------------------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------------------------\n\n async close(): Promise<void> {\n try {\n this.db.close();\n } catch {\n // ignore\n }\n }\n\n async getStats(): Promise<FederationStats> {\n const files = (\n this.db.prepare(\"SELECT COUNT(*) AS n FROM memory_files\").get() as { n: number }\n ).n;\n const chunks = (\n this.db.prepare(\"SELECT COUNT(*) AS n FROM memory_chunks\").get() as { n: number }\n ).n;\n return { files, chunks };\n }\n\n // -------------------------------------------------------------------------\n // File tracking\n // -------------------------------------------------------------------------\n\n async getFileHash(projectId: number, path: string): Promise<string | undefined> {\n const row = this.db\n .prepare(\"SELECT hash FROM memory_files WHERE project_id = ? AND path = ?\")\n .get(projectId, path) as { hash: string } | undefined;\n return row?.hash;\n }\n\n async upsertFile(file: FileRow): Promise<void> {\n this.db\n .prepare(\n `INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(project_id, path) DO UPDATE SET\n source = excluded.source,\n tier = excluded.tier,\n hash = excluded.hash,\n mtime = excluded.mtime,\n size = excluded.size`\n )\n .run(file.projectId, file.path, file.source, file.tier, file.hash, file.mtime, file.size);\n }\n\n // -------------------------------------------------------------------------\n // Chunk management\n // -------------------------------------------------------------------------\n\n async getChunkIds(projectId: number, path: string): Promise<string[]> {\n const rows = this.db\n .prepare(\"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\")\n .all(projectId, path) as Array<{ id: string }>;\n return rows.map((r) => r.id);\n }\n\n async deleteChunksForFile(projectId: number, path: string): Promise<void> {\n const ids = await this.getChunkIds(projectId, path);\n const deleteFts = this.db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n const deleteChunks = this.db.prepare(\n \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\"\n );\n this.db.transaction(() => {\n for (const id of ids) {\n deleteFts.run(id);\n }\n deleteChunks.run(projectId, path);\n })();\n }\n\n async insertChunks(chunks: ChunkRow[]): Promise<void> {\n if (chunks.length === 0) return;\n\n const insertChunk = this.db.prepare(\n `INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n );\n const insertFts = this.db.prepare(\n `INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n );\n\n this.db.transaction(() => {\n for (const c of chunks) {\n insertChunk.run(\n c.id,\n c.projectId,\n c.source,\n c.tier,\n c.path,\n c.startLine,\n c.endLine,\n c.hash,\n c.text,\n c.updatedAt\n );\n insertFts.run(\n c.text,\n c.id,\n c.projectId,\n c.path,\n c.source,\n c.tier,\n c.startLine,\n c.endLine\n );\n }\n })();\n }\n\n async getDistinctChunkPaths(projectId: number): Promise<string[]> {\n const rows = this.db\n .prepare(\"SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?\")\n .all(projectId) as Array<{ path: string }>;\n return rows.map((r) => r.path);\n }\n\n async deletePaths(projectId: number, paths: string[]): Promise<void> {\n if (paths.length === 0) return;\n const deleteFts = this.db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n const deleteChunks = this.db.prepare(\n \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\"\n );\n const deleteFile = this.db.prepare(\n \"DELETE FROM memory_files WHERE project_id = ? AND path = ?\"\n );\n this.db.transaction(() => {\n for (const path of paths) {\n const ids = this.db\n .prepare(\"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\")\n .all(projectId, path) as Array<{ id: string }>;\n for (const { id } of ids) {\n deleteFts.run(id);\n }\n deleteChunks.run(projectId, path);\n deleteFile.run(projectId, path);\n }\n })();\n }\n\n async getUnembeddedChunkIds(projectId?: number, limit?: number): Promise<Array<{ id: string; text: string; project_id: number; path: string }>> {\n const conditions = [\"embedding IS NULL\"];\n const params: (string | number)[] = [];\n\n if (projectId !== undefined) {\n conditions.push(\"project_id = ?\");\n params.push(projectId);\n }\n\n const where = \"WHERE \" + conditions.join(\" AND \");\n // Prioritize real knowledge notes over PAI session/job-search noise.\n // CASE expression assigns lower priority numbers to knowledge paths.\n const rows = this.db\n .prepare(`SELECT id, text, project_id, path FROM memory_chunks ${where}\n ORDER BY CASE\n WHEN path LIKE '🧠 Ideaverse/%' THEN 0\n WHEN path LIKE 'Z - Zettelkasten/%' THEN 0\n WHEN path LIKE '💼 Business/%' THEN 0\n WHEN path LIKE '📆 Meetings/%' THEN 1\n WHEN path LIKE '💡 Insights/%' THEN 1\n WHEN path LIKE '👨‍💻 People/%' THEN 1\n WHEN path LIKE 'University/%' THEN 1\n WHEN path LIKE 'Copilot/%' THEN 1\n WHEN path LIKE '🗓️ Daily Notes/%' THEN 2\n WHEN path LIKE 'PAI/%' THEN 3\n WHEN path LIKE '09-job-search/%' THEN 4\n WHEN path LIKE 'seriousletter/%' THEN 4\n WHEN path LIKE 'Attachments/%' THEN 5\n ELSE 2\n END, id${limit !== undefined ? \" LIMIT ?\" : \"\"}`)\n .all(...params, ...(limit !== undefined ? [limit] : [])) as Array<{ id: string; text: string; project_id: number; path: string }>;\n return rows;\n }\n\n async updateEmbedding(chunkId: string, embedding: Buffer): Promise<void> {\n this.db\n .prepare(\"UPDATE memory_chunks SET embedding = ? WHERE id = ?\")\n .run(embedding, chunkId);\n }\n\n // -------------------------------------------------------------------------\n // Search\n // -------------------------------------------------------------------------\n\n async searchKeyword(query: string, opts?: SearchOptions): Promise<SearchResult[]> {\n return searchMemory(this.db, query, opts);\n }\n\n async searchSemantic(queryEmbedding: Float32Array, opts?: SearchOptions): Promise<SearchResult[]> {\n return searchMemorySemantic(this.db, queryEmbedding, opts);\n }\n\n // -------------------------------------------------------------------------\n // Vault operations — not supported on SQLite backend (use Postgres)\n // -------------------------------------------------------------------------\n\n private vaultNotSupported(): never {\n throw new Error(\"Vault operations require the Postgres backend\");\n }\n\n async upsertVaultFile(): Promise<void> { this.vaultNotSupported(); }\n async deleteVaultFile(): Promise<void> { this.vaultNotSupported(); }\n async getVaultFile(): Promise<null> { this.vaultNotSupported(); }\n async getVaultFileByInode(): Promise<null> { this.vaultNotSupported(); }\n async getAllVaultFiles(): Promise<never[]> { this.vaultNotSupported(); }\n async getRecentVaultFiles(): Promise<never[]> { this.vaultNotSupported(); }\n async countVaultFiles(): Promise<number> { this.vaultNotSupported(); }\n async upsertVaultAliases(): Promise<void> { this.vaultNotSupported(); }\n async deleteVaultAliases(): Promise<void> { this.vaultNotSupported(); }\n async replaceLinksForSources(): Promise<void> { this.vaultNotSupported(); }\n async getLinksFromSource(): Promise<never[]> { this.vaultNotSupported(); }\n async getLinksToTarget(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultLinkGraph(): Promise<never[]> { this.vaultNotSupported(); }\n async upsertVaultHealth(): Promise<void> { this.vaultNotSupported(); }\n async getVaultHealth(): Promise<null> { this.vaultNotSupported(); }\n async getOrphans(): Promise<never[]> { this.vaultNotSupported(); }\n async getDeadLinks(): Promise<never[]> { this.vaultNotSupported(); }\n async upsertNameIndex(): Promise<void> { this.vaultNotSupported(); }\n async replaceNameIndex(): Promise<void> { this.vaultNotSupported(); }\n async resolveVaultName(): Promise<never[]> { this.vaultNotSupported(); }\n async searchVaultNameIndex(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultFilesByPaths(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultFilesByPathsAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultLinksFromPaths(): Promise<never[]> { this.vaultNotSupported(); }\n async getChunksWithEmbeddings(): Promise<never[]> { this.vaultNotSupported(); }\n async getChunksForPath(): Promise<never[]> { this.vaultNotSupported(); }\n async searchChunksByText(): Promise<never[]> { this.vaultNotSupported(); }\n async countVaultFilesWithPrefix(): Promise<number> { this.vaultNotSupported(); }\n async countVaultFilesAfter(): Promise<number> { this.vaultNotSupported(); }\n async countVaultLinksWithPrefix(): Promise<number> { this.vaultNotSupported(); }\n async countVaultLinksAfter(): Promise<number> { this.vaultNotSupported(); }\n async getDeadLinksWithLineNumbers(): Promise<never[]> { this.vaultNotSupported(); }\n async getDeadLinksWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n async getDeadLinksAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getOrphansWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n async getOrphansAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getLowConnectivity(): Promise<never[]> { this.vaultNotSupported(); }\n async getLowConnectivityWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n async getLowConnectivityAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getAllVaultFilePaths(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultFilePathsWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultFilePathsAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultLinkEdges(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultLinkEdgesWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultLinkEdgesAfter(): Promise<never[]> { this.vaultNotSupported(); }\n async getVaultAlias(): Promise<null> { this.vaultNotSupported(); }\n}\n"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import { A as vaultIndexInProgress, C as setShutdownRequested, D as shutdownRequested, E as setVaultIndexInProgress, O as startTime, S as setRegistryDb, T as setStorageBackend, _ as setIndexSchedulerTimer, a as indexInProgress, b as setLastVaultIndexTime, c as lastIndexTime, d as registryDb, f as setCacheKeepaliveTimer, g as setIndexInProgress, h as setEmbedSchedulerTimer, i as embedSchedulerTimer, k as storageBackend, l as lastVaultIndexTime, m as setEmbedInProgress, n as daemonConfig, o as indexSchedulerTimer, p as setDaemonConfig, r as embedInProgress, s as lastEmbedTime, t as cacheKeepaliveTimer, u as notificationConfig, v as setLastEmbedTime, w as setStartTime, x as setNotificationConfig, y as setLastIndexTime } from "./state-DwrsOoMi.mjs";
2
-
3
- export { lastVaultIndexTime, notificationConfig };
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-DwrsOoMi.mjs","names":[],"sources":["../src/daemon/daemon/state.ts"],"sourcesContent":["/**\n * Shared mutable daemon state — module-level singletons used across all daemon sub-modules.\n */\n\nimport { openRegistry } from \"../../registry/db.js\";\nimport type { StorageBackend } from \"../../storage/interface.js\";\nimport type { NotificationConfig } from \"../../notifications/types.js\";\nimport type { PaiDaemonConfig } from \"../config.js\";\n\n// ---------------------------------------------------------------------------\n// Core singletons (assigned at startup by serve())\n// ---------------------------------------------------------------------------\n\nexport let registryDb: ReturnType<typeof openRegistry>;\nexport let storageBackend: StorageBackend;\nexport let daemonConfig: PaiDaemonConfig;\nexport let startTime = Date.now();\n\n// ---------------------------------------------------------------------------\n// Scheduler state\n// ---------------------------------------------------------------------------\n\n/** True while a project index pass is running. */\nexport let indexInProgress = false;\nexport let lastIndexTime = 0;\nexport let indexSchedulerTimer: ReturnType<typeof setInterval> | null = null;\n\n/** True while an embedding pass is running. */\nexport let embedInProgress = false;\nexport let lastEmbedTime = 0;\nexport let embedSchedulerTimer: ReturnType<typeof setInterval> | null = null;\n\n/** True while a vault index pass is running. */\nexport let vaultIndexInProgress = false;\nexport let lastVaultIndexTime = 0;\n\n/** Interval timer of the worker prompt-cache keepalive (null = not armed). */\nexport let cacheKeepaliveTimer: ReturnType<typeof setInterval> | null = null;\n\n// ---------------------------------------------------------------------------\n// Notification state\n// ---------------------------------------------------------------------------\n\n/** Mutable notification config — loaded from disk at startup, patchable at runtime. */\nexport let notificationConfig: NotificationConfig;\n\n// ---------------------------------------------------------------------------\n// Graceful shutdown flag\n// ---------------------------------------------------------------------------\n\n/**\n * Set to true when a SIGTERM/SIGINT is received so that long-running loops\n * (embed, index) can detect the signal and exit their inner loops before the\n * pool/backend is closed.\n */\nexport let shutdownRequested = false;\n\n// ---------------------------------------------------------------------------\n// Setters (TypeScript requires assignment functions for exported `let` vars\n// that need to be mutated across module boundaries)\n// ---------------------------------------------------------------------------\n\nexport function setRegistryDb(db: ReturnType<typeof openRegistry>): void { registryDb = db; }\nexport function setStorageBackend(b: StorageBackend): void { storageBackend = b; }\nexport function setDaemonConfig(c: PaiDaemonConfig): void { daemonConfig = c; }\nexport function setStartTime(t: number): void { startTime = t; }\nexport function setNotificationConfig(c: NotificationConfig): void { notificationConfig = c; }\nexport function setShutdownRequested(v: boolean): void { shutdownRequested = v; }\nexport function setIndexInProgress(v: boolean): void { indexInProgress = v; }\nexport function setLastIndexTime(v: number): void { lastIndexTime = v; }\nexport function setIndexSchedulerTimer(v: ReturnType<typeof setInterval> | null): void { indexSchedulerTimer = v; }\nexport function setEmbedInProgress(v: boolean): void { embedInProgress = v; }\nexport function setLastEmbedTime(v: number): void { lastEmbedTime = v; }\nexport function setEmbedSchedulerTimer(v: ReturnType<typeof setInterval> | null): void { embedSchedulerTimer = v; }\nexport function setVaultIndexInProgress(v: boolean): void { vaultIndexInProgress = v; }\nexport function setLastVaultIndexTime(v: number): void { lastVaultIndexTime = v; }\nexport function setCacheKeepaliveTimer(v: ReturnType<typeof setInterval> | null): void { cacheKeepaliveTimer = v; }\n"],"mappings":";AAaA,IAAW;AACX,IAAW;AACX,IAAW;AACX,IAAW,YAAY,KAAK,KAAK;;AAOjC,IAAW,kBAAkB;AAC7B,IAAW,gBAAgB;AAC3B,IAAW,sBAA6D;;AAGxE,IAAW,kBAAkB;AAC7B,IAAW,gBAAgB;AAC3B,IAAW,sBAA6D;;AAGxE,IAAW,uBAAuB;AAClC,IAAW,qBAAqB;;AAGhC,IAAW,sBAA6D;;AAOxE,IAAW;;;;;;AAWX,IAAW,oBAAoB;AAO/B,SAAgB,cAAc,IAA2C;AAAE,cAAa;;AACxF,SAAgB,kBAAkB,GAAyB;AAAE,kBAAiB;;AAC9E,SAAgB,gBAAgB,GAA0B;AAAE,gBAAe;;AAC3E,SAAgB,aAAa,GAAiB;AAAE,aAAY;;AAC5D,SAAgB,sBAAsB,GAA6B;AAAE,sBAAqB;;AAC1F,SAAgB,qBAAqB,GAAkB;AAAE,qBAAoB;;AAC7E,SAAgB,mBAAmB,GAAkB;AAAE,mBAAkB;;AACzE,SAAgB,iBAAiB,GAAiB;AAAE,iBAAgB;;AACpE,SAAgB,uBAAuB,GAAgD;AAAE,uBAAsB;;AAC/G,SAAgB,mBAAmB,GAAkB;AAAE,mBAAkB;;AACzE,SAAgB,iBAAiB,GAAiB;AAAE,iBAAgB;;AACpE,SAAgB,uBAAuB,GAAgD;AAAE,uBAAsB;;AAC/G,SAAgB,wBAAwB,GAAkB;AAAE,wBAAuB;;AACnF,SAAgB,sBAAsB,GAAiB;AAAE,sBAAqB;;AAC9E,SAAgB,uBAAuB,GAAgD;AAAE,uBAAsB"}
@@ -1,310 +0,0 @@
1
- import { a as parseSessionTitleChunk, c as yieldToEventLoop, f as sha256File, i as isPathTooBroadForContentScan, l as chunkMarkdown, n as chunkId, o as walkContentFiles, r as detectTier, s as walkMdFiles, t as INDEX_YIELD_EVERY } from "./helpers-crDEr6S2.mjs";
2
- import { existsSync, readFileSync, statSync } from "node:fs";
3
- import { basename, join, relative } from "node:path";
4
-
5
- //#region src/memory/indexer/sync.ts
6
- /**
7
- * Synchronous (SQLite) indexer for the PAI federation memory engine.
8
- *
9
- * Scans project memory/ and Notes/ directories, chunks markdown files, and
10
- * inserts the resulting chunks into federation.db for BM25 search.
11
- *
12
- * Change detection: files whose SHA-256 hash has not changed since the last
13
- * index run are skipped, keeping incremental re-indexing fast.
14
- *
15
- * Uses raw better-sqlite3 Database directly for maximum SQLite performance
16
- * (synchronous transactions, no serialisation overhead).
17
- */
18
- /**
19
- * Index a single file into the federation database.
20
- *
21
- * @returns true if the file was re-indexed (changed or new), false if skipped.
22
- */
23
- function indexFile(db, projectId, rootPath, relativePath, source, tier) {
24
- const absPath = join(rootPath, relativePath);
25
- let content;
26
- let stat;
27
- try {
28
- content = readFileSync(absPath, "utf8");
29
- stat = statSync(absPath);
30
- } catch {
31
- return false;
32
- }
33
- const hash = sha256File(content);
34
- const mtime = Math.floor(stat.mtimeMs);
35
- const size = stat.size;
36
- if (db.prepare("SELECT hash FROM memory_files WHERE project_id = ? AND path = ?").get(projectId, relativePath)?.hash === hash) return false;
37
- const oldChunkIds = db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, relativePath);
38
- const deleteFts = db.prepare("DELETE FROM memory_fts WHERE id = ?");
39
- const deleteChunk = db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
40
- db.transaction(() => {
41
- for (const row of oldChunkIds) deleteFts.run(row.id);
42
- deleteChunk.run(projectId, relativePath);
43
- })();
44
- const chunks = chunkMarkdown(content);
45
- const insertChunk = db.prepare(`
46
- INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
47
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
48
- `);
49
- const insertFts = db.prepare(`
50
- INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
51
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
52
- `);
53
- const upsertFile = db.prepare(`
54
- INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)
55
- VALUES (?, ?, ?, ?, ?, ?, ?)
56
- ON CONFLICT(project_id, path) DO UPDATE SET
57
- source = excluded.source,
58
- tier = excluded.tier,
59
- hash = excluded.hash,
60
- mtime = excluded.mtime,
61
- size = excluded.size
62
- `);
63
- const updatedAt = Date.now();
64
- db.transaction(() => {
65
- for (let i = 0; i < chunks.length; i++) {
66
- const chunk = chunks[i];
67
- const id = chunkId(projectId, relativePath, i, chunk.startLine, chunk.endLine);
68
- insertChunk.run(id, projectId, source, tier, relativePath, chunk.startLine, chunk.endLine, chunk.hash, chunk.text, updatedAt);
69
- insertFts.run(chunk.text, id, projectId, relativePath, source, tier, chunk.startLine, chunk.endLine);
70
- }
71
- upsertFile.run(projectId, relativePath, source, tier, hash, mtime, size);
72
- })();
73
- return true;
74
- }
75
- /**
76
- * Index all memory, Notes, and content files for a single registered project.
77
- *
78
- * Scans:
79
- * - {rootPath}/MEMORY.md → source='memory', tier='evergreen'
80
- * - {rootPath}/memory/ → source='memory', tier from detectTier()
81
- * - {rootPath}/Notes/ → source='notes', tier='session'
82
- * - {rootPath}/**\/*.md → source='content', tier='topic' (all other .md files, recursive)
83
- * - {claudeNotesDir}/ → source='notes', tier='session' (if set and different)
84
- */
85
- async function indexProject(db, projectId, rootPath, claudeNotesDir) {
86
- const result = {
87
- filesProcessed: 0,
88
- chunksCreated: 0,
89
- filesSkipped: 0
90
- };
91
- const filesToIndex = [];
92
- const rootMemoryMd = join(rootPath, "MEMORY.md");
93
- if (existsSync(rootMemoryMd)) filesToIndex.push({
94
- absPath: rootMemoryMd,
95
- rootBase: rootPath,
96
- source: "memory",
97
- tier: "evergreen"
98
- });
99
- const memoryDir = join(rootPath, "memory");
100
- for (const absPath of walkMdFiles(memoryDir)) {
101
- const tier = detectTier(relative(rootPath, absPath));
102
- filesToIndex.push({
103
- absPath,
104
- rootBase: rootPath,
105
- source: "memory",
106
- tier
107
- });
108
- }
109
- const notesDir = join(rootPath, "Notes");
110
- for (const absPath of walkMdFiles(notesDir)) filesToIndex.push({
111
- absPath,
112
- rootBase: rootPath,
113
- source: "notes",
114
- tier: "session"
115
- });
116
- {
117
- const titleInsertChunk = db.prepare(`
118
- INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
119
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
120
- `);
121
- const titleInsertFts = db.prepare(`
122
- INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
123
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
124
- `);
125
- const updatedAt = Date.now();
126
- for (const absPath of walkMdFiles(notesDir)) {
127
- const text = parseSessionTitleChunk(basename(absPath));
128
- if (!text) continue;
129
- const syntheticPath = `${relative(rootPath, absPath)}::title`;
130
- const id = chunkId(projectId, syntheticPath, 0, 0, 0);
131
- const hash = sha256File(text);
132
- db.transaction(() => {
133
- titleInsertChunk.run(id, projectId, "notes", "session", syntheticPath, 0, 0, hash, text, updatedAt);
134
- titleInsertFts.run(text, id, projectId, syntheticPath, "notes", "session", 0, 0);
135
- })();
136
- }
137
- }
138
- if (!isPathTooBroadForContentScan(rootPath)) for (const absPath of walkContentFiles(rootPath)) filesToIndex.push({
139
- absPath,
140
- rootBase: rootPath,
141
- source: "content",
142
- tier: "topic"
143
- });
144
- if (claudeNotesDir && claudeNotesDir !== notesDir) {
145
- for (const absPath of walkMdFiles(claudeNotesDir)) filesToIndex.push({
146
- absPath,
147
- rootBase: claudeNotesDir,
148
- source: "notes",
149
- tier: "session"
150
- });
151
- {
152
- const updatedAt = Date.now();
153
- const titleInsertChunk2 = db.prepare(`
154
- INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
155
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
156
- `);
157
- const titleInsertFts2 = db.prepare(`
158
- INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
159
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
160
- `);
161
- for (const absPath of walkMdFiles(claudeNotesDir)) {
162
- const text = parseSessionTitleChunk(basename(absPath));
163
- if (!text) continue;
164
- const syntheticPath = `${relative(claudeNotesDir, absPath)}::title`;
165
- const id = chunkId(projectId, syntheticPath, 0, 0, 0);
166
- const hash = sha256File(text);
167
- db.transaction(() => {
168
- titleInsertChunk2.run(id, projectId, "notes", "session", syntheticPath, 0, 0, hash, text, updatedAt);
169
- titleInsertFts2.run(text, id, projectId, syntheticPath, "notes", "session", 0, 0);
170
- })();
171
- }
172
- }
173
- if (claudeNotesDir.endsWith("/Notes")) {
174
- const claudeProjectDir = claudeNotesDir.slice(0, -6);
175
- const claudeMemoryDir = join(claudeProjectDir, "memory");
176
- const claudeMemoryMd = join(claudeProjectDir, "MEMORY.md");
177
- if (existsSync(claudeMemoryMd)) filesToIndex.push({
178
- absPath: claudeMemoryMd,
179
- rootBase: claudeProjectDir,
180
- source: "memory",
181
- tier: "evergreen"
182
- });
183
- for (const absPath of walkMdFiles(claudeMemoryDir)) {
184
- const tier = detectTier(relative(claudeProjectDir, absPath));
185
- filesToIndex.push({
186
- absPath,
187
- rootBase: claudeProjectDir,
188
- source: "memory",
189
- tier
190
- });
191
- }
192
- }
193
- }
194
- await yieldToEventLoop();
195
- let filesSinceYield = 0;
196
- for (const { absPath, rootBase, source, tier } of filesToIndex) {
197
- if (filesSinceYield >= INDEX_YIELD_EVERY) {
198
- await yieldToEventLoop();
199
- filesSinceYield = 0;
200
- }
201
- filesSinceYield++;
202
- const relPath = relative(rootBase, absPath);
203
- if (indexFile(db, projectId, rootBase, relPath, source, tier)) {
204
- const count = db.prepare("SELECT COUNT(*) as n FROM memory_chunks WHERE project_id = ? AND path = ?").get(projectId, relPath);
205
- result.filesProcessed++;
206
- result.chunksCreated += count.n;
207
- } else result.filesSkipped++;
208
- }
209
- const livePaths = /* @__PURE__ */ new Set();
210
- for (const { absPath, rootBase } of filesToIndex) livePaths.add(relative(rootBase, absPath));
211
- const dbChunkPaths = db.prepare("SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?").all(projectId);
212
- const stalePaths = [];
213
- for (const row of dbChunkPaths) {
214
- const basePath = row.path.endsWith("::title") ? row.path.slice(0, -7) : row.path;
215
- if (!livePaths.has(basePath)) stalePaths.push(row.path);
216
- }
217
- if (stalePaths.length > 0) {
218
- const deleteChunksFts = db.prepare("DELETE FROM memory_fts WHERE id = ?");
219
- const deleteChunks = db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
220
- const deleteFile = db.prepare("DELETE FROM memory_files WHERE project_id = ? AND path = ?");
221
- db.transaction(() => {
222
- for (const stalePath of stalePaths) {
223
- const chunkIds = db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, stalePath);
224
- for (const { id } of chunkIds) deleteChunksFts.run(id);
225
- deleteChunks.run(projectId, stalePath);
226
- deleteFile.run(projectId, stalePath);
227
- }
228
- })();
229
- }
230
- return result;
231
- }
232
- /**
233
- * Index all active projects registered in the registry DB.
234
- *
235
- * Async: yields to the event loop between each project so that the daemon's
236
- * Unix socket server can process IPC requests (e.g. status) while indexing.
237
- */
238
- async function indexAll(db, registryDb) {
239
- const projects = registryDb.prepare("SELECT id, root_path, claude_notes_dir FROM projects WHERE status = 'active'").all();
240
- const totals = {
241
- filesProcessed: 0,
242
- chunksCreated: 0,
243
- filesSkipped: 0
244
- };
245
- for (const project of projects) {
246
- await yieldToEventLoop();
247
- const r = await indexProject(db, project.id, project.root_path, project.claude_notes_dir);
248
- totals.filesProcessed += r.filesProcessed;
249
- totals.chunksCreated += r.chunksCreated;
250
- totals.filesSkipped += r.filesSkipped;
251
- }
252
- return {
253
- projects: projects.length,
254
- result: totals
255
- };
256
- }
257
- /**
258
- * Generate and store embeddings for chunks that do not yet have one.
259
- *
260
- * Because better-sqlite3 is synchronous but the embedding pipeline is async,
261
- * we fetch all unembedded chunk texts first, generate embeddings in batches,
262
- * and then write them back in a transaction.
263
- *
264
- * @param db Open federation database.
265
- * @param projectId Optional — restrict to a specific project.
266
- * @param batchSize Number of chunks to embed per round. Default 50.
267
- * @param onProgress Optional callback called after each batch with running totals.
268
- */
269
- async function embedChunks(db, projectId, batchSize = 50, onProgress) {
270
- const { generateEmbedding, serializeEmbedding } = await import("./embeddings-ClqKtLZ8.mjs");
271
- const conditions = ["embedding IS NULL"];
272
- const params = [];
273
- if (projectId !== void 0) {
274
- conditions.push("project_id = ?");
275
- params.push(projectId);
276
- }
277
- const where = "WHERE " + conditions.join(" AND ");
278
- const rows = db.prepare(`SELECT id, text FROM memory_chunks ${where} ORDER BY id`).all(...params);
279
- if (rows.length === 0) return {
280
- chunksEmbedded: 0,
281
- chunksSkipped: 0
282
- };
283
- const updateStmt = db.prepare("UPDATE memory_chunks SET embedding = ? WHERE id = ?");
284
- let embedded = 0;
285
- const total = rows.length;
286
- for (let i = 0; i < rows.length; i += batchSize) {
287
- const batch = rows.slice(i, i + batchSize);
288
- const embeddings = [];
289
- for (const row of batch) {
290
- const blob = serializeEmbedding(await generateEmbedding(row.text));
291
- embeddings.push({
292
- id: row.id,
293
- blob
294
- });
295
- }
296
- db.transaction(() => {
297
- for (const { id, blob } of embeddings) updateStmt.run(blob, id);
298
- })();
299
- embedded += embeddings.length;
300
- onProgress?.(embedded, total);
301
- }
302
- return {
303
- chunksEmbedded: embedded,
304
- chunksSkipped: 0
305
- };
306
- }
307
-
308
- //#endregion
309
- export { indexProject as i, indexAll as n, indexFile as r, embedChunks as t };
310
- //# sourceMappingURL=sync-Btcb6xA4.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-Btcb6xA4.mjs","names":[],"sources":["../src/memory/indexer/sync.ts"],"sourcesContent":["/**\n * Synchronous (SQLite) indexer for the PAI federation memory engine.\n *\n * Scans project memory/ and Notes/ directories, chunks markdown files, and\n * inserts the resulting chunks into federation.db for BM25 search.\n *\n * Change detection: files whose SHA-256 hash has not changed since the last\n * index run are skipped, keeping incremental re-indexing fast.\n *\n * Uses raw better-sqlite3 Database directly for maximum SQLite performance\n * (synchronous transactions, no serialisation overhead).\n */\n\nimport { readFileSync, statSync, existsSync } from \"node:fs\";\nimport { join, relative, basename } from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { chunkMarkdown } from \"../chunker.js\";\nimport {\n sha256File,\n chunkId,\n detectTier,\n walkMdFiles,\n walkContentFiles,\n isPathTooBroadForContentScan,\n parseSessionTitleChunk,\n yieldToEventLoop,\n INDEX_YIELD_EVERY,\n} from \"./helpers.js\";\nimport type { IndexResult, EmbedResult } from \"./types.js\";\n\nexport type { IndexResult, EmbedResult };\n\n// Re-export detectTier for backward-compatibility (consumers import it from indexer.js)\nexport { detectTier };\n\n// ---------------------------------------------------------------------------\n// Single-file indexing\n// ---------------------------------------------------------------------------\n\n/**\n * Index a single file into the federation database.\n *\n * @returns true if the file was re-indexed (changed or new), false if skipped.\n */\nexport function indexFile(\n db: Database,\n projectId: number,\n rootPath: string,\n relativePath: string,\n source: string,\n tier: string,\n): boolean {\n const absPath = join(rootPath, relativePath);\n\n // Read file content\n let content: string;\n let stat: ReturnType<typeof statSync>;\n try {\n content = readFileSync(absPath, \"utf8\");\n stat = statSync(absPath);\n } catch {\n // File unreadable or missing — skip silently\n return false;\n }\n\n const hash = sha256File(content);\n const mtime = Math.floor(stat.mtimeMs);\n const size = stat.size;\n\n // Check if the file has changed since last index\n const existing = db\n .prepare(\n \"SELECT hash FROM memory_files WHERE project_id = ? AND path = ?\",\n )\n .get(projectId, relativePath) as { hash: string } | undefined;\n\n if (existing?.hash === hash) {\n // Unchanged — skip\n return false;\n }\n\n // Delete old chunks for this file from both tables\n const oldChunkIds = db\n .prepare(\n \"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\",\n )\n .all(projectId, relativePath) as Array<{ id: string }>;\n\n const deleteFts = db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n const deleteChunk = db.prepare(\n \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\",\n );\n\n db.transaction(() => {\n for (const row of oldChunkIds) {\n deleteFts.run(row.id);\n }\n deleteChunk.run(projectId, relativePath);\n })();\n\n // Chunk the new content\n const chunks = chunkMarkdown(content);\n\n // Insert new chunks into memory_chunks and memory_fts\n const insertChunk = db.prepare(`\n INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const insertFts = db.prepare(`\n INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const upsertFile = db.prepare(`\n INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(project_id, path) DO UPDATE SET\n source = excluded.source,\n tier = excluded.tier,\n hash = excluded.hash,\n mtime = excluded.mtime,\n size = excluded.size\n `);\n\n const updatedAt = Date.now();\n\n db.transaction(() => {\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i]!;\n const id = chunkId(projectId, relativePath, i, chunk.startLine, chunk.endLine);\n insertChunk.run(\n id,\n projectId,\n source,\n tier,\n relativePath,\n chunk.startLine,\n chunk.endLine,\n chunk.hash,\n chunk.text,\n updatedAt,\n );\n insertFts.run(\n chunk.text,\n id,\n projectId,\n relativePath,\n source,\n tier,\n chunk.startLine,\n chunk.endLine,\n );\n }\n upsertFile.run(projectId, relativePath, source, tier, hash, mtime, size);\n })();\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Project-level indexing\n// ---------------------------------------------------------------------------\n\n/**\n * Index all memory, Notes, and content files for a single registered project.\n *\n * Scans:\n * - {rootPath}/MEMORY.md → source='memory', tier='evergreen'\n * - {rootPath}/memory/ → source='memory', tier from detectTier()\n * - {rootPath}/Notes/ → source='notes', tier='session'\n * - {rootPath}/**\\/*.md → source='content', tier='topic' (all other .md files, recursive)\n * - {claudeNotesDir}/ → source='notes', tier='session' (if set and different)\n */\nexport async function indexProject(\n db: Database,\n projectId: number,\n rootPath: string,\n claudeNotesDir?: string | null,\n): Promise<IndexResult> {\n const result: IndexResult = {\n filesProcessed: 0,\n chunksCreated: 0,\n filesSkipped: 0,\n };\n\n const filesToIndex: Array<{ absPath: string; rootBase: string; source: string; tier: string }> = [];\n\n // Root-level MEMORY.md\n const rootMemoryMd = join(rootPath, \"MEMORY.md\");\n if (existsSync(rootMemoryMd)) {\n filesToIndex.push({ absPath: rootMemoryMd, rootBase: rootPath, source: \"memory\", tier: \"evergreen\" });\n }\n\n // memory/ directory\n const memoryDir = join(rootPath, \"memory\");\n for (const absPath of walkMdFiles(memoryDir)) {\n const relPath = relative(rootPath, absPath);\n const tier = detectTier(relPath);\n filesToIndex.push({ absPath, rootBase: rootPath, source: \"memory\", tier });\n }\n\n // {rootPath}/Notes/ directory\n const notesDir = join(rootPath, \"Notes\");\n for (const absPath of walkMdFiles(notesDir)) {\n filesToIndex.push({ absPath, rootBase: rootPath, source: \"notes\", tier: \"session\" });\n }\n\n // Synthetic session-title chunks for Notes files with the standard filename format:\n // \"NNNN - YYYY-MM-DD - Descriptive Title.md\"\n {\n const titleInsertChunk = db.prepare(`\n INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n const titleInsertFts = db.prepare(`\n INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n const updatedAt = Date.now();\n for (const absPath of walkMdFiles(notesDir)) {\n const fileName = basename(absPath);\n const text = parseSessionTitleChunk(fileName);\n if (!text) continue;\n const relPath = relative(rootPath, absPath);\n const syntheticPath = `${relPath}::title`;\n const id = chunkId(projectId, syntheticPath, 0, 0, 0);\n const hash = sha256File(text);\n db.transaction(() => {\n titleInsertChunk.run(id, projectId, \"notes\", \"session\", syntheticPath, 0, 0, hash, text, updatedAt);\n titleInsertFts.run(text, id, projectId, syntheticPath, \"notes\", \"session\", 0, 0);\n })();\n }\n }\n\n // {rootPath}/**/*.md — all other markdown content\n if (!isPathTooBroadForContentScan(rootPath)) {\n for (const absPath of walkContentFiles(rootPath)) {\n filesToIndex.push({ absPath, rootBase: rootPath, source: \"content\", tier: \"topic\" });\n }\n }\n\n // Claude Code session notes directory (~/.claude/projects/{encoded}/Notes/)\n if (claudeNotesDir && claudeNotesDir !== notesDir) {\n for (const absPath of walkMdFiles(claudeNotesDir)) {\n filesToIndex.push({ absPath, rootBase: claudeNotesDir, source: \"notes\", tier: \"session\" });\n }\n\n // Synthetic title chunks for claude notes dir\n {\n const updatedAt = Date.now();\n const titleInsertChunk2 = db.prepare(`\n INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n const titleInsertFts2 = db.prepare(`\n INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n for (const absPath of walkMdFiles(claudeNotesDir)) {\n const fileName = basename(absPath);\n const text = parseSessionTitleChunk(fileName);\n if (!text) continue;\n const relPath = relative(claudeNotesDir, absPath);\n const syntheticPath = `${relPath}::title`;\n const id = chunkId(projectId, syntheticPath, 0, 0, 0);\n const hash = sha256File(text);\n db.transaction(() => {\n titleInsertChunk2.run(id, projectId, \"notes\", \"session\", syntheticPath, 0, 0, hash, text, updatedAt);\n titleInsertFts2.run(text, id, projectId, syntheticPath, \"notes\", \"session\", 0, 0);\n })();\n }\n }\n\n // Derive the sibling memory/ directory: .../Notes/ → .../memory/\n if (claudeNotesDir.endsWith(\"/Notes\")) {\n const claudeProjectDir = claudeNotesDir.slice(0, -\"/Notes\".length);\n const claudeMemoryDir = join(claudeProjectDir, \"memory\");\n\n const claudeMemoryMd = join(claudeProjectDir, \"MEMORY.md\");\n if (existsSync(claudeMemoryMd)) {\n filesToIndex.push({\n absPath: claudeMemoryMd,\n rootBase: claudeProjectDir,\n source: \"memory\",\n tier: \"evergreen\",\n });\n }\n\n for (const absPath of walkMdFiles(claudeMemoryDir)) {\n const relPath = relative(claudeProjectDir, absPath);\n const tier = detectTier(relPath);\n filesToIndex.push({ absPath, rootBase: claudeProjectDir, source: \"memory\", tier });\n }\n }\n }\n\n // Yield after collection phase before processing\n await yieldToEventLoop();\n\n let filesSinceYield = 0;\n\n for (const { absPath, rootBase, source, tier } of filesToIndex) {\n if (filesSinceYield >= INDEX_YIELD_EVERY) {\n await yieldToEventLoop();\n filesSinceYield = 0;\n }\n filesSinceYield++;\n\n const relPath = relative(rootBase, absPath);\n const changed = indexFile(db, projectId, rootBase, relPath, source, tier);\n\n if (changed) {\n const count = db\n .prepare(\n \"SELECT COUNT(*) as n FROM memory_chunks WHERE project_id = ? AND path = ?\",\n )\n .get(projectId, relPath) as { n: number };\n\n result.filesProcessed++;\n result.chunksCreated += count.n;\n } else {\n result.filesSkipped++;\n }\n }\n\n // Prune stale paths: remove DB entries for files that no longer exist on disk.\n const livePaths = new Set<string>();\n for (const { absPath, rootBase } of filesToIndex) {\n livePaths.add(relative(rootBase, absPath));\n }\n\n const dbChunkPaths = db\n .prepare(\"SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?\")\n .all(projectId) as Array<{ path: string }>;\n\n const stalePaths: string[] = [];\n for (const row of dbChunkPaths) {\n const basePath = row.path.endsWith(\"::title\")\n ? row.path.slice(0, -\"::title\".length)\n : row.path;\n if (!livePaths.has(basePath)) {\n stalePaths.push(row.path);\n }\n }\n\n if (stalePaths.length > 0) {\n const deleteChunksFts = db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n const deleteChunks = db.prepare(\n \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\",\n );\n const deleteFile = db.prepare(\n \"DELETE FROM memory_files WHERE project_id = ? AND path = ?\",\n );\n\n db.transaction(() => {\n for (const stalePath of stalePaths) {\n const chunkIds = db\n .prepare(\"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\")\n .all(projectId, stalePath) as Array<{ id: string }>;\n for (const { id } of chunkIds) {\n deleteChunksFts.run(id);\n }\n deleteChunks.run(projectId, stalePath);\n deleteFile.run(projectId, stalePath);\n }\n })();\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Global indexing (all registered projects)\n// ---------------------------------------------------------------------------\n\n/**\n * Index all active projects registered in the registry DB.\n *\n * Async: yields to the event loop between each project so that the daemon's\n * Unix socket server can process IPC requests (e.g. status) while indexing.\n */\nexport async function indexAll(\n db: Database,\n registryDb: Database,\n): Promise<{ projects: number; result: IndexResult }> {\n const projects = registryDb\n .prepare(\"SELECT id, root_path, claude_notes_dir FROM projects WHERE status = 'active'\")\n .all() as Array<{ id: number; root_path: string; claude_notes_dir: string | null }>;\n\n const totals: IndexResult = {\n filesProcessed: 0,\n chunksCreated: 0,\n filesSkipped: 0,\n };\n\n for (const project of projects) {\n await yieldToEventLoop();\n const r = await indexProject(db, project.id, project.root_path, project.claude_notes_dir);\n totals.filesProcessed += r.filesProcessed;\n totals.chunksCreated += r.chunksCreated;\n totals.filesSkipped += r.filesSkipped;\n }\n\n return { projects: projects.length, result: totals };\n}\n\n// ---------------------------------------------------------------------------\n// Embedding generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate and store embeddings for chunks that do not yet have one.\n *\n * Because better-sqlite3 is synchronous but the embedding pipeline is async,\n * we fetch all unembedded chunk texts first, generate embeddings in batches,\n * and then write them back in a transaction.\n *\n * @param db Open federation database.\n * @param projectId Optional — restrict to a specific project.\n * @param batchSize Number of chunks to embed per round. Default 50.\n * @param onProgress Optional callback called after each batch with running totals.\n */\nexport async function embedChunks(\n db: Database,\n projectId?: number,\n batchSize = 50,\n onProgress?: (embedded: number, total: number) => void,\n): Promise<EmbedResult> {\n // Dynamic import — keeps the heavy ML runtime out of the module load path\n const { generateEmbedding, serializeEmbedding } = await import(\"../embeddings.js\");\n\n const conditions = [\"embedding IS NULL\"];\n const params: (string | number)[] = [];\n\n if (projectId !== undefined) {\n conditions.push(\"project_id = ?\");\n params.push(projectId);\n }\n\n const where = \"WHERE \" + conditions.join(\" AND \");\n\n const rows = db\n .prepare(`SELECT id, text FROM memory_chunks ${where} ORDER BY id`)\n .all(...params) as Array<{ id: string; text: string }>;\n\n if (rows.length === 0) {\n return { chunksEmbedded: 0, chunksSkipped: 0 };\n }\n\n const updateStmt = db.prepare(\n \"UPDATE memory_chunks SET embedding = ? WHERE id = ?\",\n );\n\n let embedded = 0;\n const total = rows.length;\n\n for (let i = 0; i < rows.length; i += batchSize) {\n const batch = rows.slice(i, i + batchSize);\n\n // Generate embeddings for the batch (async — must happen OUTSIDE transaction)\n const embeddings: Array<{ id: string; blob: Buffer }> = [];\n for (const row of batch) {\n const vec = await generateEmbedding(row.text);\n const blob = serializeEmbedding(vec);\n embeddings.push({ id: row.id, blob });\n }\n\n // Write the batch in a single transaction\n db.transaction(() => {\n for (const { id, blob } of embeddings) {\n updateStmt.run(blob, id);\n }\n })();\n\n embedded += embeddings.length;\n onProgress?.(embedded, total);\n }\n\n return { chunksEmbedded: embedded, chunksSkipped: 0 };\n}\n"],"mappings":"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"}