@tekmidian/pai 0.63.0 → 0.65.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 (206) hide show
  1. package/ARCHITECTURE.md +3 -0
  2. package/README.md +38 -5
  3. package/dist/{aibroker-client-BYG-GtAg.mjs → aibroker-client-B8c42Lh8.mjs} +1 -1
  4. package/dist/{aibroker-client-BK1h--W_.mjs → aibroker-client-C5Fw7DNz.mjs} +10 -4
  5. package/dist/{aibroker-client-BK1h--W_.mjs.map → aibroker-client-C5Fw7DNz.mjs.map} +1 -1
  6. package/dist/{async-DNVWtQl2.mjs → async-CNn36gm4.mjs} +5 -4
  7. package/dist/async-CNn36gm4.mjs.map +1 -0
  8. package/dist/{auto-route-CF8WNaoy.mjs → auto-route-sLMU-NnM.mjs} +2 -2
  9. package/dist/{auto-route-CF8WNaoy.mjs.map → auto-route-sLMU-NnM.mjs.map} +1 -1
  10. package/dist/{chain-CUfE6WYK.mjs → chain-C8QO8gwj.mjs} +4 -4
  11. package/dist/{chain-CUfE6WYK.mjs.map → chain-C8QO8gwj.mjs.map} +1 -1
  12. package/dist/chunker-BH4i-F2b.mjs +300 -0
  13. package/dist/chunker-BH4i-F2b.mjs.map +1 -0
  14. package/dist/cli/index.mjs +20 -20
  15. package/dist/cli/program.d.mts.map +1 -1
  16. package/dist/cli/program.mjs +20 -20
  17. package/dist/{clusters-Ci1mseh2.mjs → clusters-PgIUYT_v.mjs} +2 -2
  18. package/dist/{clusters-Ci1mseh2.mjs.map → clusters-PgIUYT_v.mjs.map} +1 -1
  19. package/dist/{config-CQCmH_gV.mjs → config-BbLFD7Uf.mjs} +3 -3
  20. package/dist/{config-CQCmH_gV.mjs.map → config-BbLFD7Uf.mjs.map} +1 -1
  21. package/dist/{config-DjXq_W42.mjs → config-YinjgXEJ.mjs} +1 -1
  22. package/dist/{context-handover-cache-DFNnlWv2.mjs → context-handover-cache-mGxq2-8f.mjs} +11 -29
  23. package/dist/context-handover-cache-mGxq2-8f.mjs.map +1 -0
  24. package/dist/daemon/index.mjs +16 -16
  25. package/dist/daemon-CX9JomIJ.mjs +19 -0
  26. package/dist/{daemon-Cex3dT9f.mjs → daemon-D2r1AQqE.mjs} +31 -30
  27. package/dist/{daemon-Cex3dT9f.mjs.map → daemon-D2r1AQqE.mjs.map} +1 -1
  28. package/dist/daemon-mcp/index.mjs +29 -20
  29. package/dist/daemon-mcp/index.mjs.map +1 -1
  30. package/dist/detector-C3Q7mxQU.mjs +3 -0
  31. package/dist/{detector-3g7XuU1r.mjs → detector-CIHEXKsV.mjs} +2 -2
  32. package/dist/{detector-3g7XuU1r.mjs.map → detector-CIHEXKsV.mjs.map} +1 -1
  33. package/dist/{embeddings-DfrYNIng.mjs → embeddings-BbNVXa_0.mjs} +1 -1
  34. package/dist/{embeddings-DfrYNIng.mjs.map → embeddings-BbNVXa_0.mjs.map} +1 -1
  35. package/dist/{embeddings-BNv3TQC5.mjs → embeddings-CDfM63uC.mjs} +1 -1
  36. package/dist/{factory-UO9_DFMN.mjs → factory-CaswxuJ0.mjs} +1 -1
  37. package/dist/{factory-BozZa81q.mjs → factory-y36qGegI.mjs} +13 -13
  38. package/dist/{factory-BozZa81q.mjs.map → factory-y36qGegI.mjs.map} +1 -1
  39. package/dist/{fallback-ktbR0aR7.mjs → fallback-CWDQYmJi.mjs} +1205 -1204
  40. package/dist/fallback-CWDQYmJi.mjs.map +1 -0
  41. package/dist/{federation-db-w8e-sKjp.mjs → federation-db-BTyoufBh.mjs} +2 -2
  42. package/dist/{federation-db-w8e-sKjp.mjs.map → federation-db-BTyoufBh.mjs.map} +1 -1
  43. package/dist/federation-db-HfIFc7FG.mjs +3 -0
  44. package/dist/{helpers-Dj-B314s.mjs → helpers-BRCJg0G3.mjs} +2 -208
  45. package/dist/helpers-BRCJg0G3.mjs.map +1 -0
  46. package/dist/hooks/block-sleep-poll.mjs +36 -11
  47. package/dist/hooks/block-sleep-poll.mjs.map +2 -2
  48. package/dist/hooks/capture-all-events.mjs +3 -17
  49. package/dist/hooks/capture-all-events.mjs.map +3 -3
  50. package/dist/hooks/capture-session-summary.mjs +3 -17
  51. package/dist/hooks/capture-session-summary.mjs.map +3 -3
  52. package/dist/hooks/capture-tool-output.mjs +3 -17
  53. package/dist/hooks/capture-tool-output.mjs.map +3 -3
  54. package/dist/hooks/cleanup-session-files.mjs +3 -17
  55. package/dist/hooks/cleanup-session-files.mjs.map +2 -2
  56. package/dist/hooks/context-compression-hook.mjs +3 -17
  57. package/dist/hooks/context-compression-hook.mjs.map +3 -3
  58. package/dist/hooks/initialize-session.mjs +3 -17
  59. package/dist/hooks/initialize-session.mjs.map +2 -2
  60. package/dist/hooks/inject-observations.mjs +3 -17
  61. package/dist/hooks/inject-observations.mjs.map +2 -2
  62. package/dist/hooks/load-core-context.mjs +4 -22
  63. package/dist/hooks/load-core-context.mjs.map +2 -2
  64. package/dist/hooks/load-project-context.mjs +3 -17
  65. package/dist/hooks/load-project-context.mjs.map +3 -3
  66. package/dist/hooks/mcp-deferred-gate.mjs +3 -17
  67. package/dist/hooks/mcp-deferred-gate.mjs.map +2 -2
  68. package/dist/hooks/observe.mjs +3 -17
  69. package/dist/hooks/observe.mjs.map +2 -2
  70. package/dist/hooks/security-validator.mjs +3 -17
  71. package/dist/hooks/security-validator.mjs.map +3 -3
  72. package/dist/hooks/stop-hook.mjs +3 -17
  73. package/dist/hooks/stop-hook.mjs.map +3 -3
  74. package/dist/hooks/sync-todo-to-md.mjs +3 -17
  75. package/dist/hooks/sync-todo-to-md.mjs.map +2 -2
  76. package/dist/hooks/worker-guard.mjs.map +2 -2
  77. package/dist/hooks/worker-status-line.mjs +4 -4
  78. package/dist/hooks/worker-status-line.mjs.map +2 -2
  79. package/dist/index.d.mts +26 -8
  80. package/dist/index.d.mts.map +1 -1
  81. package/dist/index.mjs +5 -4
  82. package/dist/indexer-backend-Cnc7Tf5C.mjs +5 -0
  83. package/dist/{ipc-client-Dasm515c.mjs → ipc-client-D16Xw6Uo.mjs} +2 -2
  84. package/dist/{ipc-client-Dasm515c.mjs.map → ipc-client-D16Xw6Uo.mjs.map} +1 -1
  85. package/dist/{kg-entity-Cj8Q6YmU.mjs → kg-entity-COTUj1ZC.mjs} +1 -1
  86. package/dist/{kg-entity-Cj8Q6YmU.mjs.map → kg-entity-COTUj1ZC.mjs.map} +1 -1
  87. package/dist/{latent-ideas-BRy6HNwg.mjs → latent-ideas-ByvMjVcb.mjs} +4 -4
  88. package/dist/{latent-ideas-BRy6HNwg.mjs.map → latent-ideas-ByvMjVcb.mjs.map} +1 -1
  89. package/dist/{link-boost-BptbAPuv.mjs → link-boost-UiE-uooT.mjs} +1 -1
  90. package/dist/{link-boost-BptbAPuv.mjs.map → link-boost-UiE-uooT.mjs.map} +1 -1
  91. package/dist/{main-resolver-BIft_FVF.mjs → main-resolver-CDe7DCso.mjs} +6 -6
  92. package/dist/{main-resolver-BIft_FVF.mjs.map → main-resolver-CDe7DCso.mjs.map} +1 -1
  93. package/dist/main-resolver-DI-A7lSO.mjs +7 -0
  94. package/dist/merge-2gqRPFu2.mjs +3 -0
  95. package/dist/{merge-xoyfZQ4W.mjs → merge-DgU9OgZy.mjs} +1 -1
  96. package/dist/{merge-xoyfZQ4W.mjs.map → merge-DgU9OgZy.mjs.map} +1 -1
  97. package/dist/module-paths-DdRzbkUI.mjs +44 -0
  98. package/dist/module-paths-DdRzbkUI.mjs.map +1 -0
  99. package/dist/{neighborhood-D8uscnGy.mjs → neighborhood-CvRHlqdR.mjs} +2 -2
  100. package/dist/{neighborhood-D8uscnGy.mjs.map → neighborhood-CvRHlqdR.mjs.map} +1 -1
  101. package/dist/{note-context-CgffaUMh.mjs → note-context-BrbfUIoP.mjs} +1 -1
  102. package/dist/{note-context-CgffaUMh.mjs.map → note-context-BrbfUIoP.mjs.map} +1 -1
  103. package/dist/{pai-home-CeOIYltV.mjs → pai-home-Cm9rcJgX.mjs} +1 -1
  104. package/dist/{pai-home-CeOIYltV.mjs.map → pai-home-Cm9rcJgX.mjs.map} +1 -1
  105. package/dist/{planner-DAQMTMjP.mjs → planner-8-shIa8t.mjs} +7 -7
  106. package/dist/{planner-DAQMTMjP.mjs.map → planner-8-shIa8t.mjs.map} +1 -1
  107. package/dist/{postgres-CxD6_Dca.mjs → postgres-DYtZg7J7.mjs} +8 -15
  108. package/dist/{postgres-CxD6_Dca.mjs.map → postgres-DYtZg7J7.mjs.map} +1 -1
  109. package/dist/{program-De1GVVM4.mjs → program-DiktbiWa.mjs} +229 -249
  110. package/dist/program-DiktbiWa.mjs.map +1 -0
  111. package/dist/{query-feedback-xz5axTj3.mjs → query-feedback-BIaZTTFO.mjs} +2 -2
  112. package/dist/{query-feedback-xz5axTj3.mjs.map → query-feedback-BIaZTTFO.mjs.map} +1 -1
  113. package/dist/query-feedback-B_iigYj-.mjs +3 -0
  114. package/dist/{registry-db-Bkpbp7Gf.mjs → registry-db-C7voqML9.mjs} +2 -2
  115. package/dist/{registry-db-Bkpbp7Gf.mjs.map → registry-db-C7voqML9.mjs.map} +1 -1
  116. package/dist/registry-db-JHPhA8vF.mjs +3 -0
  117. package/dist/{registry-postgres-B07-hmn8.mjs → registry-postgres-DSrkxqfF.mjs} +2 -2
  118. package/dist/{registry-postgres-B07-hmn8.mjs.map → registry-postgres-DSrkxqfF.mjs.map} +1 -1
  119. package/dist/{registry-sqlite-CkGND_tm.mjs → registry-sqlite-B2436JgX.mjs} +2 -2
  120. package/dist/{registry-sqlite-CkGND_tm.mjs.map → registry-sqlite-B2436JgX.mjs.map} +1 -1
  121. package/dist/{reranker-BS5gqGeF.mjs → reranker-CFiEzHQu.mjs} +1 -1
  122. package/dist/{reranker-CRVawoEh.mjs → reranker-DKv80KO5.mjs} +1 -1
  123. package/dist/{reranker-CRVawoEh.mjs.map → reranker-DKv80KO5.mjs.map} +1 -1
  124. package/dist/router-S6C5BxzZ.mjs +3 -0
  125. package/dist/{router-DjFZIoDl.mjs → router-aqLMvNMg.mjs} +1 -1
  126. package/dist/{router-DjFZIoDl.mjs.map → router-aqLMvNMg.mjs.map} +1 -1
  127. package/dist/{run-CZVCCvXE.mjs → run-DUZRSnIT.mjs} +120 -42
  128. package/dist/run-DUZRSnIT.mjs.map +1 -0
  129. package/dist/{run-env-BE5xyCwy.mjs → run-env-BHWnXqle.mjs} +3 -3
  130. package/dist/{run-env-BE5xyCwy.mjs.map → run-env-BHWnXqle.mjs.map} +1 -1
  131. package/dist/{runtime-paths-BRwDeBEg.mjs → runtime-paths-CHTg3ywb.mjs} +1 -1
  132. package/dist/{runtime-paths-BRwDeBEg.mjs.map → runtime-paths-CHTg3ywb.mjs.map} +1 -1
  133. package/dist/{search-BaTbyWjK.mjs → search-BOSphCJ1.mjs} +2 -2
  134. package/dist/{search-BaTbyWjK.mjs.map → search-BOSphCJ1.mjs.map} +1 -1
  135. package/dist/search-Bf3Kub3F.mjs +3 -0
  136. package/dist/{server-BY6AedmC.mjs → server-DlL1QI2k.mjs} +4 -5
  137. package/dist/server-DlL1QI2k.mjs.map +1 -0
  138. package/dist/{session-keepalive-CWGzLBVU.mjs → session-keepalive-CZUwp5IQ.mjs} +7 -7
  139. package/dist/{session-keepalive-CWGzLBVU.mjs.map → session-keepalive-CZUwp5IQ.mjs.map} +1 -1
  140. package/dist/skills/Art/SKILL.md +1 -1
  141. package/dist/skills/Observability/SKILL.md +4 -4
  142. package/dist/skills/Research/SKILL.md +1 -1
  143. package/dist/{sources-BBe3AZ-W.mjs → sources-DX4ElmrE.mjs} +1 -1
  144. package/dist/{sources-BBe3AZ-W.mjs.map → sources-DX4ElmrE.mjs.map} +1 -1
  145. package/dist/{sqlite-CfKfuggn.mjs → sqlite-BrEu3avy.mjs} +8 -7
  146. package/dist/sqlite-BrEu3avy.mjs.map +1 -0
  147. package/dist/{state-CwlGAKgF.mjs → state-DW8zdweW.mjs} +1 -1
  148. package/dist/{state-n13uPK5B.mjs → state-HyjqTihC.mjs} +1 -1
  149. package/dist/{state-n13uPK5B.mjs.map → state-HyjqTihC.mjs.map} +1 -1
  150. package/dist/{stop-words-ClCwO8mZ.mjs → stop-words-BdQuaE9K.mjs} +1 -1
  151. package/dist/{stop-words-ClCwO8mZ.mjs.map → stop-words-BdQuaE9K.mjs.map} +1 -1
  152. package/dist/{themes-B2v7RNnA.mjs → themes-BgqahYVM.mjs} +3 -3
  153. package/dist/{themes-B2v7RNnA.mjs.map → themes-BgqahYVM.mjs.map} +1 -1
  154. package/dist/tools-BbqQIHPe.mjs +6 -0
  155. package/dist/{tools-DFdPbX-X.mjs → tools-gMGDIpJ9.mjs} +84 -68
  156. package/dist/tools-gMGDIpJ9.mjs.map +1 -0
  157. package/dist/{trace-BRflVyBi.mjs → trace-CmAB7iJZ.mjs} +1 -1
  158. package/dist/{trace-BRflVyBi.mjs.map → trace-CmAB7iJZ.mjs.map} +1 -1
  159. package/dist/{vault-indexer-mCFus_AM.mjs → vault-indexer-Ddq51X20.mjs} +4 -3
  160. package/dist/vault-indexer-Ddq51X20.mjs.map +1 -0
  161. package/dist/{wakeup-DO1O4udw.mjs → wakeup-CZw88uXf.mjs} +3 -3
  162. package/dist/{wakeup-DO1O4udw.mjs.map → wakeup-CZw88uXf.mjs.map} +1 -1
  163. package/dist/{work-queue-worker-BbrC3I_R.mjs → work-queue-worker-DCzfH0d1.mjs} +4 -4
  164. package/dist/{work-queue-worker-BbrC3I_R.mjs.map → work-queue-worker-DCzfH0d1.mjs.map} +1 -1
  165. package/dist/work-queue-worker-Taba_vcA.mjs +11 -0
  166. package/dist/{zettelkasten-BPxSOalU.mjs → zettelkasten-Dwj67e4p.mjs} +4 -4
  167. package/dist/{zettelkasten-BPxSOalU.mjs.map → zettelkasten-Dwj67e4p.mjs.map} +1 -1
  168. package/docker/docker-compose.yml +39 -0
  169. package/docs/commands/README.md +3 -3
  170. package/docs/commands/config.md +3 -3
  171. package/docs/commands/setup.md +9 -2
  172. package/docs/commands/worker.md +2 -2
  173. package/gemini-extension.json +1 -1
  174. package/package.json +3 -2
  175. package/pai-plugin.json +1 -0
  176. package/scripts/build-hooks.mjs +6 -6
  177. package/src/hooks/ts/lib/pai-paths-case.test.ts +12 -0
  178. package/src/hooks/ts/lib/pai-paths-import.test.ts +25 -0
  179. package/src/hooks/ts/lib/pai-paths.ts +5 -28
  180. package/src/hooks/ts/lib/sleep-poll-gate.test.ts +17 -1
  181. package/src/hooks/ts/lib/sleep-poll-gate.ts +21 -9
  182. package/src/hooks/ts/session-start/load-core-context.ts +2 -6
  183. package/src/hooks/ts/session-start/session-start-worker-guard.test.ts +24 -1
  184. package/dist/async-DNVWtQl2.mjs.map +0 -1
  185. package/dist/context-handover-cache-DFNnlWv2.mjs.map +0 -1
  186. package/dist/daemon-C8-uzOuV.mjs +0 -19
  187. package/dist/detector-DgDn56j-.mjs +0 -3
  188. package/dist/fallback-ktbR0aR7.mjs.map +0 -1
  189. package/dist/federation-db-CJDw_Pwq.mjs +0 -3
  190. package/dist/helpers-Dj-B314s.mjs.map +0 -1
  191. package/dist/indexer-backend-BN9rpp9O.mjs +0 -5
  192. package/dist/main-resolver-C9-poMdi.mjs +0 -7
  193. package/dist/merge-BncjJTfb.mjs +0 -3
  194. package/dist/program-De1GVVM4.mjs.map +0 -1
  195. package/dist/query-feedback-COYCnvHQ.mjs +0 -3
  196. package/dist/registry-db-DwXpf7yF.mjs +0 -3
  197. package/dist/router-C5hPk9YP.mjs +0 -3
  198. package/dist/run-CZVCCvXE.mjs.map +0 -1
  199. package/dist/search-BQHL5wDr.mjs +0 -3
  200. package/dist/server-BY6AedmC.mjs.map +0 -1
  201. package/dist/sqlite-CfKfuggn.mjs.map +0 -1
  202. package/dist/tools-B52IvaU6.mjs +0 -6
  203. package/dist/tools-DFdPbX-X.mjs.map +0 -1
  204. package/dist/vault-indexer-mCFus_AM.mjs.map +0 -1
  205. package/dist/work-queue-worker-z-AATZ2t.mjs +0 -11
  206. /package/dist/{indexer-backend-Cc-_NK3q.mjs → indexer-backend-isSLg6yE.mjs} +0 -0
