agentix-cli 0.26.0 → 0.28.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 (300) hide show
  1. package/README.md +67 -118
  2. package/apps/mac-helper/Sources/AgentXHelper/AXTree.swift +159 -0
  3. package/apps/mac-helper/Sources/AgentXHelper/Brand.swift +63 -0
  4. package/apps/mac-helper/Sources/AgentXHelper/Focus.swift +97 -0
  5. package/apps/mac-helper/Sources/AgentXHelper/HUD.swift +179 -0
  6. package/apps/mac-helper/Sources/AgentXHelper/OCR.swift +117 -0
  7. package/apps/mac-helper/Sources/AgentXHelper/Pointer.swift +309 -0
  8. package/apps/mac-helper/Sources/AgentXHelper/Screens.swift +67 -0
  9. package/apps/mac-helper/Sources/AgentXHelper/Typer.swift +185 -0
  10. package/apps/mac-helper/Sources/AgentXHelper/Vision.swift +201 -0
  11. package/apps/mac-helper/Sources/AgentXHelper/main.swift +265 -0
  12. package/apps/mac-helper/build.sh +39 -0
  13. package/apps/mac-voice/Resources/Info.plist +18 -0
  14. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +108 -0
  15. package/apps/mac-voice/Sources/AgentXVoice/Brand.swift +123 -0
  16. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +68 -0
  17. package/apps/mac-voice/Sources/AgentXVoice/Hold.swift +74 -0
  18. package/apps/mac-voice/Sources/AgentXVoice/Hotkey.swift +76 -0
  19. package/apps/mac-voice/Sources/AgentXVoice/Markdown.swift +140 -0
  20. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +275 -0
  21. package/apps/mac-voice/Sources/AgentXVoice/Progress.swift +147 -0
  22. package/apps/mac-voice/Sources/AgentXVoice/Recorder.swift +139 -0
  23. package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +162 -0
  24. package/apps/mac-voice/Sources/AgentXVoice/SmartPaste.swift +72 -0
  25. package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +192 -0
  26. package/apps/mac-voice/Sources/AgentXVoice/StepLabel.swift +159 -0
  27. package/apps/mac-voice/Sources/AgentXVoice/main.swift +380 -0
  28. package/apps/mac-voice/build.sh +32 -0
  29. package/dist/{App-BPNHIGUX.js → App-CKPH3HHF.js} +2 -2
  30. package/dist/{ChatApp-6XSSZB4U.js → ChatApp-MX7IG4K2.js} +2 -2
  31. package/dist/agent-2U574KBZ.js +2 -0
  32. package/dist/article-fields-L4D2BOGY.js +2 -0
  33. package/dist/article-quality-44ONVGLN.js +3 -0
  34. package/dist/article-quality-44ONVGLN.js.map +1 -0
  35. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  36. package/dist/backfill-MTD2B62I.js +6 -0
  37. package/dist/backfill-MTD2B62I.js.map +1 -0
  38. package/dist/{board-dashboard-M3372FLO.js → board-dashboard-QR7CVTKA.js} +1411 -453
  39. package/dist/board-dashboard-QR7CVTKA.js.map +1 -0
  40. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  41. package/dist/calibration-ILS5OI44.js +2 -0
  42. package/dist/{chunk-V3U4OF7L.js → chunk-3LYM22IV.js} +46 -48
  43. package/dist/chunk-3LYM22IV.js.map +1 -0
  44. package/dist/chunk-3SCPRYJB.js +5 -0
  45. package/dist/chunk-3SCPRYJB.js.map +1 -0
  46. package/dist/chunk-4BR7D2OU.js +36 -0
  47. package/dist/chunk-4BR7D2OU.js.map +1 -0
  48. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  49. package/dist/chunk-52ZPELD2.js.map +1 -0
  50. package/dist/chunk-5IA2CYHN.js +5 -0
  51. package/dist/{chunk-O6YPEOQF.js.map → chunk-5IA2CYHN.js.map} +1 -1
  52. package/dist/chunk-64CAIB3C.js +9 -0
  53. package/dist/chunk-64CAIB3C.js.map +1 -0
  54. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  55. package/dist/chunk-77OV7346.js.map +1 -0
  56. package/dist/chunk-AJ5RMU7X.js +2 -0
  57. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  58. package/dist/chunk-AJONZWLT.js +2 -0
  59. package/dist/chunk-AJONZWLT.js.map +1 -0
  60. package/dist/chunk-BH3GTCZ6.js +2 -0
  61. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  62. package/dist/chunk-E3UJHQGN.js +1189 -0
  63. package/dist/chunk-E3UJHQGN.js.map +1 -0
  64. package/dist/chunk-EVWEG3VD.js +3 -0
  65. package/dist/chunk-EVWEG3VD.js.map +1 -0
  66. package/dist/chunk-F4HZTZHW.js +2 -0
  67. package/dist/chunk-F4HZTZHW.js.map +1 -0
  68. package/dist/chunk-F6CNF7V3.js +25 -0
  69. package/dist/chunk-F6CNF7V3.js.map +1 -0
  70. package/dist/chunk-FIOB3MEX.js +2 -0
  71. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  72. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  73. package/dist/{chunk-BVGJSPW4.js → chunk-G2HMRDUQ.js} +7 -7
  74. package/dist/chunk-G2HMRDUQ.js.map +1 -0
  75. package/dist/chunk-H2QQU2XV.js +109 -0
  76. package/dist/chunk-H2QQU2XV.js.map +1 -0
  77. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  78. package/dist/chunk-IWMW5432.js.map +1 -0
  79. package/dist/chunk-K33GIBSE.js +2 -0
  80. package/dist/chunk-K33GIBSE.js.map +1 -0
  81. package/dist/chunk-L2WSPY6S.js +2 -0
  82. package/dist/chunk-L2WSPY6S.js.map +1 -0
  83. package/dist/chunk-LCIREYTD.js +54 -0
  84. package/dist/chunk-LCIREYTD.js.map +1 -0
  85. package/dist/chunk-MHIMGM4G.js +126 -0
  86. package/dist/chunk-MHIMGM4G.js.map +1 -0
  87. package/dist/chunk-P5ZBBEPL.js +13 -0
  88. package/dist/chunk-P5ZBBEPL.js.map +1 -0
  89. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  90. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  91. package/dist/chunk-QHTQPBHU.js +45 -0
  92. package/dist/chunk-QHTQPBHU.js.map +1 -0
  93. package/dist/chunk-S2MNL5V6.js +2 -0
  94. package/dist/chunk-S2MNL5V6.js.map +1 -0
  95. package/dist/chunk-SCTIUFL4.js +5 -0
  96. package/dist/{chunk-K2CAPYES.js.map → chunk-SCTIUFL4.js.map} +1 -1
  97. package/dist/chunk-ULLAE6LE.js +157 -0
  98. package/dist/chunk-ULLAE6LE.js.map +1 -0
  99. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  100. package/dist/chunk-VDMYBPYB.js.map +1 -0
  101. package/dist/chunk-VIH7ARPF.js +547 -0
  102. package/dist/chunk-VIH7ARPF.js.map +1 -0
  103. package/dist/chunk-W5C2GDNC.js +32 -0
  104. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  105. package/dist/chunk-WPMX3VGI.js +3 -0
  106. package/dist/chunk-WPMX3VGI.js.map +1 -0
  107. package/dist/chunk-YLBERT7N.js +24 -0
  108. package/dist/chunk-YLBERT7N.js.map +1 -0
  109. package/dist/chunk-YLGIZRHV.js +114 -0
  110. package/dist/chunk-YLGIZRHV.js.map +1 -0
  111. package/dist/chunk-ZMYGXHPT.js +121 -0
  112. package/dist/chunk-ZMYGXHPT.js.map +1 -0
  113. package/dist/chunk-ZTEU5JVQ.js +2 -0
  114. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  115. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  116. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  117. package/dist/cli.js +1 -1
  118. package/dist/cli.js.map +1 -1
  119. package/dist/client-ZLSXGPTV.js +2 -0
  120. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  121. package/dist/config-HMW3EBZL.js +2 -0
  122. package/dist/config-mutator-4SZ5YEVD.js +2 -0
  123. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  124. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  125. package/dist/context-planner-AQHJMVY2.js +17 -0
  126. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  127. package/dist/debug-3MEUO2P2.js +2 -0
  128. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  129. package/dist/discover-D4VVRNAI.js.map +1 -0
  130. package/dist/doctor-LRYV3QLK.js +2 -0
  131. package/dist/editor-chat-OB7IQOB5.js +137 -0
  132. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  133. package/dist/entry-triage-LK34TSQD.js +2 -0
  134. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  135. package/dist/facts-YVEJOM7O.js +22 -0
  136. package/dist/facts-YVEJOM7O.js.map +1 -0
  137. package/dist/heal-FVLBHYP7.js +2 -0
  138. package/dist/index.d.ts +1040 -64
  139. package/dist/index.js +12 -19
  140. package/dist/index.js.map +1 -1
  141. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  142. package/dist/loader-4ZGTK7LD.js +2 -0
  143. package/dist/loader-BOKE5M5S.js +2 -0
  144. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  145. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  146. package/dist/ntfy-X7EPB3TG.js +4 -0
  147. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  148. package/dist/processes-WU23U2OJ.js +2 -0
  149. package/dist/program-GSZ5AYAM.js +2 -0
  150. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  151. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  152. package/dist/providers-KO2DODIO.js +2 -0
  153. package/dist/query-SQDHE4HV.js +2 -0
  154. package/dist/questions-4B4KSPXG.js +3 -0
  155. package/dist/questions-4B4KSPXG.js.map +1 -0
  156. package/dist/recipes-7S4MC5OJ.js +2 -0
  157. package/dist/registry-LE6FWYZC.js +2 -0
  158. package/dist/review-candidates-RQU5KNZM.js +4 -0
  159. package/dist/review-candidates-RQU5KNZM.js.map +1 -0
  160. package/dist/review-store-B4YE3EFK.js +4 -0
  161. package/dist/review-store-B4YE3EFK.js.map +1 -0
  162. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  163. package/dist/routing-ILERJXB6.js +2 -0
  164. package/dist/runner-YRBVNYTY.js +2 -0
  165. package/dist/scheduler-BFM3EINS.js +2 -0
  166. package/dist/seat-7WM7II2B.js +2 -0
  167. package/dist/select-X4KVMOP5.js +2 -0
  168. package/dist/select-X4KVMOP5.js.map +1 -0
  169. package/dist/store-MCOA6N4F.js +2 -0
  170. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  171. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  172. package/dist/timers-PCB4ZIBD.js +2 -0
  173. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  174. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  175. package/dist/types-H5ENIUIT.js +2 -0
  176. package/dist/types-OIFU3KSQ.js +2 -0
  177. package/dist/types-OIFU3KSQ.js.map +1 -0
  178. package/dist/web/workflow-editor.global.js +1542 -0
  179. package/dist/web/workflow-editor.global.js.map +1 -0
  180. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  181. package/dist/wiki-2AUFSIKO.js +2 -0
  182. package/dist/wiki-2AUFSIKO.js.map +1 -0
  183. package/dist/workflows/templates/branching.yaml +1 -1
  184. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  185. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  186. package/package.json +14 -3
  187. package/scripts/postinstall.mjs +80 -0
  188. package/dist/agent-5QMITZT6.js +0 -2
  189. package/dist/architect-SEPWMWS4.js +0 -2
  190. package/dist/board-dashboard-M3372FLO.js.map +0 -1
  191. package/dist/chunk-2WVX3LEC.js.map +0 -1
  192. package/dist/chunk-3WQFCIB6.js +0 -24
  193. package/dist/chunk-3WQFCIB6.js.map +0 -1
  194. package/dist/chunk-4YCH6IZV.js +0 -2
  195. package/dist/chunk-53NRX2NA.js +0 -2
  196. package/dist/chunk-53NRX2NA.js.map +0 -1
  197. package/dist/chunk-5U35WAZF.js.map +0 -1
  198. package/dist/chunk-BFXONUUN.js +0 -108
  199. package/dist/chunk-BFXONUUN.js.map +0 -1
  200. package/dist/chunk-BFZUG66X.js +0 -45
  201. package/dist/chunk-BFZUG66X.js.map +0 -1
  202. package/dist/chunk-BTHHSY7D.js +0 -13
  203. package/dist/chunk-BTHHSY7D.js.map +0 -1
  204. package/dist/chunk-BVGJSPW4.js.map +0 -1
  205. package/dist/chunk-D46VZ7K7.js +0 -116
  206. package/dist/chunk-D46VZ7K7.js.map +0 -1
  207. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  208. package/dist/chunk-E5CXCI33.js +0 -155
  209. package/dist/chunk-E5CXCI33.js.map +0 -1
  210. package/dist/chunk-FDWOECJY.js +0 -54
  211. package/dist/chunk-FDWOECJY.js.map +0 -1
  212. package/dist/chunk-H5D7ATXS.js.map +0 -1
  213. package/dist/chunk-HR4JSRZS.js +0 -3
  214. package/dist/chunk-HR4JSRZS.js.map +0 -1
  215. package/dist/chunk-ISEBLHWX.js +0 -118
  216. package/dist/chunk-ISEBLHWX.js.map +0 -1
  217. package/dist/chunk-JP5IEXR7.js +0 -32
  218. package/dist/chunk-JTJIPNXY.js.map +0 -1
  219. package/dist/chunk-K2CAPYES.js +0 -5
  220. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  221. package/dist/chunk-METYYZKQ.js +0 -1115
  222. package/dist/chunk-METYYZKQ.js.map +0 -1
  223. package/dist/chunk-MTAPOCGA.js +0 -47
  224. package/dist/chunk-MTAPOCGA.js.map +0 -1
  225. package/dist/chunk-O6YPEOQF.js +0 -5
  226. package/dist/chunk-P4VWIVKN.js.map +0 -1
  227. package/dist/chunk-SR3X25TN.js +0 -3
  228. package/dist/chunk-SR3X25TN.js.map +0 -1
  229. package/dist/chunk-UHE5FCPB.js +0 -431
  230. package/dist/chunk-UHE5FCPB.js.map +0 -1
  231. package/dist/chunk-V3U4OF7L.js.map +0 -1
  232. package/dist/chunk-VJUTQAPJ.js +0 -2
  233. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  234. package/dist/chunk-VT5XYFDD.js +0 -126
  235. package/dist/chunk-VT5XYFDD.js.map +0 -1
  236. package/dist/client-HGKAC6VP.js +0 -2
  237. package/dist/config-A4RWEJHV.js +0 -2
  238. package/dist/config-mutator-MNSFUN7D.js +0 -2
  239. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  240. package/dist/context-planner-45GEWKGL.js +0 -17
  241. package/dist/context-planner-45GEWKGL.js.map +0 -1
  242. package/dist/debug-N3B5NVJU.js +0 -2
  243. package/dist/discover-XJXQXXIO.js.map +0 -1
  244. package/dist/doctor-3Q36ZFE6.js +0 -2
  245. package/dist/heal-HZ3EQD52.js +0 -2
  246. package/dist/loader-5CA3O5GM.js +0 -2
  247. package/dist/loader-6IHUCPVO.js +0 -2
  248. package/dist/memory-extract-AENFVVO5.js +0 -2
  249. package/dist/processes-UIC4XXA6.js +0 -2
  250. package/dist/program-SAUNRCCD.js +0 -2
  251. package/dist/providers-L27BJ5A6.js +0 -2
  252. package/dist/query-HZYYRCLU.js +0 -2
  253. package/dist/recipes-EPLE2SNT.js +0 -2
  254. package/dist/registry-HK3WQ7BS.js +0 -2
  255. package/dist/runner-BFYDWPUO.js +0 -2
  256. package/dist/scheduler-6T2SU5ST.js +0 -2
  257. package/dist/store-I3HEWVLQ.js +0 -2
  258. package/dist/timers-5LPZN55J.js +0 -2
  259. package/dist/types-VECTMYTO.js +0 -2
  260. package/dist/types-Y6ZCZ52Z.js +0 -2
  261. package/dist/wiki-M6YNRFC4.js +0 -2
  262. /package/dist/{App-BPNHIGUX.js.map → App-CKPH3HHF.js.map} +0 -0
  263. /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-MX7IG4K2.js.map} +0 -0
  264. /package/dist/{agent-5QMITZT6.js.map → agent-2U574KBZ.js.map} +0 -0
  265. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  266. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  267. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  268. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  269. /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
  270. /package/dist/{config-A4RWEJHV.js.map → client-ZLSXGPTV.js.map} +0 -0
  271. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  272. /package/dist/{config-mutator-MNSFUN7D.js.map → config-HMW3EBZL.js.map} +0 -0
  273. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-4SZ5YEVD.js.map} +0 -0
  274. /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
  275. /package/dist/{heal-HZ3EQD52.js.map → doctor-LRYV3QLK.js.map} +0 -0
  276. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  277. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  278. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  279. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  280. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  281. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  282. /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
  283. /package/dist/{providers-L27BJ5A6.js.map → program-GSZ5AYAM.js.map} +0 -0
  284. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  285. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  286. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  287. /package/dist/{recipes-EPLE2SNT.js.map → query-SQDHE4HV.js.map} +0 -0
  288. /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  289. /package/dist/{runner-BFYDWPUO.js.map → registry-LE6FWYZC.js.map} +0 -0
  290. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  291. /package/dist/{scheduler-6T2SU5ST.js.map → routing-ILERJXB6.js.map} +0 -0
  292. /package/dist/{store-I3HEWVLQ.js.map → runner-YRBVNYTY.js.map} +0 -0
  293. /package/dist/{surface-usage-PRL6Z4U3.js.map → scheduler-BFM3EINS.js.map} +0 -0
  294. /package/dist/{timers-5LPZN55J.js.map → seat-7WM7II2B.js.map} +0 -0
  295. /package/dist/{types-VECTMYTO.js.map → store-MCOA6N4F.js.map} +0 -0
  296. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  297. /package/dist/{types-Y6ZCZ52Z.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  298. /package/dist/{wiki-M6YNRFC4.js.map → timers-PCB4ZIBD.js.map} +0 -0
  299. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-H5ENIUIT.js.map} +0 -0
  300. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph/starter-schema.ts","../src/graph/store.ts","../src/graph/classifier.ts","../src/wiki/store.ts","../src/wiki/hub.ts"],"sourcesContent":["import type { GraphSchema } from \"./types\"\n\n/**\n * Ships with the install. When .agentx/graph/schema.json is missing,\n * the store seeds this so a fresh daemon has something to classify\n * against.\n *\n * v2 — Phase 1 of the classifier-retire plan: verb-level intent\n * taxonomy. The classifier now answers ONLY \"what kind of work is\n * this\" — it does not embed client/project/agent in the leaf because\n * those already live on the event metadata (project, subject,\n * agentId). One copy of \"review.merge-request\" instead of one per\n * client.\n *\n * Two levels:\n * category — broad work area (code/ops/support/admin/knowledge/social/system)\n * verb — specific intent (review.merge-request, deploy.staging, etc.)\n *\n * The category is closed-set; the verb is open-set so the operator\n * can add new ones via `agentx graph review` over time. Most paths\n * will reuse the seeded verbs below.\n *\n * Why so few levels: every level is information the classifier\n * actually has to decide. The previous v1 schema (scope > location >\n * org > unit > activity) re-encoded data already on the event —\n * forcing a 5-LLM-question prompt to recover what we already knew.\n */\nexport const STARTER_SCHEMA: GraphSchema = {\n version: 2,\n levels: [\n {\n id: \"category\",\n description: \"Top-level kind of work. Closed set — pick the closest fit.\",\n axes: [\n {\n name: \"kind\",\n type: \"enum\",\n values: [\n \"code\", // anything code-related: review, fix, implement, refactor\n \"ops\", // infra: deploy, monitor, rollback, scale, incident\n \"support\", // human-facing: answer question, triage, route\n \"admin\", // governance: config change, scheduling, token, role\n \"knowledge\", // wiki, docs, research, summarize\n \"social\", // marketing, comms, brief, content\n \"system\", // internal infrastructure (rare; mostly classifier itself)\n ],\n description: \"Which category this work belongs in.\",\n },\n ],\n },\n {\n id: \"verb\",\n description: \"Specific intent — verb-level only, NOT client/project-bound.\",\n axes: [\n {\n name: \"name\",\n type: \"free\",\n description:\n \"Lower-kebab dot-namespaced verb. Examples: review.merge-request, fix.bug, \" +\n \"investigate.error, deploy.staging, deploy.production, rollback.release, \" +\n \"spec.feature, triage.issue, document.feature, chat.greeting, \" +\n \"chat.support-request, schedule.add, config.change, ingest.contact, \" +\n \"transcribe.audio, brief.daily, summarize.thread, plan.work.\",\n },\n ],\n },\n ],\n leafInput: {\n name: \"input\",\n type: \"enum\",\n values: [\n \"telegram\",\n \"whatsapp\",\n \"slack\",\n \"discord\",\n \"gitlab-mr\",\n \"gitlab-issue\",\n \"github-issue\",\n \"github-pr\",\n \"cron\",\n \"webhook\",\n \"mesh\",\n \"http\",\n ],\n description: \"Where the message entered the system.\",\n },\n leafOutput: {\n name: \"output\",\n type: \"free\",\n description:\n \"One short sentence describing what the agent is expected to produce. \" +\n \"Generic — refers to the verb's outcome, not the specific client/project \" +\n \"(those are on the event metadata already).\",\n },\n}\n\n/** Pre-seeded verbs shipped with v2. The classifier picks from these\n * first; new verbs go through the review queue before they're\n * committed (so the taxonomy doesn't drift). Adding to this list is\n * cheap — the seed runs once when nodes.json is empty. */\nexport const STARTER_VERB_NODES: Array<{\n category: \"code\" | \"ops\" | \"support\" | \"admin\" | \"knowledge\" | \"social\" | \"system\"\n verb: string\n description?: string\n}> = [\n // code\n { category: \"code\", verb: \"review.merge-request\", description: \"Review a GitLab MR or GitHub PR\" },\n { category: \"code\", verb: \"review.code-change\", description: \"General code review (snippet, diff)\" },\n { category: \"code\", verb: \"fix.bug\", description: \"Fix a reported bug\" },\n { category: \"code\", verb: \"implement.feature\", description: \"Build a new feature\" },\n { category: \"code\", verb: \"investigate.error\", description: \"Investigate a failing test, exception, pipeline\" },\n { category: \"code\", verb: \"refactor.code\", description: \"Restructure existing code without behaviour change\" },\n { category: \"code\", verb: \"spec.feature\", description: \"Write or update a feature spec / RFC\" },\n { category: \"code\", verb: \"triage.issue\", description: \"Classify, label, or assign an issue\" },\n { category: \"code\", verb: \"answer.code-question\", description: \"Answer a code-related question\" },\n\n // ops\n { category: \"ops\", verb: \"deploy.staging\", description: \"Deploy to a staging/preview environment\" },\n { category: \"ops\", verb: \"deploy.production\", description: \"Deploy to production\" },\n { category: \"ops\", verb: \"rollback.release\", description: \"Roll back a release\" },\n { category: \"ops\", verb: \"investigate.incident\", description: \"Investigate a production incident\" },\n { category: \"ops\", verb: \"monitor.system\", description: \"Check system status / metrics\" },\n { category: \"ops\", verb: \"rotate.credential\", description: \"Rotate a token, key, certificate\" },\n { category: \"ops\", verb: \"audit.security\", description: \"Review for security issues\" },\n\n // support\n { category: \"support\", verb: \"chat.greeting\", description: \"A casual hello, no actionable request\" },\n { category: \"support\", verb: \"chat.support-request\", description: \"End-user / customer asking for help\" },\n { category: \"support\", verb: \"chat.casual\", description: \"Informal back-and-forth chat\" },\n { category: \"support\", verb: \"answer.question\", description: \"Answer an open-ended question\" },\n { category: \"support\", verb: \"route.request\", description: \"Forward a request to the right agent\" },\n\n // admin\n { category: \"admin\", verb: \"config.change\", description: \"Change agentx.json / environment / config\" },\n { category: \"admin\", verb: \"schedule.add\", description: \"Add a new cron / scheduled task\" },\n { category: \"admin\", verb: \"schedule.remove\", description: \"Remove or disable a schedule\" },\n { category: \"admin\", verb: \"token.create\", description: \"Mint a scoped API token\" },\n { category: \"admin\", verb: \"agent.add\", description: \"Configure a new agent\" },\n { category: \"admin\", verb: \"channel.add\", description: \"Wire up a new channel\" },\n\n // knowledge\n { category: \"knowledge\", verb: \"document.feature\", description: \"Write or update documentation\" },\n { category: \"knowledge\", verb: \"summarize.thread\", description: \"Summarize a long conversation/thread\" },\n { category: \"knowledge\", verb: \"research.topic\", description: \"Investigate / gather info on a topic\" },\n { category: \"knowledge\", verb: \"wiki.absorb\", description: \"Roll raw entries into typed wiki articles\" },\n\n // social\n { category: \"social\", verb: \"brief.daily\", description: \"Compose a daily brief / digest\" },\n { category: \"social\", verb: \"draft.post\", description: \"Draft a marketing post / announcement\" },\n { category: \"social\", verb: \"report.weekly\", description: \"Compose a weekly status report\" },\n\n // system (the classifier itself; rare to expose)\n { category: \"system\", verb: \"classify.intent\", description: \"Internal: graph-classifier sub-call\" },\n]\n\n","import {\n readFileSync,\n writeFileSync,\n appendFileSync,\n existsSync,\n mkdirSync,\n renameSync,\n} from \"fs\"\nimport { resolve, dirname } from \"path\"\nimport { createHash } from \"crypto\"\nimport {\n graphSchemaSchema,\n nodesFileSchema,\n indexFileSchema,\n classificationSchema,\n type GraphSchema,\n type GraphNode,\n type NodesFile,\n type IndexFile,\n type Classification,\n type FingerprintEntry,\n} from \"./types\"\nimport { STARTER_SCHEMA, STARTER_VERB_NODES } from \"./starter-schema\"\n\n// --- Intent Knowledge Graph filesystem store ---\n//\n// Mirrors the .agentx/wiki pattern: plain JSON on disk, one directory, no DB.\n// Not thread-safe across processes — writes go through safeWrite (tmp + rename).\n\nexport interface GraphStoreOptions {\n baseDir?: string\n log?: (...args: unknown[]) => void\n}\n\nexport class GraphStore {\n readonly baseDir: string\n private log: (...args: unknown[]) => void\n\n constructor(opts: GraphStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/graph\")\n this.log = opts.log ?? console.error.bind(console, \"[graph]\")\n mkdirSync(this.baseDir, { recursive: true })\n }\n\n // --- Paths ---\n\n schemaPath(): string { return resolve(this.baseDir, \"schema.json\") }\n nodesPath(): string { return resolve(this.baseDir, \"nodes.json\") }\n indexPath(): string { return resolve(this.baseDir, \"index.json\") }\n classificationsPath(): string { return resolve(this.baseDir, \"classifications.jsonl\") }\n\n // --- Schema ---\n\n /** Load the graph schema, seeding STARTER_SCHEMA if the file is missing.\n * Phase 1 of the classifier-retire plan ships a v2 verb-level schema;\n * installs with the v1 hierarchical schema (scope > org > unit >\n * activity) get auto-migrated on next load: the v1 file is renamed\n * to schema.v1.bak.json, the v2 starter is written, and the\n * per-event nodes.json + classifications.jsonl are left alone for\n * historical reading. New events classify under v2 from then on. */\n loadSchema(): GraphSchema {\n const p = this.schemaPath()\n if (!existsSync(p)) {\n this.saveSchema(STARTER_SCHEMA)\n this.log(\"seeded starter schema at\", p)\n return STARTER_SCHEMA\n }\n const raw = readFileSync(p, \"utf-8\")\n const parsed = graphSchemaSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n if ((parsed.data.version ?? 1) < (STARTER_SCHEMA.version ?? 2)) {\n const backup = p.replace(/\\.json$/, `.v${parsed.data.version ?? 1}.bak.json`)\n try {\n writeFileSync(backup, raw)\n this.log(`migrated graph schema v${parsed.data.version ?? 1} -> v${STARTER_SCHEMA.version}; old saved at ${backup}`)\n } catch (e: any) {\n this.log(`could not back up old schema: ${e?.message ?? e}`)\n }\n this.saveSchema(STARTER_SCHEMA)\n // Same treatment for nodes.json — the v1 nodes were parented under\n // levels that don't exist in v2 and would fail validation. Archive\n // them and re-seed.\n const np = this.nodesPath()\n if (existsSync(np)) {\n const nbackup = np.replace(/\\.json$/, `.v1.bak.json`)\n try {\n writeFileSync(nbackup, readFileSync(np, \"utf-8\"))\n this.log(`archived v1 nodes.json -> ${nbackup}`)\n } catch (e: any) {\n this.log(`could not back up old nodes: ${e?.message ?? e}`)\n }\n try {\n const seeded = seedNodesFile()\n this.saveNodes(seeded)\n } catch (e: any) {\n this.log(`could not seed v2 nodes: ${e?.message ?? e}`)\n }\n }\n return STARTER_SCHEMA\n }\n return parsed.data\n }\n\n saveSchema(schema: GraphSchema): void {\n const parsed = graphSchemaSchema.safeParse(schema)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid schema: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n safeWriteJson(this.schemaPath(), parsed.data)\n }\n\n // --- Nodes ---\n\n loadNodes(): NodesFile {\n const p = this.nodesPath()\n if (!existsSync(p)) {\n // Seed the verb-level taxonomy on first load. Lets the classifier\n // pick from a stable set of common verbs out of the box, instead of\n // inventing slightly-different variants for every event.\n const seeded = seedNodesFile()\n this.saveNodes(seeded)\n return seeded\n }\n const raw = readFileSync(p, \"utf-8\")\n const parsed = nodesFileSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n return parsed.data\n }\n\n saveNodes(file: NodesFile): void {\n const parsed = nodesFileSchema.safeParse(file)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid nodes file: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n safeWriteJson(this.nodesPath(), parsed.data)\n }\n\n /** Validate a node against the schema AND uniqueness within the nodes file. */\n validateNode(node: GraphNode, schema: GraphSchema, all: GraphNode[]): void {\n const level = schema.levels.find((l) => l.id === node.level)\n if (!level) throw new Error(`Unknown level: \"${node.level}\"`)\n\n const missing = level.axes\n .filter((a) => !a.optional)\n .filter((a) => !(a.name in node.axes))\n if (missing.length) {\n throw new Error(\n `Node ${node.id} (${node.level}) missing required axes: ${missing.map((a) => a.name).join(\", \")}`,\n )\n }\n\n for (const a of level.axes) {\n const v = node.axes[a.name]\n if (v == null) continue\n if (a.type === \"enum\" && a.values && !a.values.includes(v)) {\n throw new Error(`Node ${node.id}: axis ${a.name}=\"${v}\" not in enum ${JSON.stringify(a.values)}`)\n }\n if (a.type === \"ref\") {\n if (!all.some((n) => n.id === v)) {\n throw new Error(`Node ${node.id}: axis ${a.name}=\"${v}\" refers to unknown node`)\n }\n if (a.refLevel) {\n const target = all.find((n) => n.id === v)!\n if (target.level !== a.refLevel) {\n throw new Error(\n `Node ${node.id}: axis ${a.name} expected level \"${a.refLevel}\" but got \"${target.level}\"`,\n )\n }\n }\n }\n }\n\n const levelIdx = schema.levels.findIndex((l) => l.id === node.level)\n if (levelIdx === 0) {\n if (node.parentId !== null) {\n throw new Error(`Root-level node ${node.id} must have parentId: null`)\n }\n } else {\n if (!node.parentId) {\n throw new Error(`Non-root node ${node.id} must have a parentId`)\n }\n const parent = all.find((n) => n.id === node.parentId)\n if (!parent) throw new Error(`Node ${node.id}: parent \"${node.parentId}\" not found`)\n const parentLevelIdx = schema.levels.findIndex((l) => l.id === parent.level)\n // Parent may be at any higher level — intermediate levels can be skipped\n // for orgs that don't use them (e.g. remote-first: no location node).\n if (parentLevelIdx < 0 || parentLevelIdx >= levelIdx) {\n throw new Error(\n `Node ${node.id} at level \"${node.level}\" has parent at level \"${parent.level}\" — parent must be at a higher level`,\n )\n }\n }\n }\n\n /**\n * Commit a full classification path: for each path element that doesn't\n * yet exist, infer its schema level by matching the proposed axes against\n * the level's required axes (first match ≥ cursor wins — this handles\n * paths that skip levels, like business → acme where location is\n * absent). Refreshes the node list between adds so callers in a loop\n * don't hit \"Node id already exists\" on the second iteration.\n */\n commitNodesAlongPath(\n path: string[],\n proposedAxes: Record<string, Record<string, string>>,\n schema: GraphSchema,\n createdBy?: string,\n ): { added: GraphNode[]; skipped: string[] } {\n const added: GraphNode[] = []\n const skipped: string[] = []\n const now = new Date().toISOString()\n\n for (let i = 0; i < path.length; i++) {\n const id = path[i]\n // Load fresh each iteration — cheap O(N) file read, avoids stale snapshots.\n const current = this.loadNodes().nodes\n const existing = current.find((n) => n.id === id)\n if (existing) {\n skipped.push(id)\n continue\n }\n\n // Cursor: how deep the schema-levels we've already filled up to.\n let cursor = 0\n // Scan backwards through path[0..i-1] to find the deepest already-committed\n // or just-added node and resume after its level.\n for (let j = i - 1; j >= 0; j--) {\n const priorId = path[j]\n const priorNode = current.find((n) => n.id === priorId) || added.find((n) => n.id === priorId)\n if (priorNode) {\n cursor = schema.levels.findIndex((l) => l.id === priorNode.level) + 1\n break\n }\n }\n\n const axes = proposedAxes[id] ?? {}\n // Find the first level at or after the cursor whose required axes are\n // satisfied by proposedAxes[id]. \"Required\" here = listed in level.axes;\n // we accept a partial match (axes present in proposal, even if some\n // schema axes are missing — validateNode will catch truly broken ones).\n let chosenLevel: string | null = null\n for (let j = cursor; j < schema.levels.length; j++) {\n const level = schema.levels[j]\n const missing = level.axes\n .filter((a) => !a.optional)\n .filter((a) => !(a.name in axes))\n if (missing.length === 0) {\n chosenLevel = level.id\n cursor = j + 1\n break\n }\n }\n // Fallback: if no level's axes fully match, try the NEXT level after\n // the cursor anyway. Better to commit at the likely level and let\n // validateNode reject on write than to silently drop the whole path.\n if (!chosenLevel && cursor < schema.levels.length) {\n chosenLevel = schema.levels[cursor].id\n cursor++\n }\n if (!chosenLevel) {\n throw new Error(`Can't place node ${id} — no schema level left (cursor=${cursor}, levels=${schema.levels.length})`)\n }\n\n // Parent is the DEEPEST already-placed node at a level < chosenLevel.\n let parentId: string | null = null\n const chosenIdx = schema.levels.findIndex((l) => l.id === chosenLevel)\n for (let j = i - 1; j >= 0; j--) {\n const priorId = path[j]\n const prior = current.find((n) => n.id === priorId) || added.find((n) => n.id === priorId)\n if (!prior) continue\n const priorIdx = schema.levels.findIndex((l) => l.id === prior.level)\n if (priorIdx >= 0 && priorIdx < chosenIdx) { parentId = priorId; break }\n }\n\n const node: GraphNode = {\n id, level: chosenLevel, parentId, axes,\n createdAt: now,\n createdBy,\n }\n this.addNode(node) // throws if schema validation fails\n added.push(node)\n }\n\n return { added, skipped }\n }\n\n /** Append a node. Caller is responsible for id uniqueness; we check and throw. */\n addNode(node: GraphNode): GraphNode {\n const schema = this.loadSchema()\n const file = this.loadNodes()\n if (file.nodes.some((n) => n.id === node.id)) {\n throw new Error(`Node id already exists: ${node.id}`)\n }\n this.validateNode(node, schema, [...file.nodes, node])\n file.nodes.push(node)\n this.saveNodes(file)\n return node\n }\n\n // --- Classifications (append-only log) ---\n\n appendClassification(c: Classification): boolean {\n const parsed = classificationSchema.safeParse(c)\n if (!parsed.success) {\n // Before the fix this threw and took down the whole task. Now we drop\n // the bad classification and log enough detail (the offending path +\n // axes keys) for the operator to diagnose the prompt. Returning false\n // lets callers decide whether to retry with a cleaner proposal.\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\" · \")\n this.log(\n `skipped invalid classification — ${issues} ·`,\n `path=${JSON.stringify(c.path)}`,\n `axes-keys=${JSON.stringify(Object.keys(c.proposedAxes || {}))}`,\n )\n return false\n }\n const line = JSON.stringify(parsed.data) + \"\\n\"\n const p = this.classificationsPath()\n if (!existsSync(p)) {\n mkdirSync(dirname(p), { recursive: true })\n }\n appendFileSync(p, line)\n return true\n }\n\n /**\n * All pending classifications, newest first. Collapses the append-only\n * ledger by msgHash — the most recent entry per message wins (so if an\n * entry was updated via updateClassificationStatus, we see the current\n * state, not the original).\n */\n listPendingClassifications(limit = 200): Classification[] {\n return this.listByStatus(\"pending\", limit)\n }\n\n /** Every classification, newest-first, collapsed one row per message.\n * The human-label queue needs the whole population rather than one\n * status, because the rows worth checking are the auto-approved ones. */\n listAllClassifications(): Classification[] {\n return this.readAllCollapsed()\n }\n\n /** Same contract as listPendingClassifications but filtered to any status. */\n listByStatus(status: Classification[\"status\"], limit = 200): Classification[] {\n const all = this.readAllCollapsed()\n return all.filter((c) => c.status === status).slice(0, limit)\n }\n\n /**\n * Read every classification, collapse by msgHash keeping the newest entry\n * per message. Used by listByStatus + the review loop to see current state.\n * Returns newest-first order.\n */\n private readAllCollapsed(): Classification[] {\n const p = this.classificationsPath()\n if (!existsSync(p)) return []\n const raw = readFileSync(p, \"utf-8\")\n const lines = raw.split(\"\\n\").filter(Boolean)\n const byHash = new Map<string, Classification>()\n for (const line of lines) {\n try {\n const parsed = classificationSchema.safeParse(JSON.parse(line))\n if (!parsed.success) continue\n // Later entries overwrite earlier ones for the same msgHash.\n byHash.set(parsed.data.msgHash, parsed.data)\n } catch {\n // skip malformed\n }\n }\n // Newest-first — sort by ts desc.\n return Array.from(byHash.values()).sort((a, b) => (a.ts < b.ts ? 1 : -1))\n }\n\n /**\n * Append a status-update entry for an existing classification. The ledger\n * stays append-only; readers collapse by msgHash. Returns the updated\n * classification, or null if no prior entry for msgHash exists.\n */\n updateClassificationStatus(\n msgHash: string,\n update: {\n status: Classification[\"status\"]\n reviewer?: string\n reviewReason?: string\n },\n ): Classification | null {\n const all = this.readAllCollapsed()\n const prior = all.find((c) => c.msgHash === msgHash)\n if (!prior) return null\n const next: Classification = {\n ...prior,\n status: update.status,\n ts: new Date().toISOString(),\n }\n // Store reviewer + reason in the preview field so readers can see who\n // flipped what without needing a separate review-log file. Non-breaking:\n // preview is already optional text meant for UI display.\n if (update.reviewer || update.reviewReason) {\n const suffix = ` · ${update.reviewer ? `reviewer=${update.reviewer}` : \"\"}${update.reviewReason ? ` · ${update.reviewReason}` : \"\"}`\n next.preview = ((prior.preview || \"\") + suffix).slice(0, 500)\n }\n this.appendClassification(next)\n return next\n }\n\n /** Read the last N classifications, newest first. Cheap enough for the admin UI. */\n readRecentClassifications(limit = 50): Classification[] {\n const p = this.classificationsPath()\n if (!existsSync(p)) return []\n const raw = readFileSync(p, \"utf-8\")\n const lines = raw.split(\"\\n\").filter(Boolean)\n const out: Classification[] = []\n for (let i = lines.length - 1; i >= 0 && out.length < limit; i--) {\n try {\n const parsed = classificationSchema.safeParse(JSON.parse(lines[i]))\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip malformed line — append-only log; don't fail reads on one bad line\n }\n }\n return out\n }\n\n // --- Fingerprint index ---\n\n /**\n * Hash that represents \"this kind of message\" for snap-to-path caching.\n * Normalizes casing + whitespace; includes channel + sender so two people\n * writing the same words can still land on different paths if they should.\n */\n fingerprint(msg: {\n text: string\n channel?: string\n sender?: string\n }): string {\n const norm = msg.text.toLowerCase().replace(/\\s+/g, \" \").trim()\n const payload = [norm, msg.channel ?? \"\", msg.sender ?? \"\"].join(\"\\u0001\")\n return createHash(\"sha256\").update(payload).digest(\"hex\").slice(0, 32)\n }\n\n loadIndex(): IndexFile {\n const p = this.indexPath()\n if (!existsSync(p)) return { version: 1, entries: {} }\n const raw = readFileSync(p, \"utf-8\")\n const parsed = indexFileSchema.safeParse(JSON.parse(raw))\n if (!parsed.success) {\n throw new Error(`Invalid ${p}: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n return parsed.data\n }\n\n saveIndex(file: IndexFile): void {\n const parsed = indexFileSchema.safeParse(file)\n if (!parsed.success) {\n throw new Error(`Refusing to save invalid index: ${parsed.error.issues.map((i) => i.message).join(\"; \")}`)\n }\n safeWriteJson(this.indexPath(), parsed.data)\n }\n\n getFingerprint(fp: string): FingerprintEntry | undefined {\n return this.loadIndex().entries[fp]\n }\n\n setFingerprint(fp: string, entry: Omit<FingerprintEntry, \"fingerprint\" | \"updatedAt\">): void {\n const file = this.loadIndex()\n file.entries[fp] = {\n ...entry,\n fingerprint: fp,\n updatedAt: new Date().toISOString(),\n }\n this.saveIndex(file)\n }\n\n /** Update the axes on an existing node. Schema-validates the result. */\n updateNodeAxes(id: string, axes: Record<string, string>): GraphNode {\n const schema = this.loadSchema()\n const file = this.loadNodes()\n const node = file.nodes.find((n) => n.id === id)\n if (!node) throw new Error(`Node not found: ${id}`)\n const updated: GraphNode = { ...node, axes }\n this.validateNode(updated, schema, file.nodes.map((n) => (n.id === id ? updated : n)))\n file.nodes = file.nodes.map((n) => (n.id === id ? updated : n))\n this.saveNodes(file)\n return updated\n }\n\n /** Remove a node. Refuses to delete if any other node still references it\n * (child via parentId, or axis ref). */\n deleteNode(id: string): void {\n const file = this.loadNodes()\n const child = file.nodes.find((n) => n.parentId === id)\n if (child) {\n throw new Error(`Cannot delete \"${id}\" — child node \"${child.id}\" still points to it`)\n }\n const refHolder = file.nodes.find((n) =>\n Object.values(n.axes).some((v) => v === id),\n )\n if (refHolder) {\n throw new Error(`Cannot delete \"${id}\" — node \"${refHolder.id}\" references it via an axis`)\n }\n file.nodes = file.nodes.filter((n) => n.id !== id)\n this.saveNodes(file)\n }\n\n /** Approve a pending classification. Commits any new nodes along its path,\n * stamps the fingerprint index so future similar messages hit the cache,\n * and appends an audit entry. Idempotent — no-op if already cached. */\n approveClassification(c: Classification): void {\n if (this.getFingerprint(c.msgHash)) return // already approved\n // Commit any path nodes the LLM proposed but hadn't been confirmed yet.\n const schema = this.loadSchema()\n const known = new Set(this.loadNodes().nodes.map((n) => n.id))\n for (let i = 0; i < c.path.length; i++) {\n const id = c.path[i]\n if (known.has(id)) continue\n const level = schema.levels[i]?.id\n if (!level) break\n const axes = c.proposedAxes?.[id] ?? {}\n const parentId = i === 0 ? null : c.path[i - 1]\n this.addNode({\n id,\n level,\n parentId,\n axes,\n createdAt: new Date().toISOString(),\n createdBy: c.agentId,\n })\n known.add(id)\n }\n this.setFingerprint(c.msgHash, { path: c.path, leaf: c.leaf })\n this.appendClassification({ ...c, status: \"approved\", ts: new Date().toISOString() })\n }\n\n /** Record rejection — no nodes committed, no fingerprint set. */\n rejectClassification(c: Classification): void {\n this.appendClassification({ ...c, status: \"rejected\", ts: new Date().toISOString() })\n }\n}\n\n/** Atomic write: tmp file + rename. Avoids half-written JSON on crashes. */\nfunction safeWriteJson(path: string, data: unknown): void {\n mkdirSync(dirname(path), { recursive: true })\n const tmp = `${path}.tmp-${process.pid}`\n writeFileSync(tmp, JSON.stringify(data, null, 2))\n renameSync(tmp, path)\n}\n\n/** Build the seed nodes.json — one category node per enum value, plus\n * one verb node per STARTER_VERB_NODES entry parented under its\n * category. Idempotent shape: same input always produces the same\n * file, so re-seeding is safe. */\nfunction seedNodesFile(): NodesFile {\n const now = new Date().toISOString()\n const nodes: NodesFile[\"nodes\"] = []\n const seenCats = new Set<string>()\n for (const v of STARTER_VERB_NODES) {\n if (!seenCats.has(v.category)) {\n nodes.push({\n id: v.category,\n level: \"category\",\n parentId: null,\n axes: { kind: v.category },\n createdAt: now,\n createdBy: \"starter\",\n })\n seenCats.add(v.category)\n }\n }\n for (const v of STARTER_VERB_NODES) {\n nodes.push({\n id: v.verb,\n level: \"verb\",\n parentId: v.category,\n axes: { name: v.verb, ...(v.description ? { description: v.description } : {}) },\n createdAt: now,\n createdBy: \"starter\",\n })\n }\n return { version: 1, nodes }\n}\n","import { createHash } from \"crypto\"\nimport { extractJson } from \"@/utils/extract-json\"\nimport { GraphStore } from \"./store\"\nimport {\n type GraphSchema,\n type GraphNode,\n type Classification,\n} from \"./types\"\nimport { createProvider, type ProviderName, type AgentProvider } from \"@/agent/providers\"\n\n// --- Intent Knowledge Graph classifier ---\n//\n// For every incoming message, returns a path through the taxonomy:\n// 1. fingerprint → cache hit → reuse path (0 LLM calls, source=cache)\n// 2. miss → LLM draftAgent proposes { path, axes } → validated → pending\n// 3. autoApproveConfidence met → committed + cached\n//\n// Pending paths are still usable immediately (caller tags artifacts with\n// them); approval flips the flag and populates the fingerprint index so\n// the next similar message hits the cache.\n\nexport interface ClassifyInput {\n text: string\n channel?: string\n sender?: string\n /** Agent that will RECEIVE the message after classification. When this\n * equals `draftAgent`, classification is skipped to prevent a deadlock —\n * the classifier's sub-task would otherwise queue behind the in-progress\n * main task on the same agent. */\n agentId?: string\n}\n\nexport interface ClassifyResult {\n /** Node ids from root to leaf. Shorter-than-full paths are allowed. */\n path: string[]\n /** Stable hash of the path — used as the `graph:<pathId>` wiki tag. */\n pathId: string\n /** Human-readable \"A › B › C\" path for logs + UI. */\n pathLabel: string\n /** Axis values asserted by the classifier. Keyed by node id. */\n axes: Record<string, Record<string, string>>\n leaf: { input?: string; output?: string }\n source: \"cache\" | \"llm\"\n status: \"pending\" | \"approved\"\n confidence?: number\n}\n\nexport type ApprovalStructurePolicy = \"strict\" | \"extend-leaves\" | \"any\"\n\nexport interface ClassifierOptions {\n store: GraphStore\n /** Base URL of the daemon — used to POST /task for LLM proposals. */\n daemonUrl: string\n /** Optional bearer token for the daemon. */\n token?: string\n /** Which agent makes the proposal. Required if caller ever expects a\n * non-cached classification (cache hits work without an agent). */\n draftAgent?: string\n /** Structural approval policy — see config.ts for semantics. Default\n * \"extend-leaves\" (auto-approve pure reuse + single-leaf additions). */\n autoApproveStructure?: ApprovalStructurePolicy\n /** Minimum confidence (0..1) to commit without human approval. 1.0 = never.\n * OR'd with the structural policy. */\n autoApproveConfidence?: number\n /** Provider used for the in-process classifier call. Defaults to\n * \"claude-code\", which auto-detects auth via `loadAuthConfig()` —\n * Claude Max OAuth (CLI subprocess), API key, etc. NEVER opens a\n * paid path beyond what the operator has already configured. */\n classifierProvider?: ProviderName\n /** Model id passed to the provider. For claude-code OAuth this is\n * mapped through `CLI_MODEL_ALIASES` (sonnet/opus/haiku). Cheap\n * default — classification is metadata, not work. */\n classifierModel?: string\n log?: (...args: unknown[]) => void\n}\n\nexport class Classifier {\n private store: GraphStore\n private daemonUrl: string\n private token?: string\n private draftAgent?: string\n private autoApprove: number\n private autoApproveStructure: ApprovalStructurePolicy\n private classifierModel: string\n private classifierProviderName: ProviderName\n private providerInstance?: AgentProvider\n private log: (...args: unknown[]) => void\n\n constructor(opts: ClassifierOptions) {\n this.store = opts.store\n this.daemonUrl = opts.daemonUrl.replace(/\\/+$/, \"\")\n this.token = opts.token\n this.draftAgent = opts.draftAgent\n this.autoApprove = opts.autoApproveConfidence ?? 1.0\n this.autoApproveStructure = opts.autoApproveStructure ?? \"extend-leaves\"\n this.classifierModel = opts.classifierModel ?? \"claude-haiku-4-5-20251001\"\n this.classifierProviderName = opts.classifierProvider ?? \"claude-code\"\n this.log = opts.log ?? console.error.bind(console, \"[classifier]\")\n }\n\n /** Lazily build the provider instance. Lazy because constructing\n * ClaudeCodeProvider hits auth-store; deferring it keeps the\n * classifier importable in tests / dry-run paths that never call\n * classify(). */\n private getProvider(): AgentProvider {\n if (!this.providerInstance) {\n this.providerInstance = createProvider(this.classifierProviderName)\n }\n return this.providerInstance\n }\n\n async classify(input: ClassifyInput): Promise<ClassifyResult | null> {\n const fp = this.store.fingerprint({\n text: input.text,\n channel: input.channel,\n sender: input.sender,\n })\n\n // 1. Cache hit — instant return, zero LLM. Logged to classifications.jsonl\n // so the cache-hit rate is measurable from the persisted record alone\n // (otherwise hits leave no trace and ROI of the classifier is invisible).\n const cached = this.store.getFingerprint(fp)\n if (cached) {\n this.store.appendClassification({\n ts: new Date().toISOString(),\n msgHash: fp,\n agentId: input.agentId,\n channel: input.channel,\n sender: input.sender,\n path: cached.path,\n proposedAxes: {},\n leaf: cached.leaf,\n source: \"cache\",\n status: \"approved\",\n preview: input.text.slice(0, 200),\n })\n return {\n path: cached.path,\n pathId: hashPath(cached.path),\n pathLabel: pathLabel(cached.path, this.store.loadNodes().nodes),\n axes: {},\n leaf: cached.leaf,\n source: \"cache\",\n status: \"approved\",\n }\n }\n\n // 2. Cache miss — need an LLM proposal. Phase 2 of classifier-retire\n // replaced the /task → graph-agent dispatch with a direct Anthropic\n // Messages call, so we no longer need a draftAgent and the\n // self-dispatch deadlock guard (`agentId === draftAgent`) is moot.\n // `graph.enabled` is the only gate now.\n const schema = this.store.loadSchema()\n const nodes = this.store.loadNodes().nodes\n const proposal = await this.proposePath(input, schema, nodes).catch((e) => {\n this.log(\"LLM proposal failed:\", e?.message || e)\n return null\n })\n if (!proposal) return null\n\n // 3. Validate + persist as pending. Any NEW node the LLM proposed is\n // only committed if the whole classification auto-approves below.\n const { path, proposedAxes, confidence, leaf } = proposal\n if (path.length === 0) return null\n\n const classification: Classification = {\n ts: new Date().toISOString(),\n msgHash: fp,\n agentId: input.agentId,\n channel: input.channel,\n sender: input.sender,\n path,\n proposedAxes,\n leaf,\n source: \"llm\",\n status: \"pending\",\n confidence,\n preview: input.text.slice(0, 200),\n }\n\n // --- Auto-approval policy (see config.ts for full semantics) ---\n //\n // Two conditions, OR'd. Either approves the classification:\n // 1. Structure policy — what KIND of change the proposed path makes:\n // strict: nothing auto — always pending\n // extend-leaves: auto if the path either (a) reuses only existing\n // nodes, or (b) adds exactly one new node at the\n // DEEPEST level (a new leaf). Structural changes\n // (new mid-path or root node) still queue.\n // any: auto regardless of structure.\n // 2. Confidence — LLM-self-reported confidence >= autoApproveConfidence.\n //\n // Pending classifications still persist to the log, but don't commit nodes\n // and don't populate the fingerprint cache (so similar messages re-query).\n const existingIds = new Set(nodes.map((n) => n.id))\n const newNodeIndices = path\n .map((id, idx) => (existingIds.has(id) ? -1 : idx))\n .filter((idx) => idx >= 0)\n\n let structureOk = false\n if (this.autoApproveStructure === \"any\") {\n structureOk = true\n } else if (this.autoApproveStructure === \"extend-leaves\") {\n const isReuseOnly = newNodeIndices.length === 0\n const isSingleLeafAddition =\n newNodeIndices.length === 1 && newNodeIndices[0] === path.length - 1\n structureOk = isReuseOnly || isSingleLeafAddition\n }\n const confidenceOk = confidence !== undefined && confidence >= this.autoApprove\n\n let status: \"pending\" | \"approved\" = \"pending\"\n if (structureOk || confidenceOk) {\n // Commit any new nodes; if ANY fail schema validation (missing axes,\n // bad ref, etc.) fall back to pending for the whole classification\n // rather than half-committing the graph and bubbling the error up to\n // the task handler.\n try {\n this.commitNewNodes(path, proposedAxes, schema, nodes, input.sender)\n this.store.setFingerprint(fp, { path, leaf })\n status = \"approved\"\n } catch (e: any) {\n this.log(\n `auto-approve failed — staying pending ·`,\n `path=${JSON.stringify(path)}`,\n `reason=${(e?.message || e).toString().slice(0, 200)}`,\n )\n }\n }\n classification.status = status\n this.store.appendClassification(classification)\n\n return {\n path,\n pathId: hashPath(path),\n pathLabel: pathLabel(path, this.store.loadNodes().nodes),\n axes: proposedAxes,\n leaf,\n source: \"llm\",\n status,\n confidence,\n }\n }\n\n /** Direct call to Anthropic to propose a path. Pure I/O; no side effects.\n * Bypasses the mesh / dispatch / ledger pipeline so a classification\n * doesn't surface as a sibling task in the activity graph. */\n private async proposePath(\n input: ClassifyInput,\n schema: GraphSchema,\n nodes: GraphNode[],\n ): Promise<{\n path: string[]\n proposedAxes: Record<string, Record<string, string>>\n confidence?: number\n leaf: { input?: string; output?: string }\n } | null> {\n const nodesForPrompt = nodes.map((n) => ({\n id: n.id,\n level: n.level,\n parentId: n.parentId,\n axes: n.axes,\n }))\n const userPrompt = [\n `You are the intent classifier for AgentX. You answer ONE question:`,\n `\"what kind of work does this message describe?\" — verb-level only.`,\n ``,\n `IMPORTANT — what NOT to encode in the path:`,\n `- Client / company / project / team names. Those are already on the event`,\n ` metadata (project, channel, agentId). Embedding them in the path causes`,\n ` duplicate nodes (one per client) for the same verb.`,\n `- The specific subject (issue number, file name, person name). Same reason.`,\n `- The agent's name or role. Pick the verb the *requester* intended.`,\n ``,\n `SCHEMA (fixed levels — pick one node per level):`,\n \"```json\",\n JSON.stringify(schema, null, 2),\n \"```\",\n ``,\n `EXISTING NODES (prefer reusing these; only invent a new node when no`,\n `existing one fits):`,\n \"```json\",\n JSON.stringify(nodesForPrompt, null, 2),\n \"```\",\n ``,\n `MESSAGE:`,\n \"```\",\n input.text.slice(0, 2000),\n \"```\",\n `channel: ${input.channel ?? \"?\"}`,\n `sender: ${input.sender ?? \"?\"}`,\n ``,\n `TASK:`,\n `1. Classify into 'category' (closed enum): code, ops, support, admin,`,\n ` knowledge, social, system. Pick the closest fit.`,\n `2. Pick or propose a 'verb' node. Verb ids are dot-namespaced lower-kebab,`,\n ` e.g. \"review.merge-request\", \"deploy.staging\", \"investigate.error\",`,\n ` \"chat.greeting\", \"fix.bug\". Reuse an existing verb when the message is`,\n ` the same kind of work as something already classified, even when the`,\n ` client / project / subject differs.`,\n ``,\n `Examples (right vs wrong):`,\n `- \"Please review MR #957 on globex/system\" → [\"code\", \"review.merge-request\"]`,\n ` NOT [\"business\", \"acme\", \"globex-v2\", \"review-mr-957-system\"]`,\n `- \"Deploy initech-v2 to staging please\" → [\"ops\", \"deploy.staging\"]`,\n ` NOT [\"business\", \"acme\", \"initech-v2\", \"deploy-initech-v2-to-staging\"]`,\n `- \"Hello Atlas\" → [\"support\", \"chat.greeting\"]`,\n ``,\n `NODE ID RULES (strict — invalid ids get dropped):`,\n `- lowercase-kebab + dots only: [a-z0-9][a-z0-9._-]*`,\n `- no spaces, no uppercase, no Arabic/other non-Latin`,\n `- verb ids should use a \"category.specific\" or \"verb.modifier\" shape`,\n ``,\n `Return ONE JSON object on one line, no prose, no fences:`,\n ` { \"path\": string[], \"proposedAxes\": { [nodeId]: { [axisName]: string } }, \"leaf\": { \"input\"?: string, \"output\"?: string }, \"confidence\": number }`,\n `confidence in [0,1]. Prefer low confidence over guessing.`,\n ].join(\"\\n\")\n\n // Phase 2 of classifier-retire: in-process provider call.\n // Replaces the `/task → graph-agent → mesh` dispatch path that\n // generated a sibling ledger event per classification (60–70% of\n // dispatches in the activity graph). Reuses whatever auth the\n // operator already configured (Claude Max OAuth via CLI subprocess,\n // Anthropic API key, etc.) — never opens a paid path the operator\n // didn't already opt into. The provider returns a plain JSON-text\n // GenerationResult, so we parse the same way as before.\n const text = await this.callProvider(userPrompt)\n const parsed = extractJson(text)\n if (!parsed || !Array.isArray(parsed.path)) return null\n\n // LLM often returns human-readable labels (\"Business\", \"Sales Manager\").\n // The node-id schema requires a lowercase slug, so normalize here and\n // remember the original → slug mapping so proposedAxes stays attached.\n // We also drop any element that can't be slugified into a valid id (e.g.\n // a path entry of pure Arabic script) — better to classify with a shorter\n // path than to have the store reject the whole classification.\n const NODE_ID_RE = /^[a-z0-9][a-z0-9._-]*$/\n const slugMap = new Map<string, string>()\n const path: string[] = parsed.path\n .filter((s: unknown): s is string => typeof s === \"string\" && s.length > 0)\n .map((s: string) => {\n const trimmed = s.trim()\n const slugged = slugifyNodeId(trimmed)\n if (trimmed !== slugged) slugMap.set(trimmed, slugged)\n return slugged\n })\n .filter((s: string) => NODE_ID_RE.test(s))\n if (path.length === 0) return null\n\n const proposedAxes: Record<string, Record<string, string>> = {}\n if (parsed.proposedAxes && typeof parsed.proposedAxes === \"object\") {\n for (const [rawNodeId, axes] of Object.entries(parsed.proposedAxes as Record<string, any>)) {\n if (!axes || typeof axes !== \"object\") continue\n const nodeId = slugMap.get(rawNodeId) ?? slugifyNodeId(rawNodeId)\n if (!nodeId) continue\n const clean: Record<string, string> = {}\n for (const [k, v] of Object.entries(axes)) {\n if (typeof v === \"string\") clean[k] = v\n }\n // If the LLM didn't volunteer a human-readable name but we slugified\n // one away, preserve the original as a `name` axis so the UI can\n // render \"Sales Manager\" not \"sales-manager\".\n if (!clean.name && rawNodeId !== nodeId) clean.name = rawNodeId.trim()\n proposedAxes[nodeId] = clean\n }\n }\n\n const leaf: { input?: string; output?: string } = {}\n if (parsed.leaf && typeof parsed.leaf === \"object\") {\n if (typeof parsed.leaf.input === \"string\") leaf.input = parsed.leaf.input\n if (typeof parsed.leaf.output === \"string\") leaf.output = parsed.leaf.output\n }\n\n const confidence = typeof parsed.confidence === \"number\"\n ? Math.max(0, Math.min(1, parsed.confidence))\n : undefined\n\n return { path, proposedAxes, confidence, leaf }\n }\n\n /** Create any nodes along `path` that don't exist yet. Delegates to the\n * store so level inference (for paths that skip levels) + fresh reloads\n * between adds are shared with the review path. */\n private commitNewNodes(\n path: string[],\n proposedAxes: Record<string, Record<string, string>>,\n schema: GraphSchema,\n _existing: GraphNode[],\n createdBy?: string,\n ): void {\n this.store.commitNodesAlongPath(path, proposedAxes, schema, createdBy)\n }\n\n /** Run the prompt through whichever AgentProvider the operator already\n * configured (Claude Max subscription via CLI, Anthropic API key, …).\n * Hard rule: NEVER open a paid path the operator didn't opt into —\n * this is just a thin wrapper that reuses existing auth. 30s timeout\n * is enough for Haiku/CLI cold-start; any longer and the classifier\n * is stalling the caller. */\n private async callProvider(userPrompt: string): Promise<string> {\n const provider = this.getProvider()\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), 30_000)\n try {\n const res = await provider.generate(\n [\n {\n role: \"system\",\n content:\n \"You are a taxonomy classifier. Respond with a single JSON object on one line. No prose, no code fences.\",\n },\n { role: \"user\", content: userPrompt },\n ],\n { model: this.classifierModel, maxTokens: 800 },\n )\n return res.content || \"\"\n } finally {\n clearTimeout(timer)\n }\n }\n}\n\n/** Normalize an LLM-proposed node label to a valid node id. Matches the\n * schema's `^[a-z0-9][a-z0-9._-]*$` — lowercase, alnum/./-/_, leading alnum.\n * Dots are preserved for verb ids like \"review.merge-request\". */\nfunction slugifyNodeId(raw: string): string {\n const s = raw\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^[-_]+|[-_]+$/g, \"\")\n .replace(/-{2,}/g, \"-\")\n if (!s) return \"\"\n // Ensure leading char is alnum (schema rejects leading _ or -).\n return /^[a-z0-9]/.test(s) ? s : `n-${s}`.replace(/[-_]+$/, \"\")\n}\n\n// --- path helpers used by both classifier + wiki retrieval ---\n\n/** Deterministic hash of a path, used as the `graph:<pathId>` wiki tag. */\nexport function hashPath(path: string[]): string {\n return createHash(\"sha1\")\n .update(path.join(\"\\u0001\"))\n .digest(\"hex\")\n .slice(0, 16)\n}\n\n/** \"Business › Acme › DevOps › Review MR\" — for the UI + context render. */\nexport function pathLabel(path: string[], nodes: GraphNode[]): string {\n return path\n .map((id) => {\n const n = nodes.find((x) => x.id === id)\n if (!n) return id\n // Prefer a `name` axis when the schema has one; fall back to id.\n const name = n.axes?.name || n.axes?.what || id\n return name\n })\n .join(\" › \")\n}\n\n/**\n * Depth of the deepest common ancestor between two paths, normalized to [0,1].\n * Used to score wiki articles: exact match = 1, shared grandparent = 0.5, none = 0.\n */\nexport function ancestryScore(a: string[], b: string[]): number {\n if (a.length === 0 || b.length === 0) return 0\n let shared = 0\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n if (a[i] === b[i]) shared++\n else break\n }\n if (shared === 0) return 0\n const depth = Math.max(a.length, b.length)\n return shared / depth\n}\n\n","import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join, relative, dirname } from \"path\"\nimport { isWikiArticleType } from \"./types\"\nimport type { WikiArticle, WikiArticleMeta, WikiEntry, WikiIndex, WikiAccess } from \"./types\"\nimport { buildIndex, buildIndexCached, scoreAll } from \"../memory/bm25\"\nimport { askSeat } from \"../decisions/seat\"\nimport {\n DEFAULT_SHORTLIST,\n WIKI_RERANK_SEAT,\n rerankQuestions,\n rerankState,\n toRanking,\n type RerankCandidate,\n} from \"../decisions/seats/wiki-rerank\"\nimport { ancestryScore as ancestryOf } from \"@/graph\"\n\n// --- Wiki Store: filesystem-based knowledge base with permissions ---\n//\n// Structure:\n// .agentx/wiki/\n// _index.md # Master index with aliases\n// _backlinks.json # Reverse link index\n// raw/entries/ # One .md per ingested entry\n// {directories}/ # Articles organized by type\n//\n// Permission model:\n// - private: only owner agent reads/writes\n// - shared: owner writes, listed agents read\n// - public: owner writes, all agents read\n\nexport class WikiStore {\n readonly baseDir: string\n private rawDir: string\n private log: (...args: unknown[]) => void\n\n constructor(\n baseDir: string = resolve(process.cwd(), \".agentx/wiki\"),\n log: (...args: unknown[]) => void = console.error.bind(console, \"[wiki]\"),\n ) {\n this.baseDir = baseDir\n this.rawDir = resolve(baseDir, \"raw/entries\")\n this.log = log\n\n mkdirSync(this.rawDir, { recursive: true })\n mkdirSync(resolve(baseDir, \"raw\"), { recursive: true })\n\n // Initialize schema if missing\n this.ensureSchema()\n }\n\n // --- Permission checks ---\n\n /**\n * Check if an agent can read an article.\n */\n canRead(article: WikiArticleMeta, agentId?: string): boolean {\n if (article.access === \"public\") return true\n if (!agentId) return false\n if (article.owner === agentId) return true\n if (article.access === \"shared\" && article.sharedWith?.includes(agentId)) return true\n return false\n }\n\n /**\n * Check if an agent can write an article.\n */\n canWrite(article: WikiArticleMeta, agentId: string): boolean {\n return article.owner === agentId\n }\n\n // --- Raw entries (ingestion) ---\n\n /**\n * Add a raw entry (from a conversation, cron result, etc.)\n */\n addEntry(entry: WikiEntry): string {\n const filename = `${entry.date}_${entry.id}.md`\n const filepath = resolve(this.rawDir, filename)\n\n const frontmatter = [\n \"---\",\n `id: ${entry.id}`,\n `date: ${entry.date}`,\n `agent: ${entry.agentId}`,\n `source: ${entry.source}`,\n ]\n if (entry.sourceContext) frontmatter.push(`context: ${entry.sourceContext}`)\n if (entry.meta) {\n for (const [k, v] of Object.entries(entry.meta)) {\n frontmatter.push(`${k}: ${JSON.stringify(v)}`)\n }\n }\n frontmatter.push(\"---\", \"\", entry.content)\n\n writeFileSync(filepath, frontmatter.join(\"\\n\"))\n this.appendLog(\"ingest\", `${entry.id} from ${entry.agentId} via ${entry.source}`)\n return filename\n }\n\n /**\n * List raw entries, optionally filtered by agent or date range.\n */\n listEntries(filter?: { agentId?: string; after?: string; before?: string }): WikiEntry[] {\n if (!existsSync(this.rawDir)) return []\n\n const files = readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).sort()\n const entries: WikiEntry[] = []\n\n for (const file of files) {\n const content = readFileSync(resolve(this.rawDir, file), \"utf-8\")\n const entry = this.parseEntry(content, file)\n if (!entry) continue\n\n if (filter?.agentId && entry.agentId !== filter.agentId) continue\n if (filter?.after && entry.date < filter.after) continue\n if (filter?.before && entry.date > filter.before) continue\n\n entries.push(entry)\n }\n\n return entries\n }\n\n private parseEntry(content: string, filename: string): WikiEntry | null {\n const fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const body = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim() || \"\"\n }\n\n // Everything addEntry wrote that is not one of the five named keys is\n // `meta`, JSON-encoded one key per line. Dropping it here is what made\n // graphPath 0%: the capture path stamps intentPath onto every entry,\n // `wiki absorb` reads entry.meta.intentPath to propagate it onto the\n // article, and this parser never returned a meta at all — so absorb\n // always fell through to a fingerprint lookup its own comment calls\n // unreliable, and the retrieval scorer multiplied the graph weight\n // (0.6 of the hybrid score) by zero for every article ever compiled.\n const KNOWN = new Set([\"id\", \"date\", \"agent\", \"source\", \"context\"])\n const meta: Record<string, unknown> = {}\n for (const line of fm.split(\"\\n\")) {\n const m = line.match(/^(\\w+):\\s*(.+)$/)\n if (!m || KNOWN.has(m[1])) continue\n try {\n meta[m[1]] = JSON.parse(m[2])\n } catch {\n meta[m[1]] = m[2].trim()\n }\n }\n\n return {\n id: get(\"id\") || filename.replace(\".md\", \"\"),\n date: get(\"date\"),\n agentId: get(\"agent\"),\n source: get(\"source\"),\n sourceContext: get(\"context\") || undefined,\n content: body,\n meta: Object.keys(meta).length > 0 ? meta : undefined,\n }\n }\n\n // --- Articles (compiled knowledge) ---\n\n /**\n * Write or update a wiki article.\n */\n writeArticle(\n path: string,\n meta: WikiArticleMeta,\n content: string,\n agentId: string,\n ): boolean {\n // LLM picks the path. We just write the file.\n const existing = this.readArticle(path)\n if (existing && !this.canWrite(existing.meta, agentId)) {\n this.log(`Permission denied: \"${agentId}\" cannot write \"${path}\" (owner: ${existing.meta.owner})`)\n return false\n }\n\n // Drop invalid `type` at write time. The parser already rejects invalid\n // values on read (returns type=undefined), so silently serialising a\n // stray value would cause the round-trip to look fine but the article\n // to render as \"untyped\" in the catalog. Log + drop beats surprise.\n if (meta.type && !isWikiArticleType(meta.type)) {\n this.log(`Dropping invalid article type \"${meta.type}\" for \"${path}\" — enum is [person, project, place, concept, event, decision, pattern]`)\n meta = { ...meta, type: undefined }\n }\n\n const fullPath = resolve(this.baseDir, path)\n\n // Version the previous content before overwriting (immutable history)\n if (existing) {\n this.saveVersion(path, fullPath)\n }\n\n mkdirSync(dirname(fullPath), { recursive: true })\n\n const frontmatter = [\n \"---\",\n `title: \"${meta.title}\"`,\n ]\n if (meta.type) frontmatter.push(`type: ${meta.type}`)\n if (meta.related?.length) {\n frontmatter.push(`related: [${meta.related.map(t => `\"${t}\"`).join(\", \")}]`)\n }\n frontmatter.push(\n `tags: [${(meta.tags || []).map(t => `\"${t}\"`).join(\", \")}]`,\n `owner: ${meta.owner}`,\n `access: ${meta.access}`,\n )\n if (meta.sharedWith?.length) {\n frontmatter.push(`shared_with: [${meta.sharedWith.map(s => `\"${s}\"`).join(\", \")}]`)\n }\n frontmatter.push(\n `created: ${meta.created}`,\n `last_updated: ${meta.lastUpdated}`,\n `sources: [${meta.sources.map(s => `\"${s}\"`).join(\", \")}]`,\n )\n if (meta.graphPath?.length) {\n frontmatter.push(`graph_path: [${meta.graphPath.map(s => `\"${s}\"`).join(\", \")}]`)\n }\n frontmatter.push(\"---\", \"\", content)\n\n writeFileSync(fullPath, frontmatter.join(\"\\n\"))\n const action = existing ? \"update\" : \"create\"\n this.appendLog(action, `${meta.title} [${(meta.tags || []).slice(0, 5).join(\", \")}] by ${agentId} at ${path}`)\n return true\n }\n\n // --- Article Versioning ---\n\n /**\n * Save current article content as an immutable version before overwriting.\n * Versions stored in _versions/{article-path}/{timestamp}.md\n */\n private saveVersion(articlePath: string, fullPath: string): void {\n try {\n if (!existsSync(fullPath)) return\n const content = readFileSync(fullPath, \"utf-8\")\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\")\n const versionDir = resolve(this.baseDir, \"_versions\", articlePath.replace(/\\.md$/, \"\"))\n mkdirSync(versionDir, { recursive: true })\n writeFileSync(resolve(versionDir, `${ts}.md`), content)\n } catch {\n // Version save is best-effort\n }\n }\n\n /**\n * List all versions of an article (newest first).\n */\n getVersions(articlePath: string): Array<{ timestamp: string; path: string }> {\n const versionDir = resolve(this.baseDir, \"_versions\", articlePath.replace(/\\.md$/, \"\"))\n if (!existsSync(versionDir)) return []\n\n try {\n return readdirSync(versionDir)\n .filter(f => f.endsWith(\".md\"))\n .sort()\n .reverse()\n .map(f => ({\n timestamp: f.replace(/\\.md$/, \"\").replace(/-/g, (m, i) => i < 19 ? (i === 10 ? \"T\" : i === 13 || i === 16 ? \":\" : \"-\") : m),\n path: resolve(versionDir, f),\n }))\n } catch {\n return []\n }\n }\n\n /**\n * Restore a specific version of an article.\n */\n restoreVersion(articlePath: string, versionTimestamp: string): boolean {\n const versions = this.getVersions(articlePath)\n const version = versions.find(v => v.timestamp.startsWith(versionTimestamp))\n if (!version) return false\n\n try {\n const content = readFileSync(version.path, \"utf-8\")\n const fullPath = resolve(this.baseDir, articlePath)\n // Save current as a version too before restoring\n this.saveVersion(articlePath, fullPath)\n writeFileSync(fullPath, content)\n this.appendLog(\"restore\", `${articlePath} restored to ${versionTimestamp}`)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Read an article. Returns null if not found.\n */\n readArticle(path: string): WikiArticle | null {\n const fullPath = resolve(this.baseDir, path)\n if (!existsSync(fullPath)) return null\n\n const raw = readFileSync(fullPath, \"utf-8\")\n return this.parseArticle(raw, path)\n }\n\n /**\n * Read an article with permission check.\n */\n readArticleAs(path: string, agentId: string): WikiArticle | null {\n const article = this.readArticle(path)\n if (!article) return null\n if (!this.canRead(article.meta, agentId)) return null\n return article\n }\n\n private parseArticle(raw: string, path: string): WikiArticle | null {\n const fmMatch = raw.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const content = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim().replace(/^\"(.*)\"$/, \"$1\") || \"\"\n }\n\n const getArray = (key: string): string[] => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*\\\\[(.*)\\\\]$`, \"m\"))\n if (!m) return []\n return m[1].split(\",\").map(s => s.trim().replace(/^\"(.*)\"$/, \"$1\")).filter(Boolean)\n }\n\n // `type` and `kind` are the new structural spine; no longer stuffed into tags.\n // Legacy articles may have a `kind:` field; we promote it to `type` on read so\n // the migration (Phase 2) can patch it in place.\n const rawType = get(\"type\") || get(\"kind\")\n const validTypes = new Set([\n \"person\", \"project\", \"place\", \"concept\", \"event\", \"decision\", \"pattern\",\n ])\n const type = validTypes.has(rawType) ? (rawType as WikiArticleMeta[\"type\"]) : undefined\n\n const tags = getArray(\"tags\").filter((v, i, a) => v && a.indexOf(v) === i)\n const related = getArray(\"related\")\n const graphPath = getArray(\"graph_path\")\n return {\n meta: {\n title: get(\"title\"),\n type,\n related: related.length ? related : undefined,\n tags,\n owner: get(\"owner\"),\n access: (get(\"access\") as WikiAccess) || \"public\",\n sharedWith: getArray(\"shared_with\"),\n created: get(\"created\"),\n lastUpdated: get(\"last_updated\"),\n sources: getArray(\"sources\"),\n graphPath: graphPath.length ? graphPath : undefined,\n },\n content,\n path,\n }\n }\n\n // --- Index and discovery ---\n\n /**\n * List all articles accessible to an agent.\n */\n listArticles(agentId: string): WikiArticle[] {\n const articles: WikiArticle[] = []\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (article && this.canRead(article.meta, agentId)) {\n articles.push(article)\n }\n })\n return articles\n }\n\n /**\n * Search articles by keyword. Returns articles the agent can read.\n */\n search(query: string, agentId: string, maxResults: number = 10): WikiArticle[] {\n const queryLower = query.toLowerCase()\n const articles: WikiArticle[] = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article || !this.canRead(article.meta, agentId)) return\n articles.push(article)\n })\n\n if (articles.length === 0) return []\n\n // BM25 over article content (cached to disk)\n const docs = articles.map((a) => a.content)\n const cachePath = resolve(this.baseDir, \"_bm25_cache.json\")\n const index = buildIndexCached(docs, cachePath)\n const bm25Scores = scoreAll(query, index)\n const scoreMap = new Map(bm25Scores.map((r) => [r.docIndex, r.score]))\n\n const results = articles.map((article, i) => {\n let score = scoreMap.get(i) ?? 0\n // Additive bonuses for title and tag matches\n if (article.meta.title.toLowerCase().includes(queryLower)) score += 10\n if (article.meta.tags?.some(t => t.toLowerCase().includes(queryLower))) score += 5\n return { article, score }\n })\n\n return results\n .filter((r) => r.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults)\n .map((r) => r.article)\n }\n\n /**\n * findRelevant, then re-ranked by the wiki-rerank seat.\n *\n * Recall comes from findRelevant rather than search() because\n * findRelevant is the function everything actually calls — search()\n * turned out to have no callers at all. Additive on purpose: the\n * synchronous findRelevant is untouched and its existing callers stay\n * as they are. Measured on the 12-query eval against this\n * wiki, re-ranking moves R@1 from 33.3% to 50.0% and R@3 from 41.7% to\n * 66.7%, for about $0.0005 and 550ms a query.\n *\n * Fail-open in every direction. Seat off, backend down, no key, fewer\n * than two candidates — all return BM25's own order, which is what the\n * caller would have got anyway.\n *\n * Note what this cannot do: it only reorders what BM25 shortlisted. In\n * the eval R@3 sat exactly on that ceiling, so every remaining miss was\n * an article BM25 never returned. Widening the shortlist does not fix\n * that — past 30 the extra candidates measurably diluted the ranking.\n */\n async findRelevantReranked(\n query: string,\n agentId?: string,\n maxResults = 10,\n opts: { shortlist?: number; excerptChars?: number } = {},\n ): Promise<WikiArticle[]> {\n const shortlist = Math.max(maxResults, opts.shortlist ?? DEFAULT_SHORTLIST)\n const pool = this.findRelevant(query, agentId, shortlist)\n if (pool.length < 2) return pool.slice(0, maxResults)\n\n const candidates: RerankCandidate[] = pool.map((article, i) => ({\n // Index, not title: titles are not unique across a wiki and a\n // Choice needs distinct keys.\n id: `c${i}`,\n title: article.meta.title || `(untitled ${i})`,\n tags: article.meta.tags,\n excerpt: article.content,\n }))\n\n const result = await askSeat(\n WIKI_RERANK_SEAT,\n rerankState({ query, candidates, excerptChars: opts.excerptChars }),\n rerankQuestions(candidates),\n { incumbent: { best: candidates[0].id }, features: { agent: agentId ?? \"unknown\" } },\n )\n if (!result || result.mode !== \"active\") return pool.slice(0, maxResults)\n\n const ranking = toRanking(result.answers as never, candidates)\n return ranking.ranked\n .map((id) => pool[Number(id.slice(1))])\n .filter((a): a is WikiArticle => Boolean(a))\n .slice(0, maxResults)\n }\n\n /**\n * Find articles relevant to a message (for context injection).\n *\n * Scoring:\n * - BM25 over title+tags+content (always)\n * - + ancestry bonus if the caller classified the message through the\n * intent graph and articles carry `graphPath` from a previous classification\n *\n * `weights.graph` is scaled against `weights.bm25`. When the caller omits\n * `messagePath`, retrieval collapses to pure BM25 and matches legacy behavior.\n */\n findRelevant(\n message: string,\n agentId?: string,\n maxArticles: number = 3,\n messagePath?: string[],\n weights: { graph: number; bm25: number } = { graph: 0.6, bm25: 0.4 },\n ): WikiArticle[] {\n const articles: WikiArticle[] = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article || !this.canRead(article.meta, agentId)) return\n articles.push(article)\n })\n\n if (articles.length === 0) return []\n\n const docs = articles.map(\n (a) => `${a.meta.title} ${(a.meta.tags || []).join(\" \")} ${a.content}`,\n )\n const cachePath = resolve(this.baseDir, \"_bm25_relevance_cache.json\")\n const index = buildIndexCached(docs, cachePath)\n const bm25Scores = scoreAll(message, index)\n\n // Normalize BM25 so the two signals live on [0,1] before the weighted sum.\n const maxBm25 = bm25Scores.reduce((m, s) => Math.max(m, s.score), 0) || 1\n const bm25Map = new Map(bm25Scores.map((s) => [s.docIndex, s.score / maxBm25]))\n\n const hasGraph = !!(messagePath && messagePath.length > 0)\n const scored = articles.map((article, i) => {\n const bm25 = bm25Map.get(i) ?? 0\n let ancestry = 0\n if (hasGraph && article.meta.graphPath?.length) {\n ancestry = ancestryOf(messagePath!, article.meta.graphPath)\n }\n // When the caller passed no path, BM25 gets the full weight.\n const combined = hasGraph\n ? weights.graph * ancestry + weights.bm25 * bm25\n : bm25\n return { article, score: combined }\n })\n\n return scored\n .filter((r) => r.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxArticles)\n .map((r) => r.article)\n }\n\n /**\n * Build context string from relevant wiki articles for prompt injection.\n * Token-efficient: includes title + first ~500 chars of each article.\n */\n buildContext(articles: WikiArticle[], maxChars: number = 4000): string {\n if (articles.length === 0) return \"\"\n\n const lines: string[] = [\"[Wiki Knowledge]\"]\n\n let totalChars = 0\n for (const article of articles) {\n const header = `\\n## ${article.meta.title} [${(article.meta.tags || []).slice(0, 3).join(\", \")}]`\n const body = article.content.length > 600\n ? article.content.slice(0, 600) + \"...\"\n : article.content\n\n const chunk = header + \"\\n\" + body\n if (totalChars + chunk.length > maxChars) break\n\n lines.push(chunk)\n totalChars += chunk.length\n }\n\n lines.push(\"\\n[End Wiki Knowledge]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Rebuild the master index.\n */\n rebuildIndex(): WikiIndex {\n const articles: WikiIndex[\"articles\"] = []\n const backlinks = new Map<string, number>()\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n // Count wikilinks for backlinks\n const links = article.content.match(/\\[\\[([^\\]]+)\\]\\]/g) || []\n for (const link of links) {\n const target = link.replace(/\\[\\[|\\]\\]/g, \"\")\n backlinks.set(target, (backlinks.get(target) || 0) + 1)\n }\n\n // Extract aliases from title and tags\n const aliases = [article.meta.title.toLowerCase()]\n if (article.meta.tags) {\n aliases.push(...article.meta.tags.map(t => t.toLowerCase()))\n }\n\n articles.push({\n path: relPath,\n title: article.meta.title,\n type: article.meta.type,\n related: article.meta.related,\n tags: article.meta.tags || [],\n owner: article.meta.owner,\n access: article.meta.access,\n sharedWith: article.meta.sharedWith,\n aliases,\n backlinks: backlinks.get(article.meta.title) || 0,\n sources: article.meta.sources,\n lastUpdated: article.meta.lastUpdated,\n })\n })\n\n const index: WikiIndex = {\n articles,\n lastRebuilt: new Date().toISOString(),\n }\n\n // Machine-readable index\n writeFileSync(\n resolve(this.baseDir, \"_index.json\"),\n JSON.stringify(index, null, 2),\n )\n\n // Farzapedia-style content catalog at _index.md — the one file the\n // agentic query reads to pick candidate articles by `type` + title.\n // Load-bearing in Phase 3; grouped by type, alphabetical within group.\n this.writeCatalog(articles, index.lastRebuilt)\n\n // Human-readable WIKI.md — legacy view, grouped by access level.\n const md = [\"# Wiki Index\", \"\", `Last rebuilt: ${index.lastRebuilt}`, \"\"]\n const byAccess = new Map<string, typeof articles>()\n for (const a of articles) {\n const bucket = a.access || \"public\"\n const list = byAccess.get(bucket) || []\n list.push(a)\n byAccess.set(bucket, list)\n }\n for (const [bucket, list] of Array.from(byAccess.entries()).sort()) {\n md.push(`## ${bucket}`, \"\")\n for (const a of list.sort((x, y) => x.title.localeCompare(y.title))) {\n const t = a.type ? ` [${a.type}]` : \"\"\n md.push(`- [${a.title}](${a.path})${t} — owner: ${a.owner}`)\n }\n md.push(\"\")\n }\n writeFileSync(resolve(this.baseDir, \"WIKI.md\"), md.join(\"\\n\"))\n\n this.log(`Index rebuilt: ${articles.length} articles`)\n this.appendLog(\"rebuild-index\", `${articles.length} articles indexed`)\n return index\n }\n\n /**\n * Write `_index.md` — the Farzapedia-style content catalog grouped by\n * article `type`. This is the one file the agentic query (Phase 3) reads\n * to pick candidate articles before walking the wikilink subgraph.\n *\n * Articles without a `type` land in \"Untyped\" at the bottom; the Phase 2\n * migration backfills those.\n */\n private writeCatalog(\n articles: WikiIndex[\"articles\"],\n lastRebuilt: string,\n ): void {\n const typeOrder: Array<NonNullable<WikiArticleMeta[\"type\"]> | \"untyped\"> = [\n \"person\", \"project\", \"place\", \"concept\", \"event\", \"decision\", \"pattern\", \"untyped\",\n ]\n const typeHeaders: Record<string, string> = {\n person: \"## People\",\n project: \"## Projects\",\n place: \"## Places\",\n concept: \"## Concepts\",\n event: \"## Events\",\n decision: \"## Decisions\",\n pattern: \"## Patterns\",\n untyped: \"## Untyped (needs migration)\",\n }\n\n const byType = new Map<string, typeof articles>()\n for (const a of articles) {\n const bucket = a.type || \"untyped\"\n const list = byType.get(bucket) || []\n list.push(a)\n byType.set(bucket, list)\n }\n\n const lines: string[] = [\n \"# Wiki Content Catalog\",\n \"\",\n `_Last rebuilt: ${lastRebuilt}_`,\n \"\",\n \"Articles grouped by type. Agentic query walks this file first to pick candidates.\",\n \"\",\n ]\n\n for (const type of typeOrder) {\n const list = byType.get(type)\n if (!list || list.length === 0) continue\n lines.push(typeHeaders[type] || `## ${type}`, \"\")\n for (const a of list.sort((x, y) => x.title.localeCompare(y.title))) {\n const access = a.access === \"private\" ? \" (private)\" : a.access === \"shared\" ? \" (shared)\" : \"\"\n const related = a.related?.length ? ` → [[${a.related.slice(0, 3).join(\"]], [[\")}]]${a.related.length > 3 ? \", …\" : \"\"}` : \"\"\n lines.push(`- [${a.title}](${a.path})${access}${related}`)\n }\n lines.push(\"\")\n }\n\n writeFileSync(resolve(this.baseDir, \"_index.md\"), lines.join(\"\\n\"))\n }\n\n /**\n * Get wiki stats.\n */\n stats(): {\n totalArticles: number\n totalEntries: number\n articlesByType: Record<string, number>\n articlesByAccess: Record<string, number>\n articlesByOwner: Record<string, number>\n } {\n let totalArticles = 0\n const byType: Record<string, number> = {}\n const byAccess: Record<string, number> = {}\n const byOwner: Record<string, number> = {}\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\") return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n totalArticles++\n for (const tag of (article.meta.tags || [])) {\n byType[tag] = (byType[tag] || 0) + 1\n }\n byAccess[article.meta.access] = (byAccess[article.meta.access] || 0) + 1\n byOwner[article.meta.owner] = (byOwner[article.meta.owner] || 0) + 1\n })\n\n const entryFiles = existsSync(this.rawDir)\n ? readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).length\n : 0\n\n return {\n totalArticles,\n totalEntries: entryFiles,\n articlesByType: byType,\n articlesByAccess: byAccess,\n articlesByOwner: byOwner,\n }\n }\n\n // --- Helpers ---\n\n // --- Log (chronological, append-only) ---\n\n /**\n * Append an entry to log.md.\n * Format: ## [YYYY-MM-DD HH:MM] action | details\n */\n appendLog(action: string, details: string): void {\n const logPath = resolve(this.baseDir, \"log.md\")\n const timestamp = new Date().toISOString().replace(\"T\", \" \").slice(0, 16)\n const entry = `## [${timestamp}] ${action} | ${details}\\n\\n`\n\n if (!existsSync(logPath)) {\n writeFileSync(logPath, \"# Wiki Log\\n\\nChronological record of all wiki operations.\\n\\n\")\n }\n\n appendFileSync(logPath, entry)\n }\n\n /**\n * Get recent log entries.\n */\n getLog(limit: number = 20): string[] {\n const logPath = resolve(this.baseDir, \"log.md\")\n if (!existsSync(logPath)) return []\n\n const content = readFileSync(logPath, \"utf-8\")\n const entries = content.split(/^## /m).filter(e => e.startsWith(\"[\")).slice(-limit)\n return entries.map(e => e.trim())\n }\n\n // --- Schema ---\n\n /**\n * Create _schema.md if it doesn't exist.\n * This tells the LLM how to operate on the wiki.\n */\n private ensureSchema(): void {\n const schemaPath = resolve(this.baseDir, \"_schema.md\")\n if (existsSync(schemaPath)) return\n\n const schema = `# Wiki Schema\n\nA personal wiki maintained by LLMs. Karpathy pattern: plain files, aggressive tagging,\nstructure emerges from data. The wiki is a compounding artifact.\n\n## Structure\n\n\\`\\`\\`\nwiki/\n _schema.md # This file\n worldview.md # YOUR mental model — the LLM reads this during absorb\n raw/entries/ # Immutable raw sources (conversations, imports, anything)\n [LLM-chosen dirs] # Structure emerges from data — the LLM picks paths\n\\`\\`\\`\n\nThe LLM decides how to organize articles. You describe YOUR world in worldview.md.\n\n## Article Format\n\n\\`\\`\\`yaml\n---\ntitle: \"Staging Deployment Process\"\ntags: [\"deploy\", \"staging\", \"devops\", \"process\", \"2026-04-06\"]\nowner: devops-agent\naccess: public\ncreated: 2026-04-06\nlast_updated: 2026-04-06\nsources: [\"entry-id-1\", \"entry-id-2\"]\n---\n\nArticle content with [[wikilinks]] and section tags.\n\n## Deploy Steps\n<!-- tags: process, runbook, staging -->\nContent specific to this section...\n\\`\\`\\`\n\n## Tags\n\nTags are the primary context-narrowing mechanism. The more tags, the better.\n\nTag aggressively:\n- **Who**: people, agents, teams involved\n- **What**: project, client, topic, technology\n- **When**: dates, periods, milestones\n- **Where**: server, environment, channel\n- **How**: process, decision, pattern\n- **Your terms**: whatever vocabulary makes sense in YOUR world\n\nSection tags (\\`<!-- tags: ... -->\\`) let different parts of an article\nmatch different queries. A deploy article might have sections tagged\n\"runbook\" and \"incident\" separately.\n\n## Operations\n\n### Absorb\nRead raw entries → read worldview.md → tag aggressively → create/update articles.\nAlso identify GAPS: \"We mention X but have no article for it.\"\n\n### Query\nMatch by tags first → then keyword within matched articles → synthesize.\nIf agent is working on a specific project, only send content tagged for that project.\n\n### Lint\nCheck for: orphan articles, broken [[wikilinks]], untagged articles,\narticles over 100 lines that should split, mentioned-but-missing topics.\n\n## Worldview\n\nEdit \\`worldview.md\\` to describe YOUR world. This is not a schema — it's your\nmental model. The LLM reads it during absorb to understand where things go.\n\nExample worldview.md:\n\\`\\`\\`\nI'm the founder of Acme Corp.\nWe build software products.\nOur agents: Marketing Bot, DevOps Bot, Coordinator.\nClients: ClientA (enterprise), ClientB (startup).\nInfrastructure: local dev machine, cloud server.\nWe use GitLab for version control.\n\\`\\`\\`\n`\n\n writeFileSync(schemaPath, schema)\n this.log(\"Created _schema.md\")\n }\n\n // --- Wikilinks and Backlinks ---\n\n /**\n * Extract all [[wikilinks]] from article content.\n */\n extractWikilinks(content: string): string[] {\n const matches = content.match(/\\[\\[([^\\]]+)\\]\\]/g) || []\n return matches.map(m => m.replace(/\\[\\[|\\]\\]/g, \"\"))\n }\n\n /**\n * Build backlinks index: for each article, which other articles link to it.\n */\n buildBacklinks(): Record<string, string[]> {\n const backlinks: Record<string, string[]> = {}\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\" || relPath === \"log.md\") return\n\n const content = readFileSync(filePath, \"utf-8\")\n const links = this.extractWikilinks(content)\n\n for (const link of links) {\n if (!backlinks[link]) backlinks[link] = []\n backlinks[link].push(relPath)\n }\n })\n\n // Save\n writeFileSync(\n resolve(this.baseDir, \"_backlinks.json\"),\n JSON.stringify(backlinks, null, 2),\n )\n\n return backlinks\n }\n\n /**\n * Get backlinks for a specific article title.\n */\n getBacklinks(title: string): string[] {\n const path = resolve(this.baseDir, \"_backlinks.json\")\n if (!existsSync(path)) return []\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\"))\n return data[title] || []\n } catch {\n return []\n }\n }\n\n // --- Tag-based filtering (Karpathy: tags are the context narrowing mechanism) ---\n\n /**\n * Find articles matching ANY of the given tags.\n * This is how agents get relevant context — only articles tagged with\n * the client/project/topic they're working on.\n */\n findByTags(tags: string[], agentId?: string, limit: number = 10): WikiArticle[] {\n const lowerTags = tags.map(t => t.toLowerCase())\n const results: Array<{ article: WikiArticle; score: number }> = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article) return\n if (agentId && !this.canRead(article.meta, agentId)) return\n\n // Score: how many of the requested tags match?\n const articleTags = (article.meta.tags || []).map(t => t.toLowerCase())\n // Also check section tags: <!-- tags: foo, bar -->\n const sectionTags = (article.content.match(/<!--\\s*tags?:\\s*([^>]+)\\s*-->/gi) || [])\n .flatMap(m => m.replace(/<!--\\s*tags?:\\s*|\\s*-->/gi, \"\").split(\",\").map(t => t.trim().toLowerCase()))\n\n const allTags = [...articleTags, ...sectionTags]\n const score = lowerTags.filter(t => allTags.includes(t)).length\n\n if (score > 0) {\n results.push({ article, score })\n }\n })\n\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map(r => r.article)\n }\n\n /**\n * Get all unique tags across all articles.\n */\n getAllTags(): Map<string, number> {\n const tagCounts = new Map<string, number>()\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article?.meta.tags) return\n\n for (const tag of article.meta.tags) {\n tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1)\n }\n })\n\n return tagCounts\n }\n\n // --- Worldview (user's mental model — input to absorb) ---\n\n /**\n * Get the worldview file. This is the user's description of their world —\n * the LLM uses it during absorb to know where things go.\n * Returns null if not set.\n */\n getWorldview(): string | null {\n const path = resolve(this.baseDir, \"worldview.md\")\n if (!existsSync(path)) return null\n return readFileSync(path, \"utf-8\")\n }\n\n /**\n * Set the worldview. The user describes their world: companies, clients,\n * projects, team, processes. The LLM uses this as context during absorb.\n */\n setWorldview(content: string): void {\n writeFileSync(resolve(this.baseDir, \"worldview.md\"), content)\n this.appendLog(\"worldview\", \"Updated worldview\")\n }\n\n // --- Lint ---\n\n /**\n * Run a lint pass on the wiki. Returns issues found.\n */\n lint(): Array<{ type: string; article: string; message: string }> {\n const issues: Array<{ type: string; article: string; message: string }> = []\n const allTitles = new Set<string>()\n const allLinks = new Map<string, string[]>() // article -> outbound links\n const articlePaths = new Map<string, string>() // title -> path\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\" || relPath === \"log.md\") return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n allTitles.add(article.meta.title)\n articlePaths.set(article.meta.title, relPath)\n\n const links = this.extractWikilinks(article.content)\n allLinks.set(relPath, links)\n\n // Check: article too long\n const lines = article.content.split(\"\\n\").length\n if (lines > 100) {\n issues.push({\n type: \"bloated\",\n article: relPath,\n message: `${lines} lines — consider splitting`,\n })\n }\n\n // Check: no sources\n if (!article.meta.sources?.length) {\n issues.push({\n type: \"unsourced\",\n article: relPath,\n message: \"No sources listed in frontmatter\",\n })\n }\n\n // Check: stub (too short)\n if (lines < 10 && article.content.trim().length < 100) {\n issues.push({\n type: \"stub\",\n article: relPath,\n message: \"Very short article — needs enrichment\",\n })\n }\n })\n\n // Check: broken wikilinks\n for (const [articlePath, links] of allLinks) {\n for (const link of links) {\n if (!allTitles.has(link)) {\n issues.push({\n type: \"broken-link\",\n article: articlePath,\n message: `[[${link}]] — target article not found`,\n })\n }\n }\n }\n\n // Check: orphan articles (no inbound links)\n const backlinks = this.buildBacklinks()\n for (const [title, path] of articlePaths) {\n if (!backlinks[title] || backlinks[title].length === 0) {\n issues.push({\n type: \"orphan\",\n article: path,\n message: `No other articles link to \"${title}\"`,\n })\n }\n }\n\n this.appendLog(\"lint\", `Found ${issues.length} issues`)\n return issues\n }\n\n // --- Absorb summary (for automated cron) ---\n\n /**\n * Get unabsorbed entries (entries not referenced by any article's sources field).\n */\n getUnabsorbedEntries(): WikiEntry[] {\n const allSources = new Set<string>()\n\n // Collect all source IDs referenced by articles\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (article?.meta.sources) {\n for (const s of article.meta.sources) allSources.add(s)\n }\n })\n\n // Find entries not in any article's sources\n const entries = this.listEntries()\n return entries.filter(e => !allSources.has(e.id))\n }\n\n /**\n * Build an absorb prompt for unprocessed entries.\n * Returns a prompt string an agent can execute to compile entries into articles.\n */\n buildAbsorbPrompt(maxEntries: number = 20): string | null {\n const unabsorbed = this.getUnabsorbedEntries().slice(0, maxEntries)\n if (unabsorbed.length === 0) return null\n\n const entrySummaries = unabsorbed.map(e =>\n `[${e.date} ${e.agentId} via ${e.source}] ${e.content.slice(0, 200)}`\n ).join(\"\\n\\n\")\n\n return `/wiki absorb\\n\\nThere are ${unabsorbed.length} unprocessed entries. Read each, understand what it means, and create or update wiki articles.\\n\\n${entrySummaries}`\n }\n\n private walkDir(dir: string, callback: (filePath: string) => void): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const stat = statSync(full)\n if (stat.isDirectory()) {\n this.walkDir(full, callback)\n } else {\n callback(full)\n }\n }\n }\n}\n","import { WikiStore } from \"./store\"\nimport { resolve } from \"path\"\nimport { existsSync, readdirSync, mkdirSync } from \"fs\"\nimport type { WikiEntry } from \"./types\"\n\nexport type WikiMode = \"flat\" | \"graph\" | \"unified\"\n\n/**\n * WikiHub: manages per-agent wikis with a shared raw entry pool.\n * Supports two compilation modes:\n * - flat: Karpathy pattern — tags, LLM-chosen paths, worldview, gap detection\n * - graph: Knowledge graph — kind, parent, hierarchy, events, entities\n *\n * Both modes share the same raw entries. Articles stored separately:\n * agents/<id>/flat/ ← Karpathy compilation\n * agents/<id>/graph/ ← Knowledge graph compilation\n */\nexport class WikiHub {\n private baseDir: string\n private agentsDir: string\n private mode: WikiMode\n private sharedStore: WikiStore\n private agentStores: Map<string, WikiStore> = new Map()\n private log: (...args: unknown[]) => void\n\n constructor(\n baseDir: string = resolve(process.cwd(), \".agentx/wiki\"),\n log: (...args: unknown[]) => void = console.error.bind(console, \"[wiki-hub]\"),\n mode: WikiMode = \"graph\",\n ) {\n this.baseDir = baseDir\n this.agentsDir = resolve(baseDir, \"agents\")\n this.mode = mode\n this.log = log\n\n mkdirSync(this.agentsDir, { recursive: true })\n this.sharedStore = new WikiStore(baseDir, log)\n }\n\n getMode(): WikiMode { return this.mode }\n\n /**\n * Get or create the wiki store for a specific agent (mode-aware).\n * flat: agents/<id>/flat/\n * graph: agents/<id>/graph/\n */\n getAgentWiki(agentId: string): WikiStore {\n const key = `${agentId}:${this.mode}`\n if (this.agentStores.has(key)) return this.agentStores.get(key)!\n\n const agentDir = resolve(this.agentsDir, agentId, this.mode)\n const store = new WikiStore(agentDir, this.log)\n this.agentStores.set(key, store)\n return store\n }\n\n getSharedStore(): WikiStore {\n return this.sharedStore\n }\n\n listAgents(): string[] {\n const agents = new Set<string>()\n\n const entries = this.sharedStore.listEntries()\n for (const e of entries) agents.add(e.agentId)\n\n if (existsSync(this.agentsDir)) {\n for (const dir of readdirSync(this.agentsDir)) {\n if (!dir.startsWith(\"_\") && !dir.startsWith(\".\")) {\n agents.add(dir)\n }\n }\n }\n\n return [...agents].sort()\n }\n\n getAgentEntries(agentId: string): WikiEntry[] {\n return this.sharedStore.listEntries({ agentId })\n }\n\n getUnabsorbedEntries(agentId: string): WikiEntry[] {\n const agentWiki = this.getAgentWiki(agentId)\n const agentEntries = this.getAgentEntries(agentId)\n\n const absorbedIds = new Set<string>()\n const index = agentWiki.rebuildIndex()\n for (const article of index.articles) {\n if (article.sources) {\n for (const s of article.sources) absorbedIds.add(s)\n }\n }\n\n return agentEntries.filter(e => !absorbedIds.has(e.id))\n }\n\n summary(): AgentWikiSummary[] {\n const agents = this.listAgents()\n return agents.map(agentId => {\n const entries = this.getAgentEntries(agentId)\n const wiki = this.getAgentWiki(agentId)\n const index = wiki.rebuildIndex()\n const unabsorbed = this.getUnabsorbedEntries(agentId)\n\n return {\n agentId,\n totalEntries: entries.length,\n totalArticles: index.articles.length,\n unabsorbed: unabsorbed.length,\n articles: index.articles,\n }\n })\n }\n}\n\nexport interface AgentWikiSummary {\n agentId: string\n totalEntries: number\n totalArticles: number\n unabsorbed: number\n articles: Array<{ title: string; path: string; tags?: string[] }>\n}\n"],"mappings":"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","names":["STARTER_SCHEMA","STARTER_VERB_NODES","readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","renameSync","resolve","dirname","createHash","GraphStore","opts","resolve","mkdirSync","p","existsSync","STARTER_SCHEMA","raw","readFileSync","parsed","graphSchemaSchema","i","backup","writeFileSync","e","np","nbackup","seeded","seedNodesFile","schema","safeWriteJson","nodesFileSchema","file","node","all","level","l","missing","a","v","n","target","levelIdx","parent","parentLevelIdx","path","proposedAxes","createdBy","added","skipped","now","id","current","cursor","j","priorId","priorNode","axes","chosenLevel","parentId","chosenIdx","prior","priorIdx","c","classificationSchema","issues","line","dirname","appendFileSync","limit","status","lines","byHash","b","msgHash","update","next","suffix","out","msg","payload","createHash","indexFileSchema","fp","entry","updated","child","refHolder","known","data","tmp","renameSync","nodes","seenCats","STARTER_VERB_NODES","init_extract_json","init_providers","createHash","Classifier","opts","createProvider","input","fp","cached","hashPath","pathLabel","schema","nodes","proposal","e","path","proposedAxes","confidence","leaf","classification","existingIds","n","newNodeIndices","id","idx","structureOk","isReuseOnly","isSingleLeafAddition","confidenceOk","status","nodesForPrompt","userPrompt","text","parsed","extractJson","NODE_ID_RE","slugMap","s","trimmed","slugged","slugifyNodeId","rawNodeId","axes","nodeId","clean","k","v","_existing","createdBy","provider","ac","timer","raw","x","ancestryScore","a","b","shared","i","depth","readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","readdirSync","statSync","resolve","join","relative","dirname","init_seat","WikiStore","baseDir","resolve","log","mkdirSync","article","agentId","entry","filename","filepath","frontmatter","k","v","writeFileSync","filter","existsSync","files","readdirSync","f","entries","file","content","readFileSync","fmMatch","fm","body","get","key","KNOWN","meta","line","m","path","existing","isWikiArticleType","fullPath","dirname","t","s","action","articlePath","ts","versionDir","i","versionTimestamp","version","raw","getArray","rawType","type","tags","a","related","graphPath","articles","filePath","relPath","relative","query","maxResults","queryLower","docs","cachePath","index","buildIndexCached","bm25Scores","scoreAll","scoreMap","r","score","b","opts","shortlist","DEFAULT_SHORTLIST","pool","candidates","result","askSeat","WIKI_RERANK_SEAT","rerankState","rerankQuestions","toRanking","id","message","maxArticles","messagePath","weights","maxBm25","bm25Map","hasGraph","bm25","ancestry","ancestryScore","combined","maxChars","lines","totalChars","header","chunk","backlinks","links","link","target","aliases","md","byAccess","bucket","list","x","y","lastRebuilt","typeOrder","typeHeaders","byType","access","totalArticles","byOwner","tag","entryFiles","details","logPath","appendFileSync","limit","e","schemaPath","title","lowerTags","results","articleTags","sectionTags","allTags","tagCounts","issues","allTitles","allLinks","articlePaths","allSources","maxEntries","unabsorbed","entrySummaries","dir","callback","full","join","statSync","resolve","existsSync","readdirSync","mkdirSync","WikiHub","baseDir","log","mode","WikiStore","agentId","key","agentDir","store","agents","entries","e","dir","agentWiki","agentEntries","absorbedIds","index","article","s","unabsorbed"]}
@@ -165,7 +165,7 @@ var d={};function g(r){d=r}function v(){return d}var w=`<link rel="preconnect" h
165
165
  // lives outside /api/admin for legacy reasons, but the page is part
166
166
  // of the admin surface and should proxy when a non-primary peer is
167
167
  // selected (otherwise the workflows page on Mac shows local-only
168
- // YAMLs while the runtime lives on clawd).
168
+ // YAMLs while the runtime lives on peer).
169
169
  function shouldProxy(url){
170
170
  if (!url) return false;
171
171
  // Admin-side surfaces that follow the mesh selector. Each entry
@@ -236,7 +236,7 @@ var d={};function g(r){d=r}function v(){return d}var w=`<link rel="preconnect" h
236
236
  <span class="name">${a(p.name)}</span>
237
237
  <span class="chev">\u25BE</span>
238
238
  <div class="ax-mesh-menu" role="menu">${x}</div>
239
- </div>`,u=[{name:"Mesh",tabs:[{id:"live",label:"Live",href:"/live"},{id:"mesh",label:"Operations",href:"/mesh"},{id:"admin",label:"Settings",href:"/admin"}]}].map(e=>{let n=e.tabs.map(o=>{let i=o.id===r.activeTab?"ax-topbar__tab is-active":"ax-topbar__tab",s=o.external?' target="_blank" rel="noopener"':"",l=o.external?' <span style="font-size:9px;opacity:0.6">\u2197</span>':"";return`<a href="${o.href}" class="${i}"${s}>${o.label}${l}</a>`}).join("");return`<div class="ax-topbar__group" title="${a(e.name)} tier"><span class="ax-topbar__group-label">${a(e.name)}</span>${n}</div>`}).join("");return`<header class="ax-topbar">
239
+ </div>`,u=[{name:"Mesh",tabs:[{id:"live",label:"Live",href:"/live"},{id:"mesh",label:"Operations",href:"/mesh"},{id:"monitor",label:"Monitor",href:"/monitor"},{id:"activity",label:"Activity",href:"/activity"},{id:"workflows",label:"Workflows",href:"/workflows"},{id:"admin",label:"Settings",href:"/admin"}]}].map(e=>{let n=e.tabs.map(o=>{let i=o.id===r.activeTab?"ax-topbar__tab is-active":"ax-topbar__tab",s=o.external?' target="_blank" rel="noopener"':"",l=o.external?' <span style="font-size:9px;opacity:0.6">\u2197</span>':"";return`<a href="${o.href}" class="${i}"${s}>${o.label}${l}</a>`}).join("");return`<div class="ax-topbar__group" title="${a(e.name)} tier"><span class="ax-topbar__group-label">${a(e.name)}</span>${n}</div>`}).join("");return`<header class="ax-topbar">
240
240
  <div class="ax-topbar__left">