package/dist/index.d.mts CHANGED
@@ -433,18 +433,13 @@ declare function readPaiMarker(projectRoot: string): {
433
433
  declare function discoverPaiMarkers(searchDirs: string[]): PaiMarker[];
434
434
  //#endregion
435
435
  //#region src/memory/chunker.d.ts
436
- /**
437
- * Markdown text chunker for the PAI memory engine.
438
- *
439
- * Splits markdown files into overlapping text segments suitable for BM25
440
- * full-text indexing. Respects heading boundaries where possible, falling
441
- * back to paragraph and sentence splitting when sections are large.
442
- */
443
436
  interface Chunk {
444
437
  text: string;
445
438
  startLine: number;
446
439
  endLine: number;
447
440
  hash: string;
441
+ /** Ancestor heading titles, outermost first, including the chunk's own section heading. */
442
+ headingPath: string[];
448
443
  }
449
444
  interface ChunkOptions {
450
445
  /** Approximate maximum tokens per chunk. Default 400. */
@@ -457,6 +452,29 @@ interface ChunkOptions {
457
452
  * Matches the OpenClaw estimate approach.
458
453
  */
459
454
  declare function estimateTokens(text: string): number;
455
+ interface Heading {
456
+ level: number;
457
+ title: string;
458
+ line: number;
459
+ }
460
+ /**
461
+ * Parse ATX headings (levels 1-6) from lines. Lines inside ``` or ~~~ fences
462
+ * are never headings.
463
+ */
464
+ declare function parseHeadings(lines: string[]): Heading[];
465
+ interface OutlineNode {
466
+ level: number;
467
+ title: string;
468
+ startLine: number;
469
+ endLine: number;
470
+ tokens: number;
471
+ children: OutlineNode[];
472
+ }
473
+ /**
474
+ * Heading tree of a markdown file. A section ends on the line before the next
475
+ * heading of the same or higher level (or at EOF) and includes its children.
476
+ */
477
+ declare function buildOutline(content: string): OutlineNode[];
460
478
  declare function chunkMarkdown(content: string, opts?: ChunkOptions): Chunk[];
461
479
  //#endregion
462
480
  //#region src/memory/indexer/helpers.d.ts
@@ -548,5 +566,5 @@ interface RerankOptions {
548
566
  */
549
567
  declare function rerankResults(query: string, results: SearchResult[], opts?: RerankOptions): Promise<SearchResult[]>;
550
568
  //#endregion
551
- export { Chunk, ChunkOptions, IndexResult, MigrationResult, PaiMarker, RerankOptions, SearchOptions, SearchResult, buildFtsQuery, chunkMarkdown, configureRerankerModel, decodeEncodedDir, detectTier, discoverPaiMarkers, ensurePaiMarker, estimateTokens, migrateFromJson, parseSessionFilename, populateSlugs, readPaiMarker, rerankResults, slugify };
569
+ export { Chunk, ChunkOptions, Heading, IndexResult, MigrationResult, OutlineNode, PaiMarker, RerankOptions, SearchOptions, SearchResult, buildFtsQuery, buildOutline, chunkMarkdown, configureRerankerModel, decodeEncodedDir, detectTier, discoverPaiMarkers, ensurePaiMarker, estimateTokens, migrateFromJson, parseHeadings, parseSessionFilename, populateSlugs, readPaiMarker, rerankResults, slugify };
552
570
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/registry/merge.ts","../src/storage/registry-interface.ts","../src/registry/migrate.ts","../src/registry/pai-marker.ts","../src/memory/chunker.ts","../src/memory/indexer/helpers.ts","../src/memory/indexer/types.ts","../src/memory/search.ts","../src/memory/reranker.ts"],"mappings":";;AAcA;;;;;;;;;;;;UAAiB,SAAA;EACf,MAAA;EACA,QAAA;EACA,MAAA;EACA,QAAA;EAQU;EANV,QAAA;IAAY,EAAA;IAAY,IAAA;IAAc,EAAA;EAAA;EACtC,IAAA;EACA,OAAA;EACA,WAAA;EACA,KAAA;ECAU;EDEV,UAAA;AAAA;;;KCHU,WAAA;AAAA,KACA,aAAA;AAAA,KACA,aAAA;AAAA,KACA,QAAA;AAAA,KACA,iBAAA;AAAA,UAEK,OAAA;EACf,EAAA;EACA,IAAA;EACA,YAAA;EACA,SAAA;EACA,WAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA,EAAQ,aAAA;EACR,SAAA;EACA,aAAA;EACA,gBAAA;EACA,cAAA;EACA,UAAA;EACA,UAAA;EACA,WAAA;AAAA;AAAA,UAGe,uBAAA,SAAgC,OAAA;EAC/C,aAAA;EACA,WAAA;AAAA;AAAA,UAGe,OAAA;EACf,EAAA;EACA,UAAA;EACA,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA,EAAQ,aAAA;EACR,iBAAA;EACA,WAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,OAAA;EAC1C,YAAA;EACA,YAAA;AAAA;AAAA,UAOe,UAAA;EACf,IAAA;EACA,WAAA;EACA,QAAA;EACA,UAAA;EACA,IAAA,GAAO,WAAA;EACP,MAAA,GAAS,aAAA;EACT,SAAA;EACA,SAAA;AAAA;;UAIe,uBAAA;EACf,QAAA;EACA,WAAA;EACA,QAAA;EACA,UAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,SAAA;EACA,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA,GAAS,aAAA;EACT,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,SAAA;EACA,SAAA;EACA,OAAA,EAAS,iBAAA;EACT,YAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,mBAAA;EACf,MAAA,GAAS,aAAA;EAnDM;EAqDf,eAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;AAAA;AAAA,UAGe,+BAAA;EACf,eAAA;AAAA;AAAA,UAGe,4BAAA;EACf,MAAA,GAAS,aAAA;EACT,KAAA;EACA,OAAA;AAAA;AAAA,UAGe,wBAAA;EAzDf;EA2DA,cAAA;AAAA;AAAA,UAGe,mBAAA;EACf,SAAA;EACA,MAAA,GAAS,aAAA;EACT,KAAA;AAAA;AAAA,UAGe,6BAAA;EACf,OAAA;EACA,KAAA;AAAA;AAAA,UAOe,eAAA;EAAA,SACN,WAAA;EAET,KAAA,IAAS,OAAA;EArET;;;;;EA4EA,aAAA,IAAiB,OAAA;EAMjB,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,OAAA;EA3ErB;EA6Ef,gBAAA,CACE,IAAA,UACA,IAAA;IAAS,eAAA;IAA2B,SAAA;EAAA,IACnC,OAAA,CAAQ,OAAA;EACX,iBAAA,CACE,KAAA,UACA,IAAA;IAAS,eAAA;EAAA,IACR,OAAA,CAAQ,OAAA;EACX,oBAAA,CAAqB,QAAA,UAAkB,IAAA;IAAS,SAAA;EAAA,IAAuB,OAAA,CAAQ,OAAA;EAC/E,sBAAA,CAAuB,UAAA,UAAoB,IAAA;IAAS,SAAA;EAAA,IAAuB,OAAA,CAAQ,OAAA;EA9E1E;EAgFT,sBAAA,CAAuB,GAAA,WAAc,OAAA,CAAQ,OAAA;EAC7C,YAAA,CAAa,IAAA,GAAO,mBAAA,GAAsB,OAAA,CAAQ,OAAA;;EAElD,4BAAA,CAA6B,IAAA,GAAO,+BAAA,GAAkC,OAAA,CAAQ,OAAA;EAC9E,4BAAA,CAA6B,IAAA,GAAO,4BAAA,GAA+B,OAAA,CAAQ,uBAAA;EA/E3E;EAiFA,iBAAA,CAAkB,IAAA,GAAO,wBAAA,GAA2B,OAAA,CAClD,KAAA,CAAM,uBAAA;IAA4B,IAAA;EAAA;EAEpC,cAAA,CAAe,KAAA,UAAe,KAAA,YAAiB,OAAA,CAAQ,OAAA;EACvD,aAAA,CAAc,IAAA;IAAS,MAAA,GAAS,aAAA;EAAA,IAAkB,OAAA;EAClD,6BAAA,IAAiC,OAAA;EACjC,kBAAA,CAAmB,QAAA,WAAmB,OAAA;;EAEtC,gCAAA,CACE,gBAAA,UACA,IAAA,UACA,KAAA,YACC,OAAA,CAAQ,KAAA;IAAQ,IAAA;IAAc,SAAA;IAAmB,aAAA;EAAA;EAMpD,aAAA,CAAc,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,OAAA;EAC1C,0BAAA,CAA2B,KAAA,EAAO,uBAAA,GAA0B,OAAA;IAAU,EAAA;IAAY,IAAA;IAAc,OAAA;EAAA;EAChG,iBAAA,CAAkB,EAAA,UAAY,KAAA;IAAS,QAAA;IAAmB,UAAA;EAAA,GAAuB,SAAA,YAAqB,OAAA;EACtG,mBAAA,CACE,EAAA,UACA,MAAA,EAAQ,aAAA,EACR,IAAA;IAAS,UAAA;IAA4B,SAAA;IAAoB,oBAAA,GAAuB,aAAA;EAAA,IAC/E,OAAA;EACH,wBAAA,CAAyB,EAAA,UAAY,WAAA,UAAqB,SAAA,YAAqB,OAAA;EAC/E,iBAAA,CAAkB,EAAA,UAAY,IAAA,EAAM,WAAA,EAAa,SAAA,YAAqB,OAAA;EACtE,0BAAA,CAA2B,EAAA,UAAY,MAAA,iBAAuB,SAAA,YAAqB,OAAA;EACnF,2BAAA,CAA4B,EAAA,UAAY,GAAA,iBAAoB,SAAA,YAAqB,OAAA;EACjF,yBAAA,CAA0B,EAAA,UAAY,IAAA,iBAAqB,SAAA,YAAqB,OAAA;EAChF,iBAAA,CAAkB,EAAA,UAAY,IAAA,UAAc,SAAA,WAAoB,OAAA;;EAEhE,qBAAA,CAAsB,YAAA,UAAsB,UAAA,WAAqB,OAAA;EACjE,aAAA,CAAc,EAAA,WAAa,OAAA;EApFZ;EAsFf,oBAAA,CAAqB,EAAA,WAAa,OAAA;EAMlC,kBAAA,CAAmB,SAAA,WAAoB,OAAA;EACvC,WAAA,IAAe,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,IAAA;EAAA;EA1F3C;EA4FA,SAAA,CAAU,IAAA,WAAe,OAAA;EACzB,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,gBAAA,CAAiB,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACpD,kBAAA,CAAmB,aAAA,UAAuB,WAAA,UAAqB,KAAA,WAAgB,OAAA;EAC/E,iBAAA,CAAkB,SAAA,WAAoB,OAAA;EACtC,0BAAA,CAA2B,SAAA,WAAoB,OAAA;EAtFhC;EAwFf,eAAA,CAAgB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EAC7D,2BAAA,CAA4B,SAAA,WAAoB,OAAA;EAtFvC;EA6FT,YAAA,CAAa,KAAA,WAAgB,OAAA;EAC7B,qBAAA,CAAsB,SAAA,WAAoB,OAAA;EAC1C,QAAA,CAAS,KAAA,UAAe,SAAA,WAAoB,OAAA;EAC5C,WAAA,CAAY,KAAA,WAAgB,OAAA;EAC5B,sBAAA,CAAuB,SAAA,WAAoB,OAAA;EA3EhC;EA6EX,kBAAA,CAAmB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EA5Ee;EA8E/E,aAAA,CAAc,KAAA,UAAe,WAAA,WAAsB,OAAA;EA7EgC;EA+EnF,YAAA,IAAgB,OAAA,CAAQ,KAAA;IAAQ,KAAA;IAAe,IAAA;IAAc,SAAA;EAAA;EAM7D,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,OAAA;EACpC,kBAAA,CAAmB,SAAA,UAAmB,MAAA,WAAiB,OAAA,CAAQ,OAAA;EAC/D,0BAAA,CAA2B,SAAA,WAAoB,OAAA,CAAQ,OAAA;EACvD,mBAAA,CAAoB,SAAA,WAAoB,OAAA;EACxC,sBAAA,CAAuB,SAAA,UAAmB,IAAA,GAAO,6BAAA,GAAgC,OAAA,CAAQ,OAAA;EAnFd;EAqF3E,YAAA,CAAa,IAAA,GAAO,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAClD,qBAAA,CAAsB,SAAA,UAAmB,QAAA,WAAmB,OAAA;IAAU,EAAA;EAAA;EACtE,kBAAA,CAAmB,SAAA,UAAmB,MAAA,WAAiB,OAAA;EACvD,aAAA,CAAc,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,OAAA;EAnFK;EAqF/C,qBAAA,CAAsB,KAAA,EAAO,UAAA,GAAa,OAAA;EAC1C,iBAAA,CAAkB,EAAA,UAAY,KAAA;IAAS,IAAA;IAAe,KAAA;IAAgB,QAAA;EAAA,IAAsB,OAAA;EAC5F,mBAAA,CAAoB,EAAA,UAAY,MAAA,UAAgB,IAAA;IAAS,QAAA;EAAA,IAAsB,OAAA;EAC/E,qBAAA,CAAsB,EAAA,UAAY,QAAA,WAAmB,OAAA;EAxEO;EA0E5D,mBAAA,CAAoB,EAAA,UAAY,MAAA,EAAQ,aAAA,EAAe,IAAA;IAAS,QAAA;EAAA,IAAsB,OAAA;EACtF,oBAAA,CAAqB,EAAA,UAAY,SAAA,UAAmB,IAAA;IAAS,MAAA;EAAA,IAAoB,OAAA;EACjF,aAAA,CAAc,EAAA,WAAa,OAAA;EAnEwD;EAqEnF,qBAAA,CAAsB,MAAA,UAAgB,MAAA,WAAiB,OAAA;EACvD,uBAAA,CAAwB,SAAA,WAAoB,OAAA;EAnEoB;EAqEhE,aAAA,IAAiB,OAAA;EACjB,wBAAA,CAAyB,SAAA,WAAoB,OAAA;EAC7C,6BAAA,IAAiC,OAAA;EAMjC,iBAAA,CAAkB,SAAA,WAAoB,OAAA;EACtC,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,gBAAA,CAAiB,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACpD,kBAAA,CAAmB,aAAA,UAAuB,WAAA,UAAqB,KAAA,WAAgB,OAAA;EAC/E,kBAAA,CAAmB,SAAA,WAAoB,OAAA;EAMvC,OAAA,CAAQ,KAAA;IAAS,SAAA;IAAmB,eAAA;IAAyB,QAAA,EAAU,QAAA;IAAU,SAAA;EAAA,IAAsB,OAAA;EACvG,mBAAA,CAAoB,SAAA,WAAoB,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,iBAAA;EAAA;EACpE,mBAAA,CAAoB,SAAA,WAAoB,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,UAAA;EAAA;EACpE,UAAA,CAAW,SAAA,UAAmB,eAAA,WAA0B,OAAA;EACxD,UAAA,CAAW,EAAA,WAAa,OAAA;EACxB,2BAAA,CAA4B,SAAA,WAAoB,OAAA;EA9Ce;EAgD/D,8BAAA,CAA+B,SAAA,WAAoB,OAAA;EACnD,YAAA,CAAa,EAAA,UAAY,eAAA,WAA0B,OAAA;EACnD,iBAAA,CAAkB,EAAA,UAAY,SAAA,WAAoB,OAAA;EAhDV;EAkDxC,mBAAA,CAAoB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACjE,oBAAA,CAAqB,SAAA,WAAoB,OAAA;EAlDwC;EAoDjF,yBAAA,CAA0B,SAAA,WAAoB,OAAA;EAM9C,mBAAA,CAAoB,KAAA,EAAO,qBAAA,GAAwB,OAAA;EACnD,6BAAA,CAA8B,SAAA,WAAoB,OAAA;EAxDU;EA0D5D,2BAAA,CAA4B,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACzE,2BAAA,CAA4B,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACzE,8BAAA,CAA+B,SAAA,WAAoB,OAAA;EAMnD,gBAAA,CAAiB,QAAA,UAAkB,QAAA,WAAmB,OAAA,CAAQ,SAAA;EAC9D,iBAAA,CAAkB,IAAA,EAAM,SAAA,GAAY,OAAA;AAAA;;;;;;;AA3TtC;;;;;AACA;;;;;AAEA;;;;;;;;;;;;iBC6FgB,gBAAA,CACd,OAAA,UACA,SAAA,GAAY,GAAA;;;;;;iBAoCE,OAAA,CAAQ,KAAA;AAAA,UAgBd,aAAA;EACR,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;AAAA;;;;;;iBAcc,oBAAA,CACd,QAAA,WACC,aAAA;AAAA,UAiCc,eAAA;EACf,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,MAAA;AAAA;;;;;;;;;;;iBAaoB,eAAA,CACpB,OAAA,EAAS,eAAA,EACT,YAAA,YACC,OAAA,CAAQ,eAAA;;;UC5KM,SAAA;EHpDL;EGsDV,IAAA;;EAEA,IAAA;;EAEA,WAAA;AAAA;;;;AF5DF;;;;;AACA;;;iBE6MgB,eAAA,CACd,WAAA,UACA,IAAA,UACA,WAAA;;AF/MF;;;iBEqRgB,aAAA,CACd,WAAA;EACG,IAAA;EAAc,UAAA;EAAoB,MAAA;AAAA;;;AFpRvC;;;;;;;;iBEiTgB,kBAAA,CAAmB,UAAA,aAAuB,SAAA;;;;AHhU1D;;;;;;UIJiB,KAAA;EACf,IAAA;EACA,SAAA;EACA,OAAA;EACA,IAAA;AAAA;AAAA,UAGe,YAAA;EJKf;EIHA,SAAA;EJKA;EIHA,OAAA;AAAA;;;;;iBAUc,cAAA,CAAe,IAAA;AAAA,iBAiLf,aAAA,CAAc,OAAA,UAAiB,IAAA,GAAO,YAAA,GAAe,KAAA;;;;AJlMrE;;;;;;;;;;;;;;iBKagB,UAAA,CACd,YAAA;;;;ALdF;;UMViB,WAAA;EACf,cAAA;EACA,aAAA;EACA,YAAA;AAAA;;;UCOe,YAAA;EACf,SAAA;EACA,WAAA;EACA,IAAA;EACA,SAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;EACA,MAAA;EACA,SAAA;EACA,cAAA;EACA,OAAA;AAAA;AAAA,UAGe,aAAA;;EAEf,UAAA;ENRqB;EMUrB,OAAA;ENVqB;EMYrB,KAAA;ENXU;EMaV,UAAA;;EAEA,QAAA;AAAA;AAAA,iBA0Cc,aAAA,CAAc,KAAA;;;;;iBA8BR,aAAA,CACpB,OAAA,EAAS,YAAA,IACT,QAAA,EAAU,eAAA,GACT,OAAA,CAAQ,YAAA;;;;;;;iBC5EK,sBAAA,CAAuB,KAAA;AAAA,UAoCtB,aAAA;EPnDL;EOqDV,IAAA;;;;APpDF;;EO0DE,aAAA;AAAA;;APzDF;;;;;AACA;;;iBOoEsB,aAAA,CACpB,KAAA,UACA,OAAA,EAAS,YAAA,IACT,IAAA,GAAO,aAAA,GACN,OAAA,CAAQ,YAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/registry/merge.ts","../src/storage/registry-interface.ts","../src/registry/migrate.ts","../src/registry/pai-marker.ts","../src/memory/chunker.ts","../src/memory/indexer/helpers.ts","../src/memory/indexer/types.ts","../src/memory/search.ts","../src/memory/reranker.ts"],"mappings":";;AAcA;;;;;;;;;;;;UAAiB,SAAA;EACf,MAAA;EACA,QAAA;EACA,MAAA;EACA,QAAA;EAQU;EANV,QAAA;IAAY,EAAA;IAAY,IAAA;IAAc,EAAA;EAAA;EACtC,IAAA;EACA,OAAA;EACA,WAAA;EACA,KAAA;ECAU;EDEV,UAAA;AAAA;;;KCHU,WAAA;AAAA,KACA,aAAA;AAAA,KACA,aAAA;AAAA,KACA,QAAA;AAAA,KACA,iBAAA;AAAA,UAEK,OAAA;EACf,EAAA;EACA,IAAA;EACA,YAAA;EACA,SAAA;EACA,WAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA,EAAQ,aAAA;EACR,SAAA;EACA,aAAA;EACA,gBAAA;EACA,cAAA;EACA,UAAA;EACA,UAAA;EACA,WAAA;AAAA;AAAA,UAGe,uBAAA,SAAgC,OAAA;EAC/C,aAAA;EACA,WAAA;AAAA;AAAA,UAGe,OAAA;EACf,EAAA;EACA,UAAA;EACA,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA,EAAQ,aAAA;EACR,iBAAA;EACA,WAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,OAAA;EAC1C,YAAA;EACA,YAAA;AAAA;AAAA,UAOe,UAAA;EACf,IAAA;EACA,WAAA;EACA,QAAA;EACA,UAAA;EACA,IAAA,GAAO,WAAA;EACP,MAAA,GAAS,aAAA;EACT,SAAA;EACA,SAAA;AAAA;;UAIe,uBAAA;EACf,QAAA;EACA,WAAA;EACA,QAAA;EACA,UAAA;EACA,SAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,SAAA;EACA,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA,GAAS,aAAA;EACT,SAAA;AAAA;AAAA,UAGe,qBAAA;EACf,SAAA;EACA,SAAA;EACA,OAAA,EAAS,iBAAA;EACT,YAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,mBAAA;EACf,MAAA,GAAS,aAAA;EAnDM;EAqDf,eAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;AAAA;AAAA,UAGe,+BAAA;EACf,eAAA;AAAA;AAAA,UAGe,4BAAA;EACf,MAAA,GAAS,aAAA;EACT,KAAA;EACA,OAAA;AAAA;AAAA,UAGe,wBAAA;EAzDf;EA2DA,cAAA;AAAA;AAAA,UAGe,mBAAA;EACf,SAAA;EACA,MAAA,GAAS,aAAA;EACT,KAAA;AAAA;AAAA,UAGe,6BAAA;EACf,OAAA;EACA,KAAA;AAAA;AAAA,UAOe,eAAA;EAAA,SACN,WAAA;EAET,KAAA,IAAS,OAAA;EArET;;;;;EA4EA,aAAA,IAAiB,OAAA;EAMjB,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,OAAA;EA3ErB;EA6Ef,gBAAA,CACE,IAAA,UACA,IAAA;IAAS,eAAA;IAA2B,SAAA;EAAA,IACnC,OAAA,CAAQ,OAAA;EACX,iBAAA,CACE,KAAA,UACA,IAAA;IAAS,eAAA;EAAA,IACR,OAAA,CAAQ,OAAA;EACX,oBAAA,CAAqB,QAAA,UAAkB,IAAA;IAAS,SAAA;EAAA,IAAuB,OAAA,CAAQ,OAAA;EAC/E,sBAAA,CAAuB,UAAA,UAAoB,IAAA;IAAS,SAAA;EAAA,IAAuB,OAAA,CAAQ,OAAA;EA9E1E;EAgFT,sBAAA,CAAuB,GAAA,WAAc,OAAA,CAAQ,OAAA;EAC7C,YAAA,CAAa,IAAA,GAAO,mBAAA,GAAsB,OAAA,CAAQ,OAAA;;EAElD,4BAAA,CAA6B,IAAA,GAAO,+BAAA,GAAkC,OAAA,CAAQ,OAAA;EAC9E,4BAAA,CAA6B,IAAA,GAAO,4BAAA,GAA+B,OAAA,CAAQ,uBAAA;EA/E3E;EAiFA,iBAAA,CAAkB,IAAA,GAAO,wBAAA,GAA2B,OAAA,CAClD,KAAA,CAAM,uBAAA;IAA4B,IAAA;EAAA;EAEpC,cAAA,CAAe,KAAA,UAAe,KAAA,YAAiB,OAAA,CAAQ,OAAA;EACvD,aAAA,CAAc,IAAA;IAAS,MAAA,GAAS,aAAA;EAAA,IAAkB,OAAA;EAClD,6BAAA,IAAiC,OAAA;EACjC,kBAAA,CAAmB,QAAA,WAAmB,OAAA;;EAEtC,gCAAA,CACE,gBAAA,UACA,IAAA,UACA,KAAA,YACC,OAAA,CAAQ,KAAA;IAAQ,IAAA;IAAc,SAAA;IAAmB,aAAA;EAAA;EAMpD,aAAA,CAAc,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,OAAA;EAC1C,0BAAA,CAA2B,KAAA,EAAO,uBAAA,GAA0B,OAAA;IAAU,EAAA;IAAY,IAAA;IAAc,OAAA;EAAA;EAChG,iBAAA,CAAkB,EAAA,UAAY,KAAA;IAAS,QAAA;IAAmB,UAAA;EAAA,GAAuB,SAAA,YAAqB,OAAA;EACtG,mBAAA,CACE,EAAA,UACA,MAAA,EAAQ,aAAA,EACR,IAAA;IAAS,UAAA;IAA4B,SAAA;IAAoB,oBAAA,GAAuB,aAAA;EAAA,IAC/E,OAAA;EACH,wBAAA,CAAyB,EAAA,UAAY,WAAA,UAAqB,SAAA,YAAqB,OAAA;EAC/E,iBAAA,CAAkB,EAAA,UAAY,IAAA,EAAM,WAAA,EAAa,SAAA,YAAqB,OAAA;EACtE,0BAAA,CAA2B,EAAA,UAAY,MAAA,iBAAuB,SAAA,YAAqB,OAAA;EACnF,2BAAA,CAA4B,EAAA,UAAY,GAAA,iBAAoB,SAAA,YAAqB,OAAA;EACjF,yBAAA,CAA0B,EAAA,UAAY,IAAA,iBAAqB,SAAA,YAAqB,OAAA;EAChF,iBAAA,CAAkB,EAAA,UAAY,IAAA,UAAc,SAAA,WAAoB,OAAA;;EAEhE,qBAAA,CAAsB,YAAA,UAAsB,UAAA,WAAqB,OAAA;EACjE,aAAA,CAAc,EAAA,WAAa,OAAA;EApFZ;EAsFf,oBAAA,CAAqB,EAAA,WAAa,OAAA;EAMlC,kBAAA,CAAmB,SAAA,WAAoB,OAAA;EACvC,WAAA,IAAe,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,IAAA;EAAA;EA1F3C;EA4FA,SAAA,CAAU,IAAA,WAAe,OAAA;EACzB,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,gBAAA,CAAiB,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACpD,kBAAA,CAAmB,aAAA,UAAuB,WAAA,UAAqB,KAAA,WAAgB,OAAA;EAC/E,iBAAA,CAAkB,SAAA,WAAoB,OAAA;EACtC,0BAAA,CAA2B,SAAA,WAAoB,OAAA;EAtFhC;EAwFf,eAAA,CAAgB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EAC7D,2BAAA,CAA4B,SAAA,WAAoB,OAAA;EAtFvC;EA6FT,YAAA,CAAa,KAAA,WAAgB,OAAA;EAC7B,qBAAA,CAAsB,SAAA,WAAoB,OAAA;EAC1C,QAAA,CAAS,KAAA,UAAe,SAAA,WAAoB,OAAA;EAC5C,WAAA,CAAY,KAAA,WAAgB,OAAA;EAC5B,sBAAA,CAAuB,SAAA,WAAoB,OAAA;EA3EhC;EA6EX,kBAAA,CAAmB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EA5Ee;EA8E/E,aAAA,CAAc,KAAA,UAAe,WAAA,WAAsB,OAAA;EA7EgC;EA+EnF,YAAA,IAAgB,OAAA,CAAQ,KAAA;IAAQ,KAAA;IAAe,IAAA;IAAc,SAAA;EAAA;EAM7D,cAAA,CAAe,EAAA,WAAa,OAAA,CAAQ,OAAA;EACpC,kBAAA,CAAmB,SAAA,UAAmB,MAAA,WAAiB,OAAA,CAAQ,OAAA;EAC/D,0BAAA,CAA2B,SAAA,WAAoB,OAAA,CAAQ,OAAA;EACvD,mBAAA,CAAoB,SAAA,WAAoB,OAAA;EACxC,sBAAA,CAAuB,SAAA,UAAmB,IAAA,GAAO,6BAAA,GAAgC,OAAA,CAAQ,OAAA;EAnFd;EAqF3E,YAAA,CAAa,IAAA,GAAO,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAClD,qBAAA,CAAsB,SAAA,UAAmB,QAAA,WAAmB,OAAA;IAAU,EAAA;EAAA;EACtE,kBAAA,CAAmB,SAAA,UAAmB,MAAA,WAAiB,OAAA;EACvD,aAAA,CAAc,KAAA,EAAO,UAAA,GAAa,OAAA,CAAQ,OAAA;EAnFK;EAqF/C,qBAAA,CAAsB,KAAA,EAAO,UAAA,GAAa,OAAA;EAC1C,iBAAA,CAAkB,EAAA,UAAY,KAAA;IAAS,IAAA;IAAe,KAAA;IAAgB,QAAA;EAAA,IAAsB,OAAA;EAC5F,mBAAA,CAAoB,EAAA,UAAY,MAAA,UAAgB,IAAA;IAAS,QAAA;EAAA,IAAsB,OAAA;EAC/E,qBAAA,CAAsB,EAAA,UAAY,QAAA,WAAmB,OAAA;EAxEO;EA0E5D,mBAAA,CAAoB,EAAA,UAAY,MAAA,EAAQ,aAAA,EAAe,IAAA;IAAS,QAAA;EAAA,IAAsB,OAAA;EACtF,oBAAA,CAAqB,EAAA,UAAY,SAAA,UAAmB,IAAA;IAAS,MAAA;EAAA,IAAoB,OAAA;EACjF,aAAA,CAAc,EAAA,WAAa,OAAA;EAnEwD;EAqEnF,qBAAA,CAAsB,MAAA,UAAgB,MAAA,WAAiB,OAAA;EACvD,uBAAA,CAAwB,SAAA,WAAoB,OAAA;EAnEoB;EAqEhE,aAAA,IAAiB,OAAA;EACjB,wBAAA,CAAyB,SAAA,WAAoB,OAAA;EAC7C,6BAAA,IAAiC,OAAA;EAMjC,iBAAA,CAAkB,SAAA,WAAoB,OAAA;EACtC,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,aAAA,CAAc,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACjD,gBAAA,CAAiB,SAAA,UAAmB,KAAA,WAAgB,OAAA;EACpD,kBAAA,CAAmB,aAAA,UAAuB,WAAA,UAAqB,KAAA,WAAgB,OAAA;EAC/E,kBAAA,CAAmB,SAAA,WAAoB,OAAA;EAMvC,OAAA,CAAQ,KAAA;IAAS,SAAA;IAAmB,eAAA;IAAyB,QAAA,EAAU,QAAA;IAAU,SAAA;EAAA,IAAsB,OAAA;EACvG,mBAAA,CAAoB,SAAA,WAAoB,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,iBAAA;EAAA;EACpE,mBAAA,CAAoB,SAAA,WAAoB,OAAA,CAAQ,KAAA;IAAQ,EAAA;IAAY,UAAA;EAAA;EACpE,UAAA,CAAW,SAAA,UAAmB,eAAA,WAA0B,OAAA;EACxD,UAAA,CAAW,EAAA,WAAa,OAAA;EACxB,2BAAA,CAA4B,SAAA,WAAoB,OAAA;EA9Ce;EAgD/D,8BAAA,CAA+B,SAAA,WAAoB,OAAA;EACnD,YAAA,CAAa,EAAA,UAAY,eAAA,WAA0B,OAAA;EACnD,iBAAA,CAAkB,EAAA,UAAY,SAAA,WAAoB,OAAA;EAhDV;EAkDxC,mBAAA,CAAoB,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACjE,oBAAA,CAAqB,SAAA,WAAoB,OAAA;EAlDwC;EAoDjF,yBAAA,CAA0B,SAAA,WAAoB,OAAA;EAM9C,mBAAA,CAAoB,KAAA,EAAO,qBAAA,GAAwB,OAAA;EACnD,6BAAA,CAA8B,SAAA,WAAoB,OAAA;EAxDU;EA0D5D,2BAAA,CAA4B,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACzE,2BAAA,CAA4B,aAAA,UAAuB,WAAA,WAAsB,OAAA;EACzE,8BAAA,CAA+B,SAAA,WAAoB,OAAA;EAMnD,gBAAA,CAAiB,QAAA,UAAkB,QAAA,WAAmB,OAAA,CAAQ,SAAA;EAC9D,iBAAA,CAAkB,IAAA,EAAM,SAAA,GAAY,OAAA;AAAA;;;;;;;AA3TtC;;;;;AACA;;;;;AAEA;;;;;;;;;;;;iBC6FgB,gBAAA,CACd,OAAA,UACA,SAAA,GAAY,GAAA;;;;;;iBAoCE,OAAA,CAAQ,KAAA;AAAA,UAgBd,aAAA;EACR,MAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;AAAA;;;;;;iBAcc,oBAAA,CACd,QAAA,WACC,aAAA;AAAA,UAiCc,eAAA;EACf,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,MAAA;AAAA;;;;;;;;;;;iBAaoB,eAAA,CACpB,OAAA,EAAS,eAAA,EACT,YAAA,YACC,OAAA,CAAQ,eAAA;;;UC5KM,SAAA;EHpDL;EGsDV,IAAA;;EAEA,IAAA;;EAEA,WAAA;AAAA;;;;AF5DF;;;;;AACA;;;iBE6MgB,eAAA,CACd,WAAA,UACA,IAAA,UACA,WAAA;;AF/MF;;;iBEqRgB,aAAA,CACd,WAAA;EACG,IAAA;EAAc,UAAA;EAAoB,MAAA;AAAA;;;AFpRvC;;;;;;;;iBEiTgB,kBAAA,CAAmB,UAAA,aAAuB,SAAA;;;UC5TzC,KAAA;EACf,IAAA;EACA,SAAA;EACA,OAAA;EACA,IAAA;EJEA;EIAA,WAAA;AAAA;AAAA,UAGe,YAAA;;EAEf,SAAA;;EAEA,OAAA;AAAA;;;;AHPF;iBGiBgB,cAAA,CAAe,IAAA;AAAA,UAWd,OAAA;EACf,KAAA;EACA,KAAA;EACA,IAAA;AAAA;;;;AH7BF;iBGoCgB,aAAA,CAAc,KAAA,aAAkB,OAAA;AAAA,UAgC/B,WAAA;EACf,KAAA;EACA,KAAA;EACA,SAAA;EACA,OAAA;EACA,MAAA;EACA,QAAA,EAAU,WAAA;AAAA;AHvEZ;;;;AAAA,iBG8EgB,YAAA,CAAa,OAAA,WAAkB,WAAA;AAAA,iBA6M/B,aAAA,CAAc,OAAA,UAAiB,IAAA,GAAO,YAAA,GAAe,KAAA;;;;AJ1SrE;;;;;;;;;;;;;;iBKagB,UAAA,CACd,YAAA;;;;ALdF;;UMViB,WAAA;EACf,cAAA;EACA,aAAA;EACA,YAAA;AAAA;;;UCOe,YAAA;EACf,SAAA;EACA,WAAA;EACA,IAAA;EACA,SAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;EACA,MAAA;EACA,SAAA;EACA,cAAA;EACA,OAAA;AAAA;AAAA,UAGe,aAAA;;EAEf,UAAA;ENRqB;EMUrB,OAAA;ENVqB;EMYrB,KAAA;ENXU;EMaV,UAAA;;EAEA,QAAA;AAAA;AAAA,iBA0Cc,aAAA,CAAc,KAAA;;;;;iBA8BR,aAAA,CACpB,OAAA,EAAS,YAAA,IACT,QAAA,EAAU,eAAA,GACT,OAAA,CAAQ,YAAA;;;;;;;iBC5EK,sBAAA,CAAuB,KAAA;AAAA,UAoCtB,aAAA;EPnDL;EOqDV,IAAA;;;;APpDF;;EO0DE,aAAA;AAAA;;APzDF;;;;;AACA;;;iBOoEsB,aAAA,CACpB,KAAA,UACA,OAAA,EAAS,YAAA,IACT,IAAA,GAAO,aAAA,GACN,OAAA,CAAQ,YAAA"}
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  import "./utils-C9HsYDpO.mjs";
2
2
  import { a as slugify, i as parseSessionFilename, n as decodeEncodedDir, r as migrateFromJson } from "./migrate-BD7D8EEh.mjs";
3
3
  import { n as ensurePaiMarker, r as readPaiMarker, t as discoverPaiMarkers } from "./pai-marker-D1MMswkz.mjs";
4
- import { l as chunkMarkdown, r as detectTier, u as estimateTokens } from "./helpers-Dj-B314s.mjs";
5
- import { i as populateSlugs, n as buildFtsQuery } from "./search-BaTbyWjK.mjs";
6
- import { n as rerankResults, t as configureRerankerModel } from "./reranker-CRVawoEh.mjs";
4
+ import { a as parseHeadings, n as chunkMarkdown, r as estimateTokens, t as buildOutline } from "./chunker-BH4i-F2b.mjs";
5
+ import { r as detectTier } from "./helpers-BRCJg0G3.mjs";
6
+ import { i as populateSlugs, n as buildFtsQuery } from "./search-BOSphCJ1.mjs";
7
+ import { n as rerankResults, t as configureRerankerModel } from "./reranker-DKv80KO5.mjs";
7
8
 
8
- export { buildFtsQuery, chunkMarkdown, configureRerankerModel, decodeEncodedDir, detectTier, discoverPaiMarkers, ensurePaiMarker, estimateTokens, migrateFromJson, parseSessionFilename, populateSlugs, readPaiMarker, rerankResults, slugify };
9
+ export { buildFtsQuery, buildOutline, chunkMarkdown, configureRerankerModel, decodeEncodedDir, detectTier, discoverPaiMarkers, ensurePaiMarker, estimateTokens, migrateFromJson, parseHeadings, parseSessionFilename, populateSlugs, readPaiMarker, rerankResults, slugify };
@@ -0,0 +1,5 @@
1
+ import { a as parseSessionTitleChunk } from "./helpers-BRCJg0G3.mjs";
2
+ import { i as indexProjectWithBackend, n as indexAllWithBackend, r as indexFileWithBackend, t as embedChunksWithBackend } from "./async-CNn36gm4.mjs";
3
+ import "./indexer-backend-isSLg6yE.mjs";
4
+
5
+ export { embedChunksWithBackend };
@@ -1,4 +1,4 @@
1
- import { i as paiSocketPath } from "./runtime-paths-BRwDeBEg.mjs";
1
+ import { i as paiSocketPath } from "./runtime-paths-CHTg3ywb.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { connect } from "node:net";
4
4
 
@@ -155,4 +155,4 @@ var PaiClient = class {
155
155
 
156
156
  //#endregion
157
157
  export { PaiClient as t };
158
- //# sourceMappingURL=ipc-client-Dasm515c.mjs.map
158
+ //# sourceMappingURL=ipc-client-D16Xw6Uo.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-client-Dasm515c.mjs","names":[],"sources":["../src/daemon/ipc-client.ts"],"sourcesContent":["/**\n * ipc-client.ts — IPC client for the PAI Daemon MCP shim\n *\n * PaiClient connects to the Unix Domain Socket served by daemon.ts\n * and forwards tool calls to the daemon. Uses a fresh socket connection per\n * call (connect → write JSON + newline → read response line → parse → destroy).\n * This keeps the client stateless and avoids connection management complexity.\n *\n * Adapted from the Coogle ipc-client pattern (which was adapted from Whazaa).\n */\n\nimport { connect, Socket } from \"node:net\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n NotificationConfig,\n NotificationMode,\n NotificationEvent,\n SendResult,\n} from \"../notifications/types.js\";\nimport type { TopicCheckParams, TopicCheckResult } from \"../topics/detector.js\";\nimport type { AutoRouteResult } from \"../session/auto-route.js\";\nimport { paiSocketPath } from \"../runtime-paths.js\";\n\n// ---------------------------------------------------------------------------\n// Protocol types\n// ---------------------------------------------------------------------------\n\n/** Default socket path */\nexport const IPC_SOCKET_PATH = paiSocketPath();\n\n/** Timeout for IPC calls (60 seconds) */\nconst IPC_TIMEOUT_MS = 60_000;\n\ninterface IpcRequest {\n id: string;\n method: string;\n params: Record<string, unknown>;\n}\n\ninterface IpcResponse {\n id: string;\n ok: boolean;\n result?: unknown;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\n/**\n * Thin IPC proxy that forwards tool calls to pai-daemon over a Unix\n * Domain Socket. Each call opens a fresh connection, sends one NDJSON request,\n * reads the response, and closes. Stateless and simple.\n */\nexport class PaiClient {\n private readonly socketPath: string;\n\n constructor(socketPath?: string) {\n this.socketPath = socketPath ?? IPC_SOCKET_PATH;\n }\n\n /**\n * Call a PAI tool by name with the given params.\n * Returns the tool result or throws on error.\n *\n * `timeoutMs` overrides the default 60s wait — for a caller on a hook's\n * critical path (e.g. the threshold-triggered handover enqueue in\n * `cli/commands/session/autosave.ts`) where the actual work happens later,\n * asynchronously, in the daemon's worker loop, and only the cheap\n * enqueue handshake itself should ever be waited on.\n */\n async call(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown> {\n return this.send(method, params, timeoutMs);\n }\n\n /**\n * Check daemon status.\n */\n async status(): Promise<Record<string, unknown>> {\n const result = await this.send(\"status\", {});\n return result as Record<string, unknown>;\n }\n\n /**\n * Trigger an immediate index run.\n */\n async triggerIndex(): Promise<void> {\n await this.send(\"index_now\", {});\n }\n\n // -------------------------------------------------------------------------\n // Notification methods\n // -------------------------------------------------------------------------\n\n /**\n * Get the current notification config from the daemon.\n */\n async getNotificationConfig(): Promise<{\n config: NotificationConfig;\n activeChannels: string[];\n }> {\n const result = await this.send(\"notification_get_config\", {});\n return result as { config: NotificationConfig; activeChannels: string[] };\n }\n\n /**\n * Patch the notification config on the daemon (and persist to disk).\n */\n async setNotificationConfig(patch: {\n mode?: NotificationMode;\n channels?: Partial<NotificationConfig[\"channels\"]>;\n routing?: Partial<NotificationConfig[\"routing\"]>;\n }): Promise<{ config: NotificationConfig }> {\n const result = await this.send(\"notification_set_config\", patch as Record<string, unknown>);\n return result as { config: NotificationConfig };\n }\n\n /**\n * Send a notification via the daemon (routes to configured channels).\n */\n async sendNotification(payload: {\n event: NotificationEvent;\n message: string;\n title?: string;\n }): Promise<SendResult> {\n const result = await this.send(\"notification_send\", payload as Record<string, unknown>);\n return result as SendResult;\n }\n\n // -------------------------------------------------------------------------\n // Topic detection methods\n // -------------------------------------------------------------------------\n\n /**\n * Check whether the provided context text has drifted to a different project\n * than the session's current routing.\n */\n async topicCheck(params: TopicCheckParams): Promise<TopicCheckResult> {\n const result = await this.send(\"topic_check\", params as unknown as Record<string, unknown>);\n return result as TopicCheckResult;\n }\n\n // -------------------------------------------------------------------------\n // Session routing methods\n // -------------------------------------------------------------------------\n\n /**\n * Automatically detect which project a session belongs to.\n * Tries path match, PAI.md marker walk, then topic detection (if context given).\n */\n async sessionAutoRoute(params: {\n cwd?: string;\n context?: string;\n }): Promise<AutoRouteResult | null> {\n // session_auto_route returns a ToolResult (content array). Extract the text\n // and parse JSON from it.\n const result = await this.send(\"session_auto_route\", params as Record<string, unknown>);\n const toolResult = result as { content?: Array<{ text: string }>; isError?: boolean };\n if (toolResult.isError) return null;\n const text = toolResult.content?.[0]?.text ?? \"\";\n // Text is either JSON (on match) or a human-readable \"no match\" message\n try {\n return JSON.parse(text) as AutoRouteResult;\n } catch {\n return null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Internal transport\n // -------------------------------------------------------------------------\n\n /**\n * Send a single IPC request and wait for the response.\n * Opens a new socket connection per call — simple and reliable.\n */\n private send(\n method: string,\n params: Record<string, unknown>,\n timeoutMs: number = IPC_TIMEOUT_MS\n ): Promise<unknown> {\n const socketPath = this.socketPath;\n\n return new Promise((resolve, reject) => {\n let socket: Socket | null = null;\n let done = false;\n let buffer = \"\";\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function finish(error: Error | null, value?: unknown): void {\n if (done) return;\n done = true;\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n try {\n socket?.destroy();\n } catch {\n // ignore\n }\n if (error) {\n reject(error);\n } else {\n resolve(value);\n }\n }\n\n socket = connect(socketPath, () => {\n const request: IpcRequest = {\n id: randomUUID(),\n method,\n params,\n };\n socket!.write(JSON.stringify(request) + \"\\n\");\n });\n\n socket.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString();\n const nl = buffer.indexOf(\"\\n\");\n if (nl === -1) return;\n\n const line = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n\n let response: IpcResponse;\n try {\n response = JSON.parse(line) as IpcResponse;\n } catch {\n finish(new Error(`IPC parse error: ${line}`));\n return;\n }\n\n if (!response.ok) {\n finish(new Error(response.error ?? \"IPC call failed\"));\n } else {\n finish(null, response.result);\n }\n });\n\n socket.on(\"error\", (e: NodeJS.ErrnoException) => {\n if (e.code === \"ENOENT\" || e.code === \"ECONNREFUSED\") {\n finish(\n new Error(\n \"PAI daemon not running. Start it with: pai daemon serve\"\n )\n );\n } else {\n finish(e);\n }\n });\n\n socket.on(\"end\", () => {\n if (!done) {\n finish(new Error(\"IPC connection closed before response\"));\n }\n });\n\n timer = setTimeout(() => {\n finish(new Error(`IPC call timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,MAAa,kBAAkB,eAAe;;AAG9C,MAAM,iBAAiB;;;;;;AAwBvB,IAAa,YAAb,MAAuB;CACrB,AAAiB;CAEjB,YAAY,YAAqB;AAC/B,OAAK,aAAa,cAAc;;;;;;;;;;;;CAalC,MAAM,KAAK,QAAgB,QAAiC,WAAsC;AAChG,SAAO,KAAK,KAAK,QAAQ,QAAQ,UAAU;;;;;CAM7C,MAAM,SAA2C;AAE/C,SADe,MAAM,KAAK,KAAK,UAAU,EAAE,CAAC;;;;;CAO9C,MAAM,eAA8B;AAClC,QAAM,KAAK,KAAK,aAAa,EAAE,CAAC;;;;;CAUlC,MAAM,wBAGH;AAED,SADe,MAAM,KAAK,KAAK,2BAA2B,EAAE,CAAC;;;;;CAO/D,MAAM,sBAAsB,OAIgB;AAE1C,SADe,MAAM,KAAK,KAAK,2BAA2B,MAAiC;;;;;CAO7F,MAAM,iBAAiB,SAIC;AAEtB,SADe,MAAM,KAAK,KAAK,qBAAqB,QAAmC;;;;;;CAYzF,MAAM,WAAW,QAAqD;AAEpE,SADe,MAAM,KAAK,KAAK,eAAe,OAA6C;;;;;;CAY7F,MAAM,iBAAiB,QAGa;EAIlC,MAAM,aADS,MAAM,KAAK,KAAK,sBAAsB,OAAkC;AAEvF,MAAI,WAAW,QAAS,QAAO;EAC/B,MAAM,OAAO,WAAW,UAAU,IAAI,QAAQ;AAE9C,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;;;;CAYX,AAAQ,KACN,QACA,QACA,YAAoB,gBACF;EAClB,MAAM,aAAa,KAAK;AAExB,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,IAAI,SAAwB;GAC5B,IAAI,OAAO;GACX,IAAI,SAAS;GACb,IAAI,QAA8C;GAElD,SAAS,OAAO,OAAqB,OAAuB;AAC1D,QAAI,KAAM;AACV,WAAO;AACP,QAAI,UAAU,MAAM;AAClB,kBAAa,MAAM;AACnB,aAAQ;;AAEV,QAAI;AACF,aAAQ,SAAS;YACX;AAGR,QAAI,MACF,QAAO,MAAM;QAEb,SAAQ,MAAM;;AAIlB,YAAS,QAAQ,kBAAkB;IACjC,MAAM,UAAsB;KAC1B,IAAI,YAAY;KAChB;KACA;KACD;AACD,WAAQ,MAAM,KAAK,UAAU,QAAQ,GAAG,KAAK;KAC7C;AAEF,UAAO,GAAG,SAAS,UAAkB;AACnC,cAAU,MAAM,UAAU;IAC1B,MAAM,KAAK,OAAO,QAAQ,KAAK;AAC/B,QAAI,OAAO,GAAI;IAEf,MAAM,OAAO,OAAO,MAAM,GAAG,GAAG;AAChC,aAAS,OAAO,MAAM,KAAK,EAAE;IAE7B,IAAI;AACJ,QAAI;AACF,gBAAW,KAAK,MAAM,KAAK;YACrB;AACN,4BAAO,IAAI,MAAM,oBAAoB,OAAO,CAAC;AAC7C;;AAGF,QAAI,CAAC,SAAS,GACZ,QAAO,IAAI,MAAM,SAAS,SAAS,kBAAkB,CAAC;QAEtD,QAAO,MAAM,SAAS,OAAO;KAE/B;AAEF,UAAO,GAAG,UAAU,MAA6B;AAC/C,QAAI,EAAE,SAAS,YAAY,EAAE,SAAS,eACpC,wBACE,IAAI,MACF,0DACD,CACF;QAED,QAAO,EAAE;KAEX;AAEF,UAAO,GAAG,aAAa;AACrB,QAAI,CAAC,KACH,wBAAO,IAAI,MAAM,wCAAwC,CAAC;KAE5D;AAEF,WAAQ,iBAAiB;AACvB,2BAAO,IAAI,MAAM,4BAA4B,UAAU,IAAI,CAAC;MAC3D,UAAU;IACb"}
1
+ {"version":3,"file":"ipc-client-D16Xw6Uo.mjs","names":[],"sources":["../src/daemon/ipc-client.ts"],"sourcesContent":["/**\n * ipc-client.ts — IPC client for the PAI Daemon MCP shim\n *\n * PaiClient connects to the Unix Domain Socket served by daemon.ts\n * and forwards tool calls to the daemon. Uses a fresh socket connection per\n * call (connect → write JSON + newline → read response line → parse → destroy).\n * This keeps the client stateless and avoids connection management complexity.\n *\n * Adapted from the Coogle ipc-client pattern (which was adapted from Whazaa).\n */\n\nimport { connect, Socket } from \"node:net\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n NotificationConfig,\n NotificationMode,\n NotificationEvent,\n SendResult,\n} from \"../notifications/types.js\";\nimport type { TopicCheckParams, TopicCheckResult } from \"../topics/detector.js\";\nimport type { AutoRouteResult } from \"../session/auto-route.js\";\nimport { paiSocketPath } from \"../runtime-paths.js\";\n\n// ---------------------------------------------------------------------------\n// Protocol types\n// ---------------------------------------------------------------------------\n\n/** Default socket path */\nexport const IPC_SOCKET_PATH = paiSocketPath();\n\n/** Timeout for IPC calls (60 seconds) */\nconst IPC_TIMEOUT_MS = 60_000;\n\ninterface IpcRequest {\n id: string;\n method: string;\n params: Record<string, unknown>;\n}\n\ninterface IpcResponse {\n id: string;\n ok: boolean;\n result?: unknown;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\n/**\n * Thin IPC proxy that forwards tool calls to pai-daemon over a Unix\n * Domain Socket. Each call opens a fresh connection, sends one NDJSON request,\n * reads the response, and closes. Stateless and simple.\n */\nexport class PaiClient {\n private readonly socketPath: string;\n\n constructor(socketPath?: string) {\n this.socketPath = socketPath ?? IPC_SOCKET_PATH;\n }\n\n /**\n * Call a PAI tool by name with the given params.\n * Returns the tool result or throws on error.\n *\n * `timeoutMs` overrides the default 60s wait — for a caller on a hook's\n * critical path (e.g. the threshold-triggered handover enqueue in\n * `cli/commands/session/autosave.ts`) where the actual work happens later,\n * asynchronously, in the daemon's worker loop, and only the cheap\n * enqueue handshake itself should ever be waited on.\n */\n async call(method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<unknown> {\n return this.send(method, params, timeoutMs);\n }\n\n /**\n * Check daemon status.\n */\n async status(): Promise<Record<string, unknown>> {\n const result = await this.send(\"status\", {});\n return result as Record<string, unknown>;\n }\n\n /**\n * Trigger an immediate index run.\n */\n async triggerIndex(): Promise<void> {\n await this.send(\"index_now\", {});\n }\n\n // -------------------------------------------------------------------------\n // Notification methods\n // -------------------------------------------------------------------------\n\n /**\n * Get the current notification config from the daemon.\n */\n async getNotificationConfig(): Promise<{\n config: NotificationConfig;\n activeChannels: string[];\n }> {\n const result = await this.send(\"notification_get_config\", {});\n return result as { config: NotificationConfig; activeChannels: string[] };\n }\n\n /**\n * Patch the notification config on the daemon (and persist to disk).\n */\n async setNotificationConfig(patch: {\n mode?: NotificationMode;\n channels?: Partial<NotificationConfig[\"channels\"]>;\n routing?: Partial<NotificationConfig[\"routing\"]>;\n }): Promise<{ config: NotificationConfig }> {\n const result = await this.send(\"notification_set_config\", patch as Record<string, unknown>);\n return result as { config: NotificationConfig };\n }\n\n /**\n * Send a notification via the daemon (routes to configured channels).\n */\n async sendNotification(payload: {\n event: NotificationEvent;\n message: string;\n title?: string;\n }): Promise<SendResult> {\n const result = await this.send(\"notification_send\", payload as Record<string, unknown>);\n return result as SendResult;\n }\n\n // -------------------------------------------------------------------------\n // Topic detection methods\n // -------------------------------------------------------------------------\n\n /**\n * Check whether the provided context text has drifted to a different project\n * than the session's current routing.\n */\n async topicCheck(params: TopicCheckParams): Promise<TopicCheckResult> {\n const result = await this.send(\"topic_check\", params as unknown as Record<string, unknown>);\n return result as TopicCheckResult;\n }\n\n // -------------------------------------------------------------------------\n // Session routing methods\n // -------------------------------------------------------------------------\n\n /**\n * Automatically detect which project a session belongs to.\n * Tries path match, PAI.md marker walk, then topic detection (if context given).\n */\n async sessionAutoRoute(params: {\n cwd?: string;\n context?: string;\n }): Promise<AutoRouteResult | null> {\n // session_auto_route returns a ToolResult (content array). Extract the text\n // and parse JSON from it.\n const result = await this.send(\"session_auto_route\", params as Record<string, unknown>);\n const toolResult = result as { content?: Array<{ text: string }>; isError?: boolean };\n if (toolResult.isError) return null;\n const text = toolResult.content?.[0]?.text ?? \"\";\n // Text is either JSON (on match) or a human-readable \"no match\" message\n try {\n return JSON.parse(text) as AutoRouteResult;\n } catch {\n return null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Internal transport\n // -------------------------------------------------------------------------\n\n /**\n * Send a single IPC request and wait for the response.\n * Opens a new socket connection per call — simple and reliable.\n */\n private send(\n method: string,\n params: Record<string, unknown>,\n timeoutMs: number = IPC_TIMEOUT_MS\n ): Promise<unknown> {\n const socketPath = this.socketPath;\n\n return new Promise((resolve, reject) => {\n let socket: Socket | null = null;\n let done = false;\n let buffer = \"\";\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function finish(error: Error | null, value?: unknown): void {\n if (done) return;\n done = true;\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n try {\n socket?.destroy();\n } catch {\n // ignore\n }\n if (error) {\n reject(error);\n } else {\n resolve(value);\n }\n }\n\n socket = connect(socketPath, () => {\n const request: IpcRequest = {\n id: randomUUID(),\n method,\n params,\n };\n socket!.write(JSON.stringify(request) + \"\\n\");\n });\n\n socket.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString();\n const nl = buffer.indexOf(\"\\n\");\n if (nl === -1) return;\n\n const line = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n\n let response: IpcResponse;\n try {\n response = JSON.parse(line) as IpcResponse;\n } catch {\n finish(new Error(`IPC parse error: ${line}`));\n return;\n }\n\n if (!response.ok) {\n finish(new Error(response.error ?? \"IPC call failed\"));\n } else {\n finish(null, response.result);\n }\n });\n\n socket.on(\"error\", (e: NodeJS.ErrnoException) => {\n if (e.code === \"ENOENT\" || e.code === \"ECONNREFUSED\") {\n finish(\n new Error(\n \"PAI daemon not running. Start it with: pai daemon serve\"\n )\n );\n } else {\n finish(e);\n }\n });\n\n socket.on(\"end\", () => {\n if (!done) {\n finish(new Error(\"IPC connection closed before response\"));\n }\n });\n\n timer = setTimeout(() => {\n finish(new Error(`IPC call timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,MAAa,kBAAkB,eAAe;;AAG9C,MAAM,iBAAiB;;;;;;AAwBvB,IAAa,YAAb,MAAuB;CACrB,AAAiB;CAEjB,YAAY,YAAqB;AAC/B,OAAK,aAAa,cAAc;;;;;;;;;;;;CAalC,MAAM,KAAK,QAAgB,QAAiC,WAAsC;AAChG,SAAO,KAAK,KAAK,QAAQ,QAAQ,UAAU;;;;;CAM7C,MAAM,SAA2C;AAE/C,SADe,MAAM,KAAK,KAAK,UAAU,EAAE,CAAC;;;;;CAO9C,MAAM,eAA8B;AAClC,QAAM,KAAK,KAAK,aAAa,EAAE,CAAC;;;;;CAUlC,MAAM,wBAGH;AAED,SADe,MAAM,KAAK,KAAK,2BAA2B,EAAE,CAAC;;;;;CAO/D,MAAM,sBAAsB,OAIgB;AAE1C,SADe,MAAM,KAAK,KAAK,2BAA2B,MAAiC;;;;;CAO7F,MAAM,iBAAiB,SAIC;AAEtB,SADe,MAAM,KAAK,KAAK,qBAAqB,QAAmC;;;;;;CAYzF,MAAM,WAAW,QAAqD;AAEpE,SADe,MAAM,KAAK,KAAK,eAAe,OAA6C;;;;;;CAY7F,MAAM,iBAAiB,QAGa;EAIlC,MAAM,aADS,MAAM,KAAK,KAAK,sBAAsB,OAAkC;AAEvF,MAAI,WAAW,QAAS,QAAO;EAC/B,MAAM,OAAO,WAAW,UAAU,IAAI,QAAQ;AAE9C,MAAI;AACF,UAAO,KAAK,MAAM,KAAK;UACjB;AACN,UAAO;;;;;;;CAYX,AAAQ,KACN,QACA,QACA,YAAoB,gBACF;EAClB,MAAM,aAAa,KAAK;AAExB,SAAO,IAAI,SAAS,SAAS,WAAW;GACtC,IAAI,SAAwB;GAC5B,IAAI,OAAO;GACX,IAAI,SAAS;GACb,IAAI,QAA8C;GAElD,SAAS,OAAO,OAAqB,OAAuB;AAC1D,QAAI,KAAM;AACV,WAAO;AACP,QAAI,UAAU,MAAM;AAClB,kBAAa,MAAM;AACnB,aAAQ;;AAEV,QAAI;AACF,aAAQ,SAAS;YACX;AAGR,QAAI,MACF,QAAO,MAAM;QAEb,SAAQ,MAAM;;AAIlB,YAAS,QAAQ,kBAAkB;IACjC,MAAM,UAAsB;KAC1B,IAAI,YAAY;KAChB;KACA;KACD;AACD,WAAQ,MAAM,KAAK,UAAU,QAAQ,GAAG,KAAK;KAC7C;AAEF,UAAO,GAAG,SAAS,UAAkB;AACnC,cAAU,MAAM,UAAU;IAC1B,MAAM,KAAK,OAAO,QAAQ,KAAK;AAC/B,QAAI,OAAO,GAAI;IAEf,MAAM,OAAO,OAAO,MAAM,GAAG,GAAG;AAChC,aAAS,OAAO,MAAM,KAAK,EAAE;IAE7B,IAAI;AACJ,QAAI;AACF,gBAAW,KAAK,MAAM,KAAK;YACrB;AACN,4BAAO,IAAI,MAAM,oBAAoB,OAAO,CAAC;AAC7C;;AAGF,QAAI,CAAC,SAAS,GACZ,QAAO,IAAI,MAAM,SAAS,SAAS,kBAAkB,CAAC;QAEtD,QAAO,MAAM,SAAS,OAAO;KAE/B;AAEF,UAAO,GAAG,UAAU,MAA6B;AAC/C,QAAI,EAAE,SAAS,YAAY,EAAE,SAAS,eACpC,wBACE,IAAI,MACF,0DACD,CACF;QAED,QAAO,EAAE;KAEX;AAEF,UAAO,GAAG,aAAa;AACrB,QAAI,CAAC,KACH,wBAAO,IAAI,MAAM,wCAAwC,CAAC;KAE5D;AAEF,WAAQ,iBAAiB;AACvB,2BAAO,IAAI,MAAM,4BAA4B,UAAU,IAAI,CAAC;MAC3D,UAAU;IACb"}
@@ -26,4 +26,4 @@ function entityContentId(name, tenantId = "default") {
26
26
 
27
27
  //#endregion
28
28
  export { entityContentId as t };
29
- //# sourceMappingURL=kg-entity-Cj8Q6YmU.mjs.map
29
+ //# sourceMappingURL=kg-entity-COTUj1ZC.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"kg-entity-Cj8Q6YmU.mjs","names":[],"sources":["../src/memory/kg-entity.ts"],"sourcesContent":["/**\n * kg-entity.ts — Entity content-addressing with multi-tenant support.\n *\n * Provides UUID5-style deterministic content hashes for KG entities and edges,\n * ensuring that the same entity name always maps to the same ID within a tenant.\n * This enables idempotent upserts and stable foreign keys for kg_triples.\n *\n * Multi-tenant support: each tenant namespace gets its own entity ID space.\n * The default tenant is \"default\" for single-user deployments.\n */\n\nimport { createHash } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface KgEntity {\n entity_id: string;\n tenant_id: string;\n name: string;\n type: string;\n description?: string;\n first_seen?: number;\n last_seen?: number;\n mention_count: number;\n feedback_weight: number;\n}\n\nexport interface KgEntityUpsertParams {\n name: string;\n type?: string;\n description?: string;\n tenantId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Content addressing\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic entity ID (UUID5-style) for a given name and tenant.\n *\n * The ID is a hex digest derived from \"tenant_id:name\" so the same entity\n * always receives the same ID within a tenant namespace.\n *\n * @param name Entity name (case-preserved)\n * @param tenantId Tenant namespace (default: \"default\")\n */\nexport function entityContentId(name: string, tenantId = \"default\"): string {\n return createHash(\"sha256\")\n .update(`entity:${tenantId}:${name}`)\n .digest(\"hex\")\n .slice(0, 32); // 128-bit hex string — UUID5-compatible length\n}\n\n/**\n * Generate a deterministic edge ID for a (source, relation, target) triple\n * within a tenant namespace.\n *\n * @param source Source entity name\n * @param relation Relation/predicate verb phrase\n * @param target Target entity name\n * @param tenantId Tenant namespace (default: \"default\")\n */\nexport function edgeContentId(\n source: string,\n relation: string,\n target: string,\n tenantId = \"default\"\n): string {\n return createHash(\"sha256\")\n .update(`edge:${tenantId}:${source}:${relation}:${target}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n// SQLite's kg_entities CRUD (upsertKgEntity/findKgEntity/listKgEntities/\n// updateEntityFeedbackWeight) lives in storage/sqlite/kg-entity.ts —\n// PostgresBackend implements the same operations inline against its own\n// pool, so there is no cross-backend function to share here.\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,gBAAgB,MAAc,WAAW,WAAmB;AAC1E,QAAO,WAAW,SAAS,CACxB,OAAO,UAAU,SAAS,GAAG,OAAO,CACpC,OAAO,MAAM,CACb,MAAM,GAAG,GAAG"}
1
+ {"version":3,"file":"kg-entity-COTUj1ZC.mjs","names":[],"sources":["../src/memory/kg-entity.ts"],"sourcesContent":["/**\n * kg-entity.ts — Entity content-addressing with multi-tenant support.\n *\n * Provides UUID5-style deterministic content hashes for KG entities and edges,\n * ensuring that the same entity name always maps to the same ID within a tenant.\n * This enables idempotent upserts and stable foreign keys for kg_triples.\n *\n * Multi-tenant support: each tenant namespace gets its own entity ID space.\n * The default tenant is \"default\" for single-user deployments.\n */\n\nimport { createHash } from \"node:crypto\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface KgEntity {\n entity_id: string;\n tenant_id: string;\n name: string;\n type: string;\n description?: string;\n first_seen?: number;\n last_seen?: number;\n mention_count: number;\n feedback_weight: number;\n}\n\nexport interface KgEntityUpsertParams {\n name: string;\n type?: string;\n description?: string;\n tenantId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Content addressing\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic entity ID (UUID5-style) for a given name and tenant.\n *\n * The ID is a hex digest derived from \"tenant_id:name\" so the same entity\n * always receives the same ID within a tenant namespace.\n *\n * @param name Entity name (case-preserved)\n * @param tenantId Tenant namespace (default: \"default\")\n */\nexport function entityContentId(name: string, tenantId = \"default\"): string {\n return createHash(\"sha256\")\n .update(`entity:${tenantId}:${name}`)\n .digest(\"hex\")\n .slice(0, 32); // 128-bit hex string — UUID5-compatible length\n}\n\n/**\n * Generate a deterministic edge ID for a (source, relation, target) triple\n * within a tenant namespace.\n *\n * @param source Source entity name\n * @param relation Relation/predicate verb phrase\n * @param target Target entity name\n * @param tenantId Tenant namespace (default: \"default\")\n */\nexport function edgeContentId(\n source: string,\n relation: string,\n target: string,\n tenantId = \"default\"\n): string {\n return createHash(\"sha256\")\n .update(`edge:${tenantId}:${source}:${relation}:${target}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n// SQLite's kg_entities CRUD (upsertKgEntity/findKgEntity/listKgEntities/\n// updateEntityFeedbackWeight) lives in storage/sqlite/kg-entity.ts —\n// PostgresBackend implements the same operations inline against its own\n// pool, so there is no cross-backend function to share here.\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiDA,SAAgB,gBAAgB,MAAc,WAAW,WAAmB;AAC1E,QAAO,WAAW,SAAS,CACxB,OAAO,UAAU,SAAS,GAAG,OAAO,CACpC,OAAO,MAAM,CACb,MAAM,GAAG,GAAG"}
@@ -1,6 +1,6 @@
1
- import { n as TITLE_STOP_WORDS } from "./stop-words-ClCwO8mZ.mjs";
2
- import "./embeddings-DfrYNIng.mjs";
3
- import { t as zettelThemes } from "./themes-B2v7RNnA.mjs";
1
+ import { n as TITLE_STOP_WORDS } from "./stop-words-BdQuaE9K.mjs";
2
+ import "./embeddings-BbNVXa_0.mjs";
3
+ import { t as zettelThemes } from "./themes-BgqahYVM.mjs";
4
4
  import { mkdirSync, writeFileSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
6
6
 
@@ -188,4 +188,4 @@ function handleIdeaMaterialize(params, vaultPath) {
188
188
 
189
189
  //#endregion
190
190
  export { handleGraphLatentIdeas, handleIdeaMaterialize };
191
- //# sourceMappingURL=latent-ideas-BRy6HNwg.mjs.map
191
+ //# sourceMappingURL=latent-ideas-ByvMjVcb.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"latent-ideas-BRy6HNwg.mjs","names":[],"sources":["../src/graph/latent-ideas.ts"],"sourcesContent":["/**\n * latent-ideas.ts — graph_latent_ideas and idea_materialize endpoint handlers\n *\n * \"Latent ideas\" are recurring themes in the vault that exist as embedding\n * clusters but have NO dedicated note written about them yet. PAI surfaces\n * these by running the same agglomerative clustering used by graph_clusters /\n * zettelThemes and then filtering OUT any cluster whose label is well-matched\n * by an existing note title.\n *\n * The materialize endpoint writes a new Markdown note to the vault filesystem\n * and returns its content so the plugin can open it immediately.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { TITLE_STOP_WORDS } from \"../utils/stop-words.js\";\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport { zettelThemes } from \"../zettelkasten/themes.js\";\n\n// ---------------------------------------------------------------------------\n// Public param / result types\n// ---------------------------------------------------------------------------\n\nexport interface GraphLatentIdeasParams {\n project_id: number;\n /** Minimum notes in a cluster (default: 3) */\n min_cluster_size?: number;\n /** Cap on returned ideas (default: 15) */\n max_ideas?: number;\n /** How far back to look in days (default: 180) */\n lookback_days?: number;\n /** Cosine similarity clustering threshold (default: 0.65) */\n similarity_threshold?: number;\n}\n\nexport interface LatentIdeaSourceNote {\n vault_path: string;\n title: string;\n /** How strongly this note relates to the theme (0-1) */\n relevance: number;\n}\n\nexport interface LatentIdea {\n id: number;\n /** Auto-generated cluster label from zettelThemes */\n label: string;\n /** Number of notes touching this theme */\n size: number;\n /** 0-1, how likely this is a real coherent idea */\n confidence: number;\n /** Notes that contribute to this theme */\n source_notes: LatentIdeaSourceNote[];\n /** Cleaned-up version of label for a potential note title */\n suggested_title: string;\n /** Most common folder among source notes */\n suggested_folder: string;\n /** Number of distinct session date-folders (e.g. \"2026/03\") touching this theme */\n sessions_count: number;\n}\n\nexport interface GraphLatentIdeasResult {\n ideas: LatentIdea[];\n total_clusters_analyzed: number;\n /** How many clusters already have a matching note (excluded from results) */\n materialized_count: number;\n}\n\n// ---------------------------------------------------------------------------\n// Materialize params / result\n// ---------------------------------------------------------------------------\n\nexport interface IdeaMaterializeParams {\n idea_label: string;\n /** User-chosen title for the new note */\n title: string;\n /** Vault-relative folder path where the note should be created */\n folder: string;\n /** Vault-relative paths of the source notes to link from the new note */\n source_paths: string[];\n project_id: number;\n}\n\nexport interface IdeaMaterializeResult {\n /** Vault-relative path of the created note */\n vault_path: string;\n /** Generated markdown content */\n content: string;\n /** Number of wikilinks inserted */\n links_created: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: check if a cluster already has a matching note\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true when any existing vault note title closely matches the cluster\n * label — meaning a dedicated note already exists for this topic.\n *\n * Matching strategy (simple, fast, no embeddings needed):\n * 1. Lowercase both sides and split into words.\n * 2. Remove stop words from the label words.\n * 3. If ≥ 60% of the significant label words appear in a note title → match.\n */\n// TITLE_STOP_WORDS imported from utils/stop-words.ts\n\nfunction labelMatchesTitle(label: string, title: string): boolean {\n const labelWords = label\n .toLowerCase()\n .split(/[\\s\\-_/]+/)\n .filter((w) => w.length > 2 && !TITLE_STOP_WORDS.has(w));\n\n if (labelWords.length === 0) return false;\n\n const titleLower = title.toLowerCase();\n const matchCount = labelWords.filter((w) => titleLower.includes(w)).length;\n return matchCount / labelWords.length >= 0.6;\n}\n\n/**\n * Check whether any note indexed in the vault has a title matching the label.\n * Fetches all vault file rows via StorageBackend for efficiency.\n */\nasync function clusterHasMatchingNote(\n backend: StorageBackend,\n label: string,\n notePaths: string[]\n): Promise<boolean> {\n // First check the notes already in the cluster themselves — if any cluster\n // member's title matches the label it IS the index note → materialized.\n const pathSet = new Set(notePaths);\n\n // Fetch all vault files (bounded — vault rarely > 50k notes)\n const rows = await backend.getAllVaultFiles();\n\n for (const row of rows) {\n if (!row.title) continue;\n // Skip notes already counted inside the cluster — they don't count as\n // \"dedicated notes\"; we only skip a cluster if a SEPARATE note exists.\n if (pathSet.has(row.vaultPath)) continue;\n if (labelMatchesTitle(label, row.title)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: generate a clean suggested title\n// ---------------------------------------------------------------------------\n\nfunction toSuggestedTitle(label: string): string {\n // Remove leading/trailing whitespace, capitalize each word, remove stop words\n // that are all-lowercase at the start of the title.\n const words = label\n .trim()\n .split(/\\s+/)\n .map((w, i) => {\n const lower = w.toLowerCase();\n // Drop leading stop words (but keep if they're the only word)\n if (i === 0 && TITLE_STOP_WORDS.has(lower) && label.trim().split(/\\s+/).length > 1) {\n return \"\";\n }\n return w.charAt(0).toUpperCase() + w.slice(1);\n })\n .filter(Boolean);\n\n return words.join(\" \") || label;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: find most common folder\n// ---------------------------------------------------------------------------\n\nfunction mostCommonFolder(vaultPaths: string[]): string {\n const counts = new Map<string, number>();\n for (const p of vaultPaths) {\n const parts = p.split(\"/\");\n const folder = parts.length > 1 ? parts.slice(0, -1).join(\"/\") : \"\";\n counts.set(folder, (counts.get(folder) ?? 0) + 1);\n }\n\n let best = \"\";\n let bestCount = 0;\n for (const [folder, count] of counts) {\n if (count > bestCount) {\n bestCount = count;\n best = folder;\n }\n }\n return best;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: count distinct session date-folders\n// ---------------------------------------------------------------------------\n\n/**\n * Heuristic: vault notes are often stored in date-based folders like\n * \"2026/03/15\" or \"Daily/2026-03\". We extract the first numeric path\n * segment that looks like a year (2020-2030) and group by year+month.\n *\n * Falls back to counting distinct top-level folders.\n */\nfunction countDistinctSessions(vaultPaths: string[]): number {\n const sessions = new Set<string>();\n const yearMonthRe = /\\b(202\\d)\\D?(0[1-9]|1[0-2])\\b/;\n\n for (const p of vaultPaths) {\n const m = yearMonthRe.exec(p);\n if (m) {\n sessions.add(`${m[1]}-${m[2]}`);\n } else {\n // Fallback: use top-level folder as a proxy for \"session bucket\"\n const topFolder = p.split(\"/\")[0];\n sessions.add(topFolder);\n }\n }\n return sessions.size;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: calculate confidence score\n// ---------------------------------------------------------------------------\n\n/**\n * Confidence combines:\n * - Cluster size (normalized, capped at 20 for max contribution)\n * - Folder diversity (0-1 already)\n * - Sessions count (normalized, capped at 5)\n *\n * Formula: 0.4 * sizeScore + 0.35 * folderDiversity + 0.25 * sessionScore\n */\nfunction calcConfidence(\n size: number,\n folderDiversity: number,\n sessionsCount: number\n): number {\n const sizeScore = Math.min(size / 20, 1.0);\n const sessionScore = Math.min(sessionsCount / 5, 1.0);\n const raw = 0.4 * sizeScore + 0.35 * folderDiversity + 0.25 * sessionScore;\n return Math.round(raw * 100) / 100;\n}\n\n// ---------------------------------------------------------------------------\n// Main handler: graph_latent_ideas\n// ---------------------------------------------------------------------------\n\nexport async function handleGraphLatentIdeas(\n backend: StorageBackend,\n params: GraphLatentIdeasParams\n): Promise<GraphLatentIdeasResult> {\n const minClusterSize = params.min_cluster_size ?? 3;\n const maxIdeas = params.max_ideas ?? 15;\n const lookbackDays = params.lookback_days ?? 180;\n const similarityThreshold = params.similarity_threshold ?? 0.65;\n\n const { project_id: vaultProjectId } = params;\n if (!vaultProjectId) {\n throw new Error(\n \"graph_latent_ideas: project_id is required (pass the vault project's numeric ID)\"\n );\n }\n\n // Run the same clustering algorithm used by graph_clusters\n const themeResult = await zettelThemes(backend, {\n vaultProjectId,\n lookbackDays,\n minClusterSize,\n maxThemes: maxIdeas * 3, // Over-fetch — many will be filtered as materialized\n similarityThreshold,\n });\n\n const ideas: LatentIdea[] = [];\n let materializedCount = 0;\n\n for (const theme of themeResult.themes) {\n const notePaths = theme.notes.map((n) => n.path);\n\n // Check if a dedicated note already exists for this theme\n if (await clusterHasMatchingNote(backend, theme.label, notePaths)) {\n materializedCount++;\n continue;\n }\n\n // This is a latent idea — no dedicated note exists yet\n const suggestedFolder = mostCommonFolder(notePaths);\n const sessionsCount = countDistinctSessions(notePaths);\n const confidence = calcConfidence(theme.size, theme.folderDiversity, sessionsCount);\n\n // Build source notes with relevance scores\n // Relevance is approximated by position in cluster (centroid-closest first)\n // zettelThemes returns notes in no guaranteed order; assign uniform relevance\n // decreasing from 1.0 to 0.5 across the list.\n const sourceNotes: LatentIdeaSourceNote[] = theme.notes.map((n, idx) => ({\n vault_path: n.path,\n title: n.title ?? n.path.split(\"/\").pop()?.replace(/\\.md$/i, \"\") ?? n.path,\n relevance: Math.round((1.0 - (idx / Math.max(theme.notes.length - 1, 1)) * 0.5) * 100) / 100,\n }));\n\n ideas.push({\n id: theme.id,\n label: theme.label,\n size: theme.size,\n confidence,\n source_notes: sourceNotes,\n suggested_title: toSuggestedTitle(theme.label),\n suggested_folder: suggestedFolder,\n sessions_count: sessionsCount,\n });\n\n if (ideas.length >= maxIdeas) break;\n }\n\n // Sort by confidence descending\n ideas.sort((a, b) => b.confidence - a.confidence);\n\n return {\n ideas,\n total_clusters_analyzed: themeResult.themes.length + materializedCount,\n materialized_count: materializedCount,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Materialize handler: idea_materialize\n// ---------------------------------------------------------------------------\n\nexport function handleIdeaMaterialize(\n params: IdeaMaterializeParams,\n vaultPath: string\n): IdeaMaterializeResult {\n const { idea_label, title, folder, source_paths } = params;\n\n // Sanitize filename: replace characters illegal in filenames\n const safeTitle = title.replace(/[/\\\\:*?\"<>|]/g, \"-\");\n const fileName = `${safeTitle}.md`;\n\n // Vault-relative path (forward slashes, no leading slash)\n const relFolder = folder.replace(/^\\/+|\\/+$/g, \"\");\n const vault_path = relFolder ? `${relFolder}/${fileName}` : fileName;\n\n // Absolute filesystem path\n const absPath = join(vaultPath, vault_path);\n const absDir = dirname(absPath);\n\n // Build wikilinks from source_paths\n const wikilinks = source_paths\n .map((p) => {\n // Derive a display name: filename without extension\n const name = p.split(\"/\").pop()?.replace(/\\.md$/i, \"\") ?? p;\n // Relative wikilink — use just the filename (Obsidian resolves by title)\n return `- [[${name}]]`;\n })\n .join(\"\\n\");\n\n const links_created = source_paths.length;\n\n const content = [\n `# ${title}`,\n \"\",\n `*Materialized from latent idea: \"${idea_label}\"*`,\n `*Sources: ${links_created} notes*`,\n \"\",\n \"## Related Notes\",\n \"\",\n wikilinks || \"*(no source notes)*\",\n \"\",\n \"## Notes\",\n \"\",\n \"<!-- Add your thoughts about this idea here -->\",\n \"\",\n ].join(\"\\n\");\n\n // Write the file (create parent directories as needed)\n mkdirSync(absDir, { recursive: true });\n writeFileSync(absPath, content, \"utf-8\");\n\n return {\n vault_path,\n content,\n links_created,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"latent-ideas-ByvMjVcb.mjs","names":[],"sources":["../src/graph/latent-ideas.ts"],"sourcesContent":["/**\n * latent-ideas.ts — graph_latent_ideas and idea_materialize endpoint handlers\n *\n * \"Latent ideas\" are recurring themes in the vault that exist as embedding\n * clusters but have NO dedicated note written about them yet. PAI surfaces\n * these by running the same agglomerative clustering used by graph_clusters /\n * zettelThemes and then filtering OUT any cluster whose label is well-matched\n * by an existing note title.\n *\n * The materialize endpoint writes a new Markdown note to the vault filesystem\n * and returns its content so the plugin can open it immediately.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { TITLE_STOP_WORDS } from \"../utils/stop-words.js\";\nimport type { StorageBackend } from \"../storage/interface.js\";\nimport { zettelThemes } from \"../zettelkasten/themes.js\";\n\n// ---------------------------------------------------------------------------\n// Public param / result types\n// ---------------------------------------------------------------------------\n\nexport interface GraphLatentIdeasParams {\n project_id: number;\n /** Minimum notes in a cluster (default: 3) */\n min_cluster_size?: number;\n /** Cap on returned ideas (default: 15) */\n max_ideas?: number;\n /** How far back to look in days (default: 180) */\n lookback_days?: number;\n /** Cosine similarity clustering threshold (default: 0.65) */\n similarity_threshold?: number;\n}\n\nexport interface LatentIdeaSourceNote {\n vault_path: string;\n title: string;\n /** How strongly this note relates to the theme (0-1) */\n relevance: number;\n}\n\nexport interface LatentIdea {\n id: number;\n /** Auto-generated cluster label from zettelThemes */\n label: string;\n /** Number of notes touching this theme */\n size: number;\n /** 0-1, how likely this is a real coherent idea */\n confidence: number;\n /** Notes that contribute to this theme */\n source_notes: LatentIdeaSourceNote[];\n /** Cleaned-up version of label for a potential note title */\n suggested_title: string;\n /** Most common folder among source notes */\n suggested_folder: string;\n /** Number of distinct session date-folders (e.g. \"2026/03\") touching this theme */\n sessions_count: number;\n}\n\nexport interface GraphLatentIdeasResult {\n ideas: LatentIdea[];\n total_clusters_analyzed: number;\n /** How many clusters already have a matching note (excluded from results) */\n materialized_count: number;\n}\n\n// ---------------------------------------------------------------------------\n// Materialize params / result\n// ---------------------------------------------------------------------------\n\nexport interface IdeaMaterializeParams {\n idea_label: string;\n /** User-chosen title for the new note */\n title: string;\n /** Vault-relative folder path where the note should be created */\n folder: string;\n /** Vault-relative paths of the source notes to link from the new note */\n source_paths: string[];\n project_id: number;\n}\n\nexport interface IdeaMaterializeResult {\n /** Vault-relative path of the created note */\n vault_path: string;\n /** Generated markdown content */\n content: string;\n /** Number of wikilinks inserted */\n links_created: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: check if a cluster already has a matching note\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true when any existing vault note title closely matches the cluster\n * label — meaning a dedicated note already exists for this topic.\n *\n * Matching strategy (simple, fast, no embeddings needed):\n * 1. Lowercase both sides and split into words.\n * 2. Remove stop words from the label words.\n * 3. If ≥ 60% of the significant label words appear in a note title → match.\n */\n// TITLE_STOP_WORDS imported from utils/stop-words.ts\n\nfunction labelMatchesTitle(label: string, title: string): boolean {\n const labelWords = label\n .toLowerCase()\n .split(/[\\s\\-_/]+/)\n .filter((w) => w.length > 2 && !TITLE_STOP_WORDS.has(w));\n\n if (labelWords.length === 0) return false;\n\n const titleLower = title.toLowerCase();\n const matchCount = labelWords.filter((w) => titleLower.includes(w)).length;\n return matchCount / labelWords.length >= 0.6;\n}\n\n/**\n * Check whether any note indexed in the vault has a title matching the label.\n * Fetches all vault file rows via StorageBackend for efficiency.\n */\nasync function clusterHasMatchingNote(\n backend: StorageBackend,\n label: string,\n notePaths: string[]\n): Promise<boolean> {\n // First check the notes already in the cluster themselves — if any cluster\n // member's title matches the label it IS the index note → materialized.\n const pathSet = new Set(notePaths);\n\n // Fetch all vault files (bounded — vault rarely > 50k notes)\n const rows = await backend.getAllVaultFiles();\n\n for (const row of rows) {\n if (!row.title) continue;\n // Skip notes already counted inside the cluster — they don't count as\n // \"dedicated notes\"; we only skip a cluster if a SEPARATE note exists.\n if (pathSet.has(row.vaultPath)) continue;\n if (labelMatchesTitle(label, row.title)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: generate a clean suggested title\n// ---------------------------------------------------------------------------\n\nfunction toSuggestedTitle(label: string): string {\n // Remove leading/trailing whitespace, capitalize each word, remove stop words\n // that are all-lowercase at the start of the title.\n const words = label\n .trim()\n .split(/\\s+/)\n .map((w, i) => {\n const lower = w.toLowerCase();\n // Drop leading stop words (but keep if they're the only word)\n if (i === 0 && TITLE_STOP_WORDS.has(lower) && label.trim().split(/\\s+/).length > 1) {\n return \"\";\n }\n return w.charAt(0).toUpperCase() + w.slice(1);\n })\n .filter(Boolean);\n\n return words.join(\" \") || label;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: find most common folder\n// ---------------------------------------------------------------------------\n\nfunction mostCommonFolder(vaultPaths: string[]): string {\n const counts = new Map<string, number>();\n for (const p of vaultPaths) {\n const parts = p.split(\"/\");\n const folder = parts.length > 1 ? parts.slice(0, -1).join(\"/\") : \"\";\n counts.set(folder, (counts.get(folder) ?? 0) + 1);\n }\n\n let best = \"\";\n let bestCount = 0;\n for (const [folder, count] of counts) {\n if (count > bestCount) {\n bestCount = count;\n best = folder;\n }\n }\n return best;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: count distinct session date-folders\n// ---------------------------------------------------------------------------\n\n/**\n * Heuristic: vault notes are often stored in date-based folders like\n * \"2026/03/15\" or \"Daily/2026-03\". We extract the first numeric path\n * segment that looks like a year (2020-2030) and group by year+month.\n *\n * Falls back to counting distinct top-level folders.\n */\nfunction countDistinctSessions(vaultPaths: string[]): number {\n const sessions = new Set<string>();\n const yearMonthRe = /\\b(202\\d)\\D?(0[1-9]|1[0-2])\\b/;\n\n for (const p of vaultPaths) {\n const m = yearMonthRe.exec(p);\n if (m) {\n sessions.add(`${m[1]}-${m[2]}`);\n } else {\n // Fallback: use top-level folder as a proxy for \"session bucket\"\n const topFolder = p.split(\"/\")[0];\n sessions.add(topFolder);\n }\n }\n return sessions.size;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: calculate confidence score\n// ---------------------------------------------------------------------------\n\n/**\n * Confidence combines:\n * - Cluster size (normalized, capped at 20 for max contribution)\n * - Folder diversity (0-1 already)\n * - Sessions count (normalized, capped at 5)\n *\n * Formula: 0.4 * sizeScore + 0.35 * folderDiversity + 0.25 * sessionScore\n */\nfunction calcConfidence(\n size: number,\n folderDiversity: number,\n sessionsCount: number\n): number {\n const sizeScore = Math.min(size / 20, 1.0);\n const sessionScore = Math.min(sessionsCount / 5, 1.0);\n const raw = 0.4 * sizeScore + 0.35 * folderDiversity + 0.25 * sessionScore;\n return Math.round(raw * 100) / 100;\n}\n\n// ---------------------------------------------------------------------------\n// Main handler: graph_latent_ideas\n// ---------------------------------------------------------------------------\n\nexport async function handleGraphLatentIdeas(\n backend: StorageBackend,\n params: GraphLatentIdeasParams\n): Promise<GraphLatentIdeasResult> {\n const minClusterSize = params.min_cluster_size ?? 3;\n const maxIdeas = params.max_ideas ?? 15;\n const lookbackDays = params.lookback_days ?? 180;\n const similarityThreshold = params.similarity_threshold ?? 0.65;\n\n const { project_id: vaultProjectId } = params;\n if (!vaultProjectId) {\n throw new Error(\n \"graph_latent_ideas: project_id is required (pass the vault project's numeric ID)\"\n );\n }\n\n // Run the same clustering algorithm used by graph_clusters\n const themeResult = await zettelThemes(backend, {\n vaultProjectId,\n lookbackDays,\n minClusterSize,\n maxThemes: maxIdeas * 3, // Over-fetch — many will be filtered as materialized\n similarityThreshold,\n });\n\n const ideas: LatentIdea[] = [];\n let materializedCount = 0;\n\n for (const theme of themeResult.themes) {\n const notePaths = theme.notes.map((n) => n.path);\n\n // Check if a dedicated note already exists for this theme\n if (await clusterHasMatchingNote(backend, theme.label, notePaths)) {\n materializedCount++;\n continue;\n }\n\n // This is a latent idea — no dedicated note exists yet\n const suggestedFolder = mostCommonFolder(notePaths);\n const sessionsCount = countDistinctSessions(notePaths);\n const confidence = calcConfidence(theme.size, theme.folderDiversity, sessionsCount);\n\n // Build source notes with relevance scores\n // Relevance is approximated by position in cluster (centroid-closest first)\n // zettelThemes returns notes in no guaranteed order; assign uniform relevance\n // decreasing from 1.0 to 0.5 across the list.\n const sourceNotes: LatentIdeaSourceNote[] = theme.notes.map((n, idx) => ({\n vault_path: n.path,\n title: n.title ?? n.path.split(\"/\").pop()?.replace(/\\.md$/i, \"\") ?? n.path,\n relevance: Math.round((1.0 - (idx / Math.max(theme.notes.length - 1, 1)) * 0.5) * 100) / 100,\n }));\n\n ideas.push({\n id: theme.id,\n label: theme.label,\n size: theme.size,\n confidence,\n source_notes: sourceNotes,\n suggested_title: toSuggestedTitle(theme.label),\n suggested_folder: suggestedFolder,\n sessions_count: sessionsCount,\n });\n\n if (ideas.length >= maxIdeas) break;\n }\n\n // Sort by confidence descending\n ideas.sort((a, b) => b.confidence - a.confidence);\n\n return {\n ideas,\n total_clusters_analyzed: themeResult.themes.length + materializedCount,\n materialized_count: materializedCount,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Materialize handler: idea_materialize\n// ---------------------------------------------------------------------------\n\nexport function handleIdeaMaterialize(\n params: IdeaMaterializeParams,\n vaultPath: string\n): IdeaMaterializeResult {\n const { idea_label, title, folder, source_paths } = params;\n\n // Sanitize filename: replace characters illegal in filenames\n const safeTitle = title.replace(/[/\\\\:*?\"<>|]/g, \"-\");\n const fileName = `${safeTitle}.md`;\n\n // Vault-relative path (forward slashes, no leading slash)\n const relFolder = folder.replace(/^\\/+|\\/+$/g, \"\");\n const vault_path = relFolder ? `${relFolder}/${fileName}` : fileName;\n\n // Absolute filesystem path\n const absPath = join(vaultPath, vault_path);\n const absDir = dirname(absPath);\n\n // Build wikilinks from source_paths\n const wikilinks = source_paths\n .map((p) => {\n // Derive a display name: filename without extension\n const name = p.split(\"/\").pop()?.replace(/\\.md$/i, \"\") ?? p;\n // Relative wikilink — use just the filename (Obsidian resolves by title)\n return `- [[${name}]]`;\n })\n .join(\"\\n\");\n\n const links_created = source_paths.length;\n\n const content = [\n `# ${title}`,\n \"\",\n `*Materialized from latent idea: \"${idea_label}\"*`,\n `*Sources: ${links_created} notes*`,\n \"\",\n \"## Related Notes\",\n \"\",\n wikilinks || \"*(no source notes)*\",\n \"\",\n \"## Notes\",\n \"\",\n \"<!-- Add your thoughts about this idea here -->\",\n \"\",\n ].join(\"\\n\");\n\n // Write the file (create parent directories as needed)\n mkdirSync(absDir, { recursive: true });\n writeFileSync(absPath, content, \"utf-8\");\n\n return {\n vault_path,\n content,\n links_created,\n };\n}\n"],"mappings":"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"}
@@ -31,4 +31,4 @@ function applyLinkBoost(results, edges, opts) {
31
31
 
32
32
  //#endregion
33
33
  export { applyLinkBoost };
34
- //# sourceMappingURL=link-boost-BptbAPuv.mjs.map
34
+ //# sourceMappingURL=link-boost-UiE-uooT.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"link-boost-BptbAPuv.mjs","names":[],"sources":["../src/memory/link-boost.ts"],"sourcesContent":["/**\n * Rank search results by how the corpus links to them, not only by similarity.\n *\n * Why this exists: the store holds 33,709 wikilinks that are *facts* — one note\n * pointing at another, written by a person — alongside 2.4M chunks whose only\n * ranking signal is embedding similarity. Similarity answers \"what reads like\n * the query\". It cannot answer \"which of these is the one the others refer\n * back to\", which is usually the note worth reading first.\n *\n * The boost is deliberately query-local: it counts links *between the results\n * themselves*, not global popularity. A note linked by many other notes that\n * also match the query is a hub for that question. A note linked by half the\n * vault is merely popular, which is not the same thing and would flatten every\n * ranking toward the same few index pages.\n *\n * Links cost nothing to maintain — no embedding pass, no model call — so this\n * signal stays correct while the embedding backlog drains, and works for chunks\n * that have no embedding at all.\n */\n\nimport type { SearchResult } from \"./search.js\";\n\n/** A directed link between two note paths, as stored in vault_links. */\nexport interface LinkEdge {\n sourcePath: string;\n targetPath: string;\n}\n\nexport interface LinkBoostOptions {\n /**\n * How much the boost may move a result, as a fraction of its current score.\n * 0.25 means the most-linked result gains 25%. Kept modest by default: the\n * link graph is a supporting signal, and a note nobody links to can still be\n * the right answer.\n */\n weight?: number;\n}\n\n/**\n * Re-rank results by inbound links *from other results in the same set*.\n *\n * Returns a new array, sorted by the adjusted score. Input is not mutated.\n * Results whose paths carry no inbound links are unchanged, so a corpus with\n * no links at all is a no-op rather than a distortion.\n */\nexport function applyLinkBoost(\n results: SearchResult[],\n edges: LinkEdge[],\n opts?: LinkBoostOptions,\n): SearchResult[] {\n const weight = opts?.weight ?? 0.25;\n if (results.length === 0 || edges.length === 0 || weight === 0) {\n return [...results];\n }\n\n // Only links whose BOTH ends are in the result set count. An edge pointing\n // out of the set says nothing about the relative rank of results inside it.\n const paths = new Set(results.map((r) => r.path));\n const inbound = new Map<string, number>();\n for (const e of edges) {\n if (e.sourcePath === e.targetPath) continue; // self-links are noise\n if (!paths.has(e.sourcePath) || !paths.has(e.targetPath)) continue;\n inbound.set(e.targetPath, (inbound.get(e.targetPath) ?? 0) + 1);\n }\n if (inbound.size === 0) return [...results];\n\n // Normalise against the most-linked result so the boost is bounded by\n // `weight` regardless of corpus size. Without this, a densely linked project\n // would swamp similarity entirely while a sparse one would see no effect.\n const maxInbound = Math.max(...inbound.values());\n\n return results\n .map((r) => {\n const links = inbound.get(r.path) ?? 0;\n if (links === 0) return { ...r };\n const factor = 1 + weight * (links / maxInbound);\n return { ...r, score: r.score * factor };\n })\n .sort((a, b) => b.score - a.score);\n}\n"],"mappings":";;;;;;;;AA6CA,SAAgB,eACd,SACA,OACA,MACgB;CAChB,MAAM,SAAS,MAAM,UAAU;AAC/B,KAAI,QAAQ,WAAW,KAAK,MAAM,WAAW,KAAK,WAAW,EAC3D,QAAO,CAAC,GAAG,QAAQ;CAKrB,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CACjD,MAAM,0BAAU,IAAI,KAAqB;AACzC,MAAK,MAAM,KAAK,OAAO;AACrB,MAAI,EAAE,eAAe,EAAE,WAAY;AACnC,MAAI,CAAC,MAAM,IAAI,EAAE,WAAW,IAAI,CAAC,MAAM,IAAI,EAAE,WAAW,CAAE;AAC1D,UAAQ,IAAI,EAAE,aAAa,QAAQ,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE;;AAEjE,KAAI,QAAQ,SAAS,EAAG,QAAO,CAAC,GAAG,QAAQ;CAK3C,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,QAAQ,CAAC;AAEhD,QAAO,QACJ,KAAK,MAAM;EACV,MAAM,QAAQ,QAAQ,IAAI,EAAE,KAAK,IAAI;AACrC,MAAI,UAAU,EAAG,QAAO,EAAE,GAAG,GAAG;EAChC,MAAM,SAAS,IAAI,UAAU,QAAQ;AACrC,SAAO;GAAE,GAAG;GAAG,OAAO,EAAE,QAAQ;GAAQ;GACxC,CACD,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM"}
1
+ {"version":3,"file":"link-boost-UiE-uooT.mjs","names":[],"sources":["../src/memory/link-boost.ts"],"sourcesContent":["/**\n * Rank search results by how the corpus links to them, not only by similarity.\n *\n * Why this exists: the store holds 33,709 wikilinks that are *facts* — one note\n * pointing at another, written by a person — alongside 2.4M chunks whose only\n * ranking signal is embedding similarity. Similarity answers \"what reads like\n * the query\". It cannot answer \"which of these is the one the others refer\n * back to\", which is usually the note worth reading first.\n *\n * The boost is deliberately query-local: it counts links *between the results\n * themselves*, not global popularity. A note linked by many other notes that\n * also match the query is a hub for that question. A note linked by half the\n * vault is merely popular, which is not the same thing and would flatten every\n * ranking toward the same few index pages.\n *\n * Links cost nothing to maintain — no embedding pass, no model call — so this\n * signal stays correct while the embedding backlog drains, and works for chunks\n * that have no embedding at all.\n */\n\nimport type { SearchResult } from \"./search.js\";\n\n/** A directed link between two note paths, as stored in vault_links. */\nexport interface LinkEdge {\n sourcePath: string;\n targetPath: string;\n}\n\nexport interface LinkBoostOptions {\n /**\n * How much the boost may move a result, as a fraction of its current score.\n * 0.25 means the most-linked result gains 25%. Kept modest by default: the\n * link graph is a supporting signal, and a note nobody links to can still be\n * the right answer.\n */\n weight?: number;\n}\n\n/**\n * Re-rank results by inbound links *from other results in the same set*.\n *\n * Returns a new array, sorted by the adjusted score. Input is not mutated.\n * Results whose paths carry no inbound links are unchanged, so a corpus with\n * no links at all is a no-op rather than a distortion.\n */\nexport function applyLinkBoost(\n results: SearchResult[],\n edges: LinkEdge[],\n opts?: LinkBoostOptions,\n): SearchResult[] {\n const weight = opts?.weight ?? 0.25;\n if (results.length === 0 || edges.length === 0 || weight === 0) {\n return [...results];\n }\n\n // Only links whose BOTH ends are in the result set count. An edge pointing\n // out of the set says nothing about the relative rank of results inside it.\n const paths = new Set(results.map((r) => r.path));\n const inbound = new Map<string, number>();\n for (const e of edges) {\n if (e.sourcePath === e.targetPath) continue; // self-links are noise\n if (!paths.has(e.sourcePath) || !paths.has(e.targetPath)) continue;\n inbound.set(e.targetPath, (inbound.get(e.targetPath) ?? 0) + 1);\n }\n if (inbound.size === 0) return [...results];\n\n // Normalise against the most-linked result so the boost is bounded by\n // `weight` regardless of corpus size. Without this, a densely linked project\n // would swamp similarity entirely while a sparse one would see no effect.\n const maxInbound = Math.max(...inbound.values());\n\n return results\n .map((r) => {\n const links = inbound.get(r.path) ?? 0;\n if (links === 0) return { ...r };\n const factor = 1 + weight * (links / maxInbound);\n return { ...r, score: r.score * factor };\n })\n .sort((a, b) => b.score - a.score);\n}\n"],"mappings":";;;;;;;;AA6CA,SAAgB,eACd,SACA,OACA,MACgB;CAChB,MAAM,SAAS,MAAM,UAAU;AAC/B,KAAI,QAAQ,WAAW,KAAK,MAAM,WAAW,KAAK,WAAW,EAC3D,QAAO,CAAC,GAAG,QAAQ;CAKrB,MAAM,QAAQ,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,KAAK,CAAC;CACjD,MAAM,0BAAU,IAAI,KAAqB;AACzC,MAAK,MAAM,KAAK,OAAO;AACrB,MAAI,EAAE,eAAe,EAAE,WAAY;AACnC,MAAI,CAAC,MAAM,IAAI,EAAE,WAAW,IAAI,CAAC,MAAM,IAAI,EAAE,WAAW,CAAE;AAC1D,UAAQ,IAAI,EAAE,aAAa,QAAQ,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE;;AAEjE,KAAI,QAAQ,SAAS,EAAG,QAAO,CAAC,GAAG,QAAQ;CAK3C,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,QAAQ,CAAC;AAEhD,QAAO,QACJ,KAAK,MAAM;EACV,MAAM,QAAQ,QAAQ,IAAI,EAAE,KAAK,IAAI;AACrC,MAAI,UAAU,EAAG,QAAO,EAAE,GAAG,GAAG;EAChC,MAAM,SAAS,IAAI,UAAU,QAAQ;AACrC,SAAO;GAAE,GAAG;GAAG,OAAO,EAAE,QAAQ;GAAQ;GACxC,CACD,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM"}
@@ -1,7 +1,7 @@
1
1
  import { _ as warn, c as ok, g as smartDecodeDir, i as err, n as dim, o as header, u as renderTable } from "./utils-C9HsYDpO.mjs";
2
- import { a as paiHomePath, o as resolvePaiFile, r as migratePaiFile } from "./pai-home-CeOIYltV.mjs";
3
- import { E as readWorkersSection, V as stripModelVariant, n as claudeCommand, t as buildRunEnv } from "./run-env-BE5xyCwy.mjs";
4
- import { a as switchToSession, n as fetchLiveSessions } from "./aibroker-client-BK1h--W_.mjs";
2
+ import { a as paiHomePath, o as resolvePaiFile, r as migratePaiFile } from "./pai-home-Cm9rcJgX.mjs";
3
+ import { E as readWorkersSection, V as stripModelVariant, n as claudeCommand, t as buildRunEnv } from "./run-env-BHWnXqle.mjs";
4
+ import { a as switchToSession, n as fetchLiveSessions } from "./aibroker-client-C5Fw7DNz.mjs";
5
5
  import { closeSync, copyFileSync, createReadStream, existsSync, fstatSync, linkSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
6
6
  import { homedir } from "node:os";
7
7
  import { basename, join } from "node:path";
@@ -67,7 +67,7 @@ function buildClcInfoMap() {
67
67
  /** Build encoded_dir → root_path map from PAI registry (authoritative cwd). */
68
68
  async function buildRegistryRootPathMap() {
69
69
  try {
70
- const { getRegistryBackend } = await import("./factory-UO9_DFMN.mjs");
70
+ const { getRegistryBackend } = await import("./factory-CaswxuJ0.mjs");
71
71
  const projects = await (await getRegistryBackend()).listProjects();
72
72
  const map = /* @__PURE__ */ new Map();
73
73
  for (const p of projects) if (p.encoded_dir) map.set(p.encoded_dir, p.root_path);
@@ -1825,7 +1825,7 @@ async function openMatch(entry, allSessions, dryRun, resume, auto) {
1825
1825
  }
1826
1826
  async function getRegisteredProjects(all = false) {
1827
1827
  try {
1828
- const { getRegistryBackend } = await import("./factory-UO9_DFMN.mjs");
1828
+ const { getRegistryBackend } = await import("./factory-CaswxuJ0.mjs");
1829
1829
  return await (await getRegistryBackend()).listProjectsWithSessionStats(all ? {} : { status: "active" });
1830
1830
  } catch {
1831
1831
  return [];
@@ -2008,4 +2008,4 @@ async function cmdMain(query, pickN, opts) {
2008
2008
 
2009
2009
  //#endregion
2010
2010
  export { resolveSessionByNameOrId as _, buildDeduped as a, hasConversation as c, providerResumePlan as d, restoreTopLevel as f, migrateSessionScanCache as g, fmtAge as h, resumeTargetFor as i, launchInDir as l, printExitDir as m, resolveSessionDir as n, normalizeName as o, resumeArgvText as p, resumeCandidateFor as r, renderDedupedSessions as s, cmdMain as t, launchPrompt as u, scanCacheFilePath as v, scanSessions as y };
2011
- //# sourceMappingURL=main-resolver-BIft_FVF.mjs.map
2011
+ //# sourceMappingURL=main-resolver-CDe7DCso.mjs.map