241
241
  <div class="ax-brand">
242
242
  <span class="ax-brand__mark">AX</span>
@@ -254,4 +254,4 @@ var d={};function g(r){d=r}function v(){return d}var w=`<link rel="preconnect" h
254
254
  </div>
255
255
  </div>
256
256
  </header>${r.subheader?`<div class="ax-subheader">${r.subheader}</div>`:""}`}function a(r){return String(r||"").replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t])}export{g as a,v as b,w as c,y as d,k as e,_ as f};
257
- //# sourceMappingURL=chunk-LDHPMJ7M.js.map
257
+ //# sourceMappingURL=chunk-IWMW5432.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/topbar.ts"],"sourcesContent":["// --- Shared top-chrome for every dashboard surface ---\n//\n// Four pages render in different files (live + kanban + glossary in\n// board-dashboard.ts, settings in admin-panel.ts) but must share: design\n// tokens, fonts, theme switcher, brand, primary tab nav, and a mesh-node\n// selector. This module exports the CSS + HTML snippets so each page\n// composes exactly the same chrome and we never drift them again.\n//\n// Nothing here is page-specific — callers pass in { activeTab, subtitle,\n// subheader? } and compose their own <main> below.\n\nexport type TopbarTab = \"monitor\" | \"activity\" | \"live\" | \"mesh\" | \"boards\" | \"admin\" | \"graph\" | \"glossary\" | \"workflows\" | \"health\" | \"cost\" | \"wiki\" | \"procedures\" | \"inbox\" | \"projects\"\n\nexport interface TopbarPeer {\n /** Stable id: primary node id, or URL for configured daemons */\n id: string\n name: string\n /** Absolute URL of the peer's dashboard (http://host:port) — selector\n * navigates to <url>/admin when switched. */\n dashboardUrl: string\n /** True for the node hosting this dashboard (locks the default). */\n primary?: boolean\n /** Optional; shown in the dropdown as \"managing via token\" hint. */\n tokenScope?: string\n}\n\nexport interface TopbarFeatures {\n /** Show the Team/Business Settings sub-tabs — config.business.enabled. */\n business?: boolean\n}\n\n// Set once at server start (board-dashboard + daemon both call it).\n//\n// This used to carry `boards` and `workflows` flags that gated top-level\n// tabs. The nav is now fixed (Live, Operations, Settings), so those flags had\n// nothing left to gate and were removed rather than left describing chrome\n// that no longer exists.\nlet topbarFeatures: TopbarFeatures = {}\n\nexport function setTopbarFeatures(features: TopbarFeatures): void {\n topbarFeatures = features\n}\n\nexport function getTopbarFeatures(): TopbarFeatures {\n return topbarFeatures\n}\n\nexport interface TopbarOpts {\n activeTab: TopbarTab\n /** Text after the brand, e.g. \"Live\" / \"Settings\" / \"Boards\". */\n subtitle: string\n /** Optional HTML inserted as a second row under the topbar. */\n subheader?: string\n /** Peers to list in the mesh selector. */\n peers?: TopbarPeer[]\n /** id of the peer currently being managed — defaults to the primary. */\n currentPeerId?: string\n /** Optional right-side HTML added before the theme switcher (clocks,\n * connection dots, etc.). */\n rightExtras?: string\n}\n\n/**\n * Font link tags + tiny inline bootstrap script. Callers drop this in <head>.\n */\n// Fonts degrade to the system stack when the box is offline or air-gapped —\n// the tokens list real fallbacks, so a failed font fetch costs typography,\n// never layout.\n//\n// The theme bootstrap runs before first paint to avoid a flash. A stored\n// \"crt\" from before that theme was removed falls back to dark rather than\n// leaving data-theme set to a value no stylesheet defines.\nexport const TOPBAR_HEAD = `<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&family=Roboto+Mono:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n<script>(function(){try{var t=localStorage.getItem('ax-theme');if(t!=='light'&&t!=='dark'){t='dark';localStorage.setItem('ax-theme',t)}document.documentElement.setAttribute('data-theme',t)}catch(e){document.documentElement.setAttribute('data-theme','dark')}})();</script>`\n\n/**\n * CSS for topbar + subheader + theme switch + mesh selector. No token\n * definitions — those are per-page because each page's CSS block owns its\n * own :root overrides.\n */\nexport const TOPBAR_CSS = `\n.ax-topbar{display:flex;align-items:center;justify-content:space-between;\n padding:8px 18px;border-bottom:1px solid var(--ax-border);\n background:var(--ax-bg-elev);position:sticky;top:0;z-index:20;gap:10px;flex-wrap:nowrap}\n.ax-topbar__left{display:flex;align-items:center;gap:18px;min-width:0}\n.ax-brand{display:flex;align-items:center;gap:10px;font-weight:600;letter-spacing:-0.01em;flex-shrink:0}\n.ax-brand__mark{font-family:var(--ax-mono);font-size:12px;padding:2px 8px;\n border:1px solid var(--ax-border-2);color:var(--ax-accent);border-radius:4px}\n.ax-brand__name{font-size:14px}\n.ax-brand__subtitle{font-size:11px;color:var(--ax-muted);\n border-left:1px solid var(--ax-border);padding-left:12px;margin-left:4px}\n.ax-topbar__tabs{display:flex;gap:14px;min-width:0;overflow-x:auto;align-items:center}\n/* Tier groups (System / Processes / Procedures) — visual separators\n * mirror the docs/architecture/three-tier.md hierarchy. */\n.ax-topbar__group{display:flex;align-items:center;gap:0;padding:0 4px;\n border-left:1px solid var(--ax-border);position:relative}\n.ax-topbar__group:first-child{border-left:none;padding-left:0}\n.ax-topbar__group-label{font:inherit;font-family:var(--ax-mono);font-size:9px;\n letter-spacing:0.08em;text-transform:uppercase;color:var(--ax-muted);\n padding:0 8px;white-space:nowrap;opacity:0.7}\n.ax-topbar__tab{background:transparent;border:none;color:var(--ax-text-2);\n padding:8px 12px;font:inherit;cursor:pointer;font-size:12px;text-decoration:none;\n border-bottom:2px solid transparent;letter-spacing:-0.005em;white-space:nowrap}\n.ax-topbar__tab:hover{color:var(--ax-text)}\n.ax-topbar__tab.is-active{color:var(--ax-text);border-bottom-color:var(--ax-accent)}\n.ax-topbar__right{display:flex;align-items:center;gap:10px;font-size:11px;\n color:var(--ax-muted);flex-shrink:0}\n.ax-topbar__right .ax-mono{color:var(--ax-text-2)}\n\n/* Mesh selector */\n.ax-mesh-sel{position:relative;display:inline-flex;align-items:center;gap:6px;\n padding:4px 10px;border:1px solid var(--ax-border-2);border-radius:4px;\n background:var(--ax-surface);cursor:pointer;font:inherit;font-size:11px;\n color:var(--ax-text-2);white-space:nowrap}\n.ax-mesh-sel:hover{border-color:var(--ax-accent);color:var(--ax-text)}\n.ax-mesh-sel.is-proxy{background:color-mix(in oklch,var(--ax-accent) 14%,var(--ax-surface));\n border-color:color-mix(in oklch,var(--ax-accent) 45%,var(--ax-border-2));color:var(--ax-accent)}\n.ax-mesh-sel .lbl{font-size:9px;letter-spacing:0.06em;text-transform:uppercase;color:var(--ax-muted);margin-right:2px}\n.ax-mesh-sel .name{font-weight:500;color:var(--ax-text)}\n.ax-mesh-sel .chev{font-size:9px;opacity:0.6;margin-left:2px}\n.ax-mesh-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:240px;\n background:var(--ax-surface);border:1px solid var(--ax-border-2);\n border-radius:6px;box-shadow:0 8px 24px rgba(0,0,0,0.4);padding:4px;z-index:30;display:none}\n.ax-mesh-sel.is-open .ax-mesh-menu{display:block}\n.ax-mesh-menu a{display:block;padding:7px 10px;border-radius:4px;text-decoration:none;\n color:var(--ax-text-2);font-size:12px}\n.ax-mesh-menu a:hover{background:var(--ax-surface-2);color:var(--ax-text)}\n.ax-mesh-menu a.is-current{background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface));\n color:var(--ax-accent);border:1px solid color-mix(in oklch,var(--ax-accent) 30%,var(--ax-border-2))}\n.ax-mesh-menu a .row{display:flex;align-items:center;gap:8px}\n.ax-mesh-menu a .row .dot{width:6px;height:6px;border-radius:50%;background:var(--ax-accent)}\n.ax-mesh-menu a.is-offline .row .dot{background:var(--ax-border-2)}\n.ax-mesh-menu a .url{font-family:var(--ax-mono);font-size:10px;color:var(--ax-muted);margin-top:2px}\n\n/* Theme switcher segmented control */\n.ax-theme-switch{display:inline-flex;border:1px solid var(--ax-border-2);\n border-radius:4px;overflow:hidden}\n.ax-theme-switch button{background:transparent;border:none;color:var(--ax-muted);\n padding:3px 9px;font:inherit;font-size:10px;cursor:pointer;letter-spacing:0.04em;\n text-transform:uppercase;font-family:var(--ax-mono);border-right:1px solid var(--ax-border-2)}\n.ax-theme-switch button:last-child{border-right:none}\n.ax-theme-switch button:hover{color:var(--ax-text);background:var(--ax-surface-2,var(--ax-surface))}\n.ax-theme-switch button.is-active{color:var(--ax-accent);\n background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface))}\n\n/* Subheader — page-specific toolbar below the main topbar. Kanban uses it\n * for the board picker + search + filters; others can use it for context\n * bars if they want. */\n.ax-subheader{display:flex;align-items:center;gap:10px;padding:10px 18px;\n background:var(--ax-bg-elev);border-bottom:1px solid var(--ax-border);\n flex-wrap:wrap}\n.ax-subheader > * {flex-shrink:0}\n.ax-subheader .spacer{flex:1}`\n\n/**\n * Shared click wiring for the theme switcher + mesh selector. Safe to call\n * multiple times — only binds once per element.\n */\nexport const TOPBAR_SCRIPT = `<script>\n(function(){\n function wireTheme(){\n var sw = document.querySelectorAll('[data-theme-opt]');\n if (!sw.length) return;\n var current = document.documentElement.getAttribute('data-theme') || 'dark';\n sw.forEach(function(b){\n if (b.dataset.wired) return; b.dataset.wired = '1';\n if (b.getAttribute('data-theme-opt') === current) b.classList.add('is-active');\n b.addEventListener('click', function(){\n var t = b.getAttribute('data-theme-opt');\n document.documentElement.setAttribute('data-theme', t);\n try { localStorage.setItem('ax-theme', t); } catch (e) {}\n sw.forEach(function(x){ x.classList.toggle('is-active', x === b); });\n });\n });\n }\n function currentPeer(){\n try { return localStorage.getItem('ax-peer') || 'primary'; } catch { return 'primary'; }\n }\n function setCurrentPeer(id){\n try {\n if (id && id !== 'primary') localStorage.setItem('ax-peer', id);\n else localStorage.removeItem('ax-peer');\n } catch {}\n }\n function reflectPeerPill(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel) return;\n var cur = currentPeer();\n sel.classList.toggle('is-proxy', cur !== 'primary');\n // If a proxy is active, update the visible pill label + highlight.\n var picked = null;\n sel.querySelectorAll('.ax-mesh-menu a').forEach(function(a){\n a.classList.toggle('is-current', a.getAttribute('data-peer-id') === cur || (cur === 'primary' && a.getAttribute('data-peer-id') === 'primary'));\n if (a.getAttribute('data-peer-id') === cur) picked = a;\n });\n if (picked) {\n var name = picked.querySelector('span'); // first <span> inside .row\n var visibleName = sel.querySelector('.name');\n if (name && visibleName) visibleName.textContent = name.textContent.replace(/ · primary/, '');\n }\n }\n function wireMesh(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel || sel.dataset.wired) return; sel.dataset.wired = '1';\n sel.addEventListener('click', function(e){\n var link = e.target.closest('.ax-mesh-menu a[data-peer-action=\"proxy\"]');\n if (link) {\n e.preventDefault();\n setCurrentPeer(link.getAttribute('data-peer-id'));\n reflectPeerPill();\n sel.classList.remove('is-open');\n // Reload so the admin panel re-reads state from the selected peer.\n location.reload();\n return;\n }\n if (e.target.closest('.ax-mesh-menu a')) return; // plain navigate anchor\n sel.classList.toggle('is-open');\n });\n document.addEventListener('click', function(e){\n if (!sel.contains(e.target)) sel.classList.remove('is-open');\n });\n reflectPeerPill();\n }\n function wireHostRewrite(){\n try {\n var h = location.hostname || 'localhost';\n document.querySelectorAll('.ax-topbar__tab[href*=\"__HOST__\"]').forEach(function(a){\n a.href = a.getAttribute('href').replace(/__HOST__/g, h);\n });\n } catch (e) {}\n }\n /**\n * Make the topbar peer selector actually drive page content. When the\n * operator picks a non-primary peer, every /api/admin/* request gets an\n * X-Agentx-Peer header (and every EventSource on that path gets a ?peer=\n * query, since EventSource can't set headers). The dashboard's existing\n * proxy handler picks it up and forwards to the chosen peer.\n *\n * Without this hook, the activity-graph (and other admin pages) silently\n * keep showing only the local node's data even after the operator picks\n * a different peer in the topbar.\n */\n function wirePeerProxy(){\n if (window.__axPeerWired) return; window.__axPeerWired = true;\n // Path-prefix allowlist for the peer-proxy interceptor. Extend here\n // when a new admin-side surface needs to follow the mesh selector.\n // /api/workflows is the workflow dashboard's read/write API — it\n // lives outside /api/admin for legacy reasons, but the page is part\n // of the admin surface and should proxy when a non-primary peer is\n // selected (otherwise the workflows page on Mac shows local-only\n // YAMLs while the runtime lives on peer).\n function shouldProxy(url){\n if (!url) return false;\n // Admin-side surfaces that follow the mesh selector. Each entry\n // is a path-prefix match. Extend when a new admin surface lands\n // outside /api/admin/* but should still proxy when the operator\n // picks a non-primary peer.\n // /api/admin/ — original admin surface (activity, ledger, …)\n // /api/workflows — workflow editor + observability page\n // /api/agents — agent dropdowns inside the workflow editor +\n // anywhere else the page asks \"which agents\n // exist on the selected node?\"\n return url.indexOf('/api/admin/') >= 0\n || url.indexOf('/api/workflows') >= 0\n || url.indexOf('/api/agents') >= 0;\n }\n var origFetch = window.fetch.bind(window);\n window.fetch = function(input, init){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary') {\n var url = typeof input === 'string' ? input : (input && input.url) || '';\n if (shouldProxy(url)) {\n init = init || {};\n init.headers = new Headers(init.headers || {});\n init.headers.set('X-Agentx-Peer', peer);\n }\n }\n } catch (e) {}\n return origFetch(input, init);\n };\n var OrigES = window.EventSource;\n if (OrigES) {\n window.EventSource = function(url, cfg){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary' && typeof url === 'string' && shouldProxy(url)) {\n url += (url.indexOf('?') >= 0 ? '&' : '?') + 'peer=' + encodeURIComponent(peer);\n }\n } catch (e) {}\n return new OrigES(url, cfg);\n };\n window.EventSource.prototype = OrigES.prototype;\n window.EventSource.CONNECTING = OrigES.CONNECTING;\n window.EventSource.OPEN = OrigES.OPEN;\n window.EventSource.CLOSED = OrigES.CLOSED;\n }\n }\n // wirePeerProxy() is synchronous and DOM-independent — it just wraps\n // window.fetch + window.EventSource. Fire it IMMEDIATELY (before\n // DOMContentLoaded) so deferred page bundles that issue their first\n // fetch on bundle-parse (e.g. the workflow editor's React useEffect)\n // see the wrapped fetch and pick up the X-Agentx-Peer header. Without\n // this, the workflow editor's load on Mac:4202 hit local /api/workflows\n // (instead of proxying to the selected peer), got a 404, and silently\n // fell back to blank-graph state — looking like a \"new workflow\".\n wirePeerProxy();\n function wire(){ wireHostRewrite(); wireTheme(); wireMesh(); /* wirePeerProxy already ran */ }\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', wire);\n } else { wire(); }\n})();\n</script>`\n\n/**\n * Render the topbar element. Caller places this at the start of <body>.\n * Emits a matching .ax-subheader row only when `subheader` HTML is provided.\n */\nexport function renderTopbar(opts: TopbarOpts): string {\n const peers = opts.peers && opts.peers.length > 0 ? opts.peers : [{ id: \"local\", name: \"this daemon\", dashboardUrl: \"\", primary: true }]\n const currentId = opts.currentPeerId || peers.find((p) => p.primary)?.id || peers[0].id\n const current = peers.find((p) => p.id === currentId) || peers[0]\n\n const meshMenu = peers.map((p) => {\n const isCurrent = p.id === currentId\n const primaryMark = p.primary ? ' <span style=\"color:var(--ax-muted);font-size:10px\">· primary</span>' : \"\"\n const proxyable = !!p.tokenScope && !p.primary\n // Proxyable peers: use data-peer-id so the click handler switches locally\n // via localStorage + reload. Un-proxyable (no token): legacy navigate.\n const href = proxyable\n ? \"#\"\n : p.dashboardUrl\n ? `${p.dashboardUrl.replace(/\\/+$/, \"\")}/admin`\n : \"#\"\n const dataAttr = proxyable ? ` data-peer-id=\"${esc(p.id)}\" data-peer-action=\"proxy\"` : p.primary ? ` data-peer-id=\"primary\" data-peer-action=\"proxy\"` : \"\"\n const hint = proxyable\n ? '<div class=\"url\" style=\"color:var(--ax-accent)\">manage from this dashboard ✓</div>'\n : p.tokenScope\n ? `<div class=\"url\">${esc(p.tokenScope)}</div>`\n : p.dashboardUrl\n ? '<div class=\"url\" style=\"color:var(--ax-muted)\">(open peer dashboard ↗)</div>'\n : \"\"\n return `<a href=\"${esc(href)}\" class=\"${isCurrent ? \"is-current\" : \"\"}\"${dataAttr}>\n <div class=\"row\"><span class=\"dot\"></span><span>${esc(p.name)}${primaryMark}</span></div>\n ${p.dashboardUrl ? `<div class=\"url\">${esc(p.dashboardUrl)}</div>` : \"\"}\n ${hint}\n </a>`\n }).join(\"\")\n\n const meshSelector = `<div class=\"ax-mesh-sel\" aria-haspopup=\"menu\">\n <span class=\"lbl\">managing</span>\n <span class=\"name\">${esc(current.name)}</span>\n <span class=\"chev\">▾</span>\n <div class=\"ax-mesh-menu\" role=\"menu\">${meshMenu}</div>\n </div>`\n\n // Minimal, mesh-first nav. The primary group answers the four ops\n // questions in order: who's alive and what are they doing (Live),\n // what happened (Ledger), what did it cost (Cost), how is it wired\n // (Settings). A second \"Work\" group appears only when the operator\n // actually configured that surface (boards / workflows) — flags are\n // set once per process via setTopbarFeatures().\n //\n // Deliberately NOT in the nav (all still routable by URL):\n // /admin/ledger /admin/cost what happened, what it cost\n // /workflows /inbox BPM surfaces\n // / (boards) Kanban\n // /admin/health /admin/activity-graph /admin/graph /admin/projects\n // /procedures /admin/wiki /glossary /processes\n //\n // The nav is the set of surfaces an operator opens on purpose, not a site\n // map. Workflows earned its way back: automation that stops running reports\n // nothing anywhere else, so it needs somewhere to be found. Every page still\n // listed above is one link away from Live or Settings, and none of them is\n // where an operator starts their day —\n // \"who is alive and what are they doing\" (Live) and \"how is this wired\"\n // (Settings) are. A tab bar that lists every surface an app HAS is a site\n // map, not navigation; it makes the two answers people actually want\n // harder to find, which is the opposite of what chrome is for.\n //\n // Removing a tab does not remove the page. If view counts later show a\n // surface earning its place, adding a tab back is one line.\n type Tab = { id: TopbarTab; label: string; href: string; external?: boolean }\n const groups: Array<{ name: string; tabs: Tab[] }> = [\n {\n name: \"Mesh\",\n tabs: [\n { id: \"live\", label: \"Live\", href: \"/live\" },\n { id: \"mesh\", label: \"Operations\", href: \"/mesh\" },\n { id: \"monitor\", label: \"Monitor\", href: \"/monitor\" },\n { id: \"activity\", label: \"Activity\", href: \"/activity\" },\n { id: \"workflows\", label: \"Workflows\", href: \"/workflows\" },\n { id: \"admin\", label: \"Settings\", href: \"/admin\" },\n ],\n },\n ]\n\n const tabs = groups.map((g) => {\n const inner = g.tabs.map((t) => {\n const cls = t.id === opts.activeTab ? \"ax-topbar__tab is-active\" : \"ax-topbar__tab\"\n const extAttrs = t.external ? ` target=\"_blank\" rel=\"noopener\"` : \"\"\n const extGlyph = t.external ? ' <span style=\"font-size:9px;opacity:0.6\">↗</span>' : \"\"\n return `<a href=\"${t.href}\" class=\"${cls}\"${extAttrs}>${t.label}${extGlyph}</a>`\n }).join(\"\")\n return `<div class=\"ax-topbar__group\" title=\"${esc(g.name)} tier\"><span class=\"ax-topbar__group-label\">${esc(g.name)}</span>${inner}</div>`\n }).join(\"\")\n\n const header = `<header class=\"ax-topbar\">\n <div class=\"ax-topbar__left\">\n <div class=\"ax-brand\">\n <span class=\"ax-brand__mark\">AX</span>\n <span class=\"ax-brand__name\">AgentX</span>\n <span class=\"ax-brand__subtitle\">${esc(opts.subtitle)}</span>\n </div>\n <nav class=\"ax-topbar__tabs\">${tabs}</nav>\n </div>\n <div class=\"ax-topbar__right\">\n ${opts.rightExtras || \"\"}\n ${meshSelector}\n <div class=\"ax-theme-switch\" role=\"tablist\" aria-label=\"Theme\">\n <button data-theme-opt=\"dark\">Dark</button>\n <button data-theme-opt=\"light\">Light</button>\n </div>\n </div>\n</header>${opts.subheader ? `<div class=\"ax-subheader\">${opts.subheader}</div>` : \"\"}`\n return header\n}\n\nfunction esc(s: string): string {\n return String(s || \"\").replace(/[&<>\"']/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" }[c] as string))\n}\n"],"mappings":"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","names":["topbarFeatures","setTopbarFeatures","features","getTopbarFeatures","TOPBAR_HEAD","TOPBAR_CSS","TOPBAR_SCRIPT","renderTopbar","opts","peers","currentId","p","current","meshMenu","isCurrent","primaryMark","proxyable","href","dataAttr","esc","hint","meshSelector","tabs","g","inner","t","cls","extAttrs","extGlyph","s","c"]}
@@ -0,0 +1,2 @@
1
+ import{a as u,b as R}from"./chunk-L2WSPY6S.js";R();import{existsSync as y,promises as x}from"fs";import m from"path";import I from"js-yaml";import w from"fast-glob";import{z as e}from"zod";var C=e.object({id:e.string(),priority:e.number().default(0),when:e.object({agentIds:e.array(e.string()).optional(),intentTags:e.array(e.string()).optional(),messageRegex:e.array(e.string()).optional(),requireTags:e.array(e.string()).optional()}).default({}),references:e.array(e.string()).default([]),skills:e.array(e.string()).default([]),maxChars:e.number().int().min(100).max(8e3).optional()}),v=e.object({recipes:e.array(C)}),S=["references/recipes",".references/recipes",".agentx/references/recipes",".agentx/recipes"];async function z(o){let t={recipes:[],sourceById:new Map};for(let r of S){let s=m.resolve(o,r);if(!y(s))continue;let n=await w.glob("**/*.{yaml,yml}",{cwd:s,deep:5});for(let d of n){let a=m.resolve(s,d);try{let f=await x.readFile(a,"utf8"),i=I.load(f),c=v.parse(i);for(let p of c.recipes){if(t.sourceById.has(p.id))throw new Error(`recipe id "${p.id}" duplicated in ${a}`);t.recipes.push(p),t.sourceById.set(p.id,a)}}catch(f){u.warn({file:a,error:f?.message},"recipes: failed to load file")}}}return t}function F(o,t,r){let s=[];for(let i of t.recipes)b(i,o)&&s.push(i);s.sort((i,c)=>c.priority-i.priority||i.id.localeCompare(c.id));let n=new Set,d=[],a=new Set,f=[];for(let i of s){for(let c of i.references){let p=T(c,r);p.length===0&&f.push(c);for(let l of p){if(n.has(l))continue;let g=r.byId.get(l);if(!g){f.push(l);continue}i.when.requireTags?.length&&!i.when.requireTags.every(h=>g.tags.includes(h))||(n.add(l),d.push(g))}}for(let c of i.skills)a.add(c)}return{matched:s,cards:d,requiredSkills:[...a],unresolvedIds:[...new Set(f)]}}function b(o,t){let r=o.when;if(r.agentIds?.length&&!r.agentIds.includes(t.agentId))return!1;if(r.intentTags?.length){let s=t.intentTags??[];if(!r.intentTags.some(d=>s.includes(d)))return!1}return!(r.messageRegex?.length&&!r.messageRegex.some(n=>{try{return new RegExp(n,"i").test(t.message)}catch{return t.message.toLowerCase().includes(n.toLowerCase())}}))}function T(o,t){if(!o.endsWith(".*"))return[o];let r=o.slice(0,-2),s=[];for(let n of t.byId.keys())(n===r||n.startsWith(`${r}.`))&&s.push(n);return s}export{z as a,F as b};
2
+ //# sourceMappingURL=chunk-K33GIBSE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/references/recipes.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport yaml from \"js-yaml\"\nimport fg from \"fast-glob\"\nimport { z } from \"zod\"\nimport { logger } from \"@/utils/logger\"\nimport type { ReferenceCard, ReferenceIndex } from \"./types\"\n\n// --- Recipes ---\n//\n// Map (agentId, optional intent tag, optional message keyword) to a bundle of\n// reference card ids and skill names. Pure deterministic lookup — no LLM in\n// the hot path. Multiple recipes can match a single turn; the resolver merges\n// their reference sets and de-duplicates.\n\nconst recipeSchema = z.object({\n id: z.string(),\n /** Higher = picked first when budgets bite. Default 0. */\n priority: z.number().default(0),\n when: z\n .object({\n agentIds: z.array(z.string()).optional(),\n intentTags: z.array(z.string()).optional(),\n /** OR'd, case-insensitive regex sources. */\n messageRegex: z.array(z.string()).optional(),\n /** Tags to require on the card pool. Cards must carry every tag. */\n requireTags: z.array(z.string()).optional(),\n })\n .default({}),\n /** Card ids OR \".*\" glob suffixes — \"initech.contacts.*\" matches all under that prefix. */\n references: z.array(z.string()).default([]),\n /** Skill names that should be considered required for this intent (audit lint\n * flags FAILING when a referenced skill has no SKILL.md). */\n skills: z.array(z.string()).default([]),\n /** Per-recipe char budget. Total is enforced by the layer; this is advisory. */\n maxChars: z.number().int().min(100).max(8000).optional(),\n})\nexport type Recipe = z.infer<typeof recipeSchema>\n\nconst recipeFileSchema = z.object({\n recipes: z.array(recipeSchema),\n})\n\nexport interface RecipeIndex {\n recipes: Recipe[]\n /** Source path per recipe id. */\n sourceById: Map<string, string>\n}\n\nconst RECIPE_DIRS = [\n \"references/recipes\",\n \".references/recipes\",\n \".agentx/references/recipes\",\n \".agentx/recipes\",\n]\n\nexport async function loadRecipes(cwd: string): Promise<RecipeIndex> {\n const index: RecipeIndex = { recipes: [], sourceById: new Map() }\n for (const dir of RECIPE_DIRS) {\n const abs = path.resolve(cwd, dir)\n if (!existsSync(abs)) continue\n const files = await fg.glob(\"**/*.{yaml,yml}\", { cwd: abs, deep: 5 })\n for (const rel of files) {\n const file = path.resolve(abs, rel)\n try {\n const raw = await fs.readFile(file, \"utf8\")\n const parsed = yaml.load(raw)\n const validated = recipeFileSchema.parse(parsed)\n for (const recipe of validated.recipes) {\n if (index.sourceById.has(recipe.id)) {\n throw new Error(`recipe id \"${recipe.id}\" duplicated in ${file}`)\n }\n index.recipes.push(recipe)\n index.sourceById.set(recipe.id, file)\n }\n } catch (e: any) {\n logger.warn({ file, error: e?.message }, \"recipes: failed to load file\")\n }\n }\n }\n return index\n}\n\nexport interface RecipeContext {\n agentId: string\n intentTags?: string[]\n message: string\n}\n\nexport interface ResolvedRecipes {\n matched: Recipe[]\n cards: ReferenceCard[]\n /** Skill names required by the matched recipes. */\n requiredSkills: string[]\n /** Reference ids in the recipes that did NOT resolve — used by audit lint. */\n unresolvedIds: string[]\n}\n\n/**\n * Match recipes deterministically. AND within a `when` clause, OR across\n * recipes. A recipe with no `when` always matches (use sparingly).\n */\nexport function resolveRecipes(\n ctx: RecipeContext,\n recipeIndex: RecipeIndex,\n referenceIndex: ReferenceIndex,\n): ResolvedRecipes {\n const matched: Recipe[] = []\n for (const recipe of recipeIndex.recipes) {\n if (recipeMatches(recipe, ctx)) matched.push(recipe)\n }\n // Highest priority first, then stable by id.\n matched.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id))\n\n const seen = new Set<string>()\n const cards: ReferenceCard[] = []\n const skills = new Set<string>()\n const unresolvedIds: string[] = []\n\n for (const recipe of matched) {\n for (const ref of recipe.references) {\n const ids = expandReferenceIds(ref, referenceIndex)\n if (ids.length === 0) unresolvedIds.push(ref)\n for (const id of ids) {\n if (seen.has(id)) continue\n const card = referenceIndex.byId.get(id)\n if (!card) {\n unresolvedIds.push(id)\n continue\n }\n if (recipe.when.requireTags?.length) {\n const has = recipe.when.requireTags.every(t => card.tags.includes(t))\n if (!has) continue\n }\n seen.add(id)\n cards.push(card)\n }\n }\n for (const s of recipe.skills) skills.add(s)\n }\n\n return {\n matched,\n cards,\n requiredSkills: [...skills],\n unresolvedIds: [...new Set(unresolvedIds)],\n }\n}\n\nfunction recipeMatches(recipe: Recipe, ctx: RecipeContext): boolean {\n const w = recipe.when\n if (w.agentIds?.length && !w.agentIds.includes(ctx.agentId)) return false\n if (w.intentTags?.length) {\n const tags = ctx.intentTags ?? []\n const hit = w.intentTags.some(t => tags.includes(t))\n if (!hit) return false\n }\n if (w.messageRegex?.length) {\n const hit = w.messageRegex.some(src => {\n try {\n return new RegExp(src, \"i\").test(ctx.message)\n } catch {\n return ctx.message.toLowerCase().includes(src.toLowerCase())\n }\n })\n if (!hit) return false\n }\n return true\n}\n\n/**\n * Expand a reference id pattern. Plain ids return [id]. A trailing `.*` matches\n * any id with that prefix in the index.\n */\nfunction expandReferenceIds(pattern: string, idx: ReferenceIndex): string[] {\n if (!pattern.endsWith(\".*\")) return [pattern]\n const prefix = pattern.slice(0, -2)\n const out: string[] = []\n for (const id of idx.byId.keys()) {\n if (id === prefix || id.startsWith(`${prefix}.`)) out.push(id)\n }\n return out\n}\n"],"mappings":"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","names":["init_logger","existsSync","fs","path","yaml","fg","z","recipeSchema","recipeFileSchema","RECIPE_DIRS","loadRecipes","cwd","index","dir","abs","files","rel","file","raw","parsed","validated","recipe","e","logger","resolveRecipes","ctx","recipeIndex","referenceIndex","matched","recipeMatches","a","b","seen","cards","skills","unresolvedIds","ref","ids","expandReferenceIds","id","card","t","s","w","tags","src","pattern","idx","prefix","out"]}
@@ -0,0 +1,2 @@
1
+ import{b as e}from"./chunk-FIOB3MEX.js";import n from"chalk";var c,l=e(()=>{"use strict";c={error(...o){console.log(n.red(...o))},warn(...o){console.log(n.yellow(...o))},info(...o){console.log(n.cyan(...o))},success(...o){console.log(n.green(...o))},break(){console.log("")}}});export{c as a,l as b};
2
+ //# sourceMappingURL=chunk-L2WSPY6S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts"],"sourcesContent":["import chalk from \"chalk\"\n\nexport const logger = {\n error(...args: unknown[]) {\n console.log(chalk.red(...args))\n },\n warn(...args: unknown[]) {\n console.log(chalk.yellow(...args))\n },\n info(...args: unknown[]) {\n console.log(chalk.cyan(...args))\n },\n success(...args: unknown[]) {\n console.log(chalk.green(...args))\n },\n break() {\n console.log(\"\")\n },\n}\n"],"mappings":"wCAAA,OAAOA,MAAW,QAAlB,IAEaC,EAFbC,EAAAC,EAAA,kBAEaF,EAAS,CACpB,SAASG,EAAiB,CACxB,QAAQ,IAAIJ,EAAM,IAAI,GAAGI,CAAI,CAAC,CAChC,EACA,QAAQA,EAAiB,CACvB,QAAQ,IAAIJ,EAAM,OAAO,GAAGI,CAAI,CAAC,CACnC,EACA,QAAQA,EAAiB,CACvB,QAAQ,IAAIJ,EAAM,KAAK,GAAGI,CAAI,CAAC,CACjC,EACA,WAAWA,EAAiB,CAC1B,QAAQ,IAAIJ,EAAM,MAAM,GAAGI,CAAI,CAAC,CAClC,EACA,OAAQ,CACN,QAAQ,IAAI,EAAE,CAChB,CACF","names":["chalk","logger","init_logger","__esmMin","args"]}