@ken-jo/agent-connector 0.4.95 → 0.4.97

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 (378) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +6 -6
  3. package/README.md +605 -599
  4. package/dist/{action-Q7GGO4PA.js → action-IPFDPOZU.js} +16 -16
  5. package/dist/action-IPFDPOZU.js.map +1 -0
  6. package/dist/{amazon-q-ETMAGY3D.js → amazon-q-IT44LEWV.js} +6 -6
  7. package/dist/amazon-q-IT44LEWV.js.map +1 -0
  8. package/dist/{amp-BQZOL5GR.js → amp-CEWOSWQO.js} +4 -4
  9. package/dist/amp-CEWOSWQO.js.map +1 -0
  10. package/dist/{amp-67BZONGF.js → amp-P35FRU2Z.js} +7 -7
  11. package/dist/amp-P35FRU2Z.js.map +1 -0
  12. package/dist/{antigravity-EA4L225L.js → antigravity-LHFEFNMC.js} +5 -5
  13. package/dist/antigravity-LHFEFNMC.js.map +1 -0
  14. package/dist/antigravity-TWIWXTQH.js +18 -0
  15. package/dist/{antigravity-cli-B2HXZFYQ.js → antigravity-cli-MOKFSWOU.js} +5 -5
  16. package/dist/antigravity-cli-MOKFSWOU.js.map +1 -0
  17. package/dist/{antigravity-cli-OM6SC3PE.js → antigravity-cli-MPE27LPI.js} +9 -9
  18. package/dist/antigravity-cli-MPE27LPI.js.map +1 -0
  19. package/dist/audit-3E5JAOP2.js +311 -0
  20. package/dist/audit-3E5JAOP2.js.map +1 -0
  21. package/dist/{chunk-PW42ZXCB.js → chunk-3EP5V2F4.js} +3 -3
  22. package/dist/chunk-3EP5V2F4.js.map +1 -0
  23. package/dist/{chunk-MGTDQLJJ.js → chunk-3FINQ2ZF.js} +4 -4
  24. package/dist/chunk-3FINQ2ZF.js.map +1 -0
  25. package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
  26. package/dist/chunk-4GH2KA7V.js.map +1 -0
  27. package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
  28. package/dist/chunk-7C2IPIIB.js.map +1 -0
  29. package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
  30. package/dist/chunk-7HHW23AC.js.map +1 -0
  31. package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
  32. package/dist/chunk-7PSNXUFK.js.map +1 -0
  33. package/dist/{chunk-5IOTP7LQ.js → chunk-7ZFK2WEO.js} +7 -5
  34. package/dist/chunk-7ZFK2WEO.js.map +1 -0
  35. package/dist/{chunk-XC5SJ35F.js → chunk-C63AAXK7.js} +5 -5
  36. package/dist/chunk-C63AAXK7.js.map +1 -0
  37. package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
  38. package/dist/chunk-C747DYNT.js.map +1 -0
  39. package/dist/{chunk-YPAXJ7EW.js → chunk-FHYR5Q5T.js} +13 -13
  40. package/dist/chunk-FHYR5Q5T.js.map +1 -0
  41. package/dist/{chunk-I5UXHIDT.js → chunk-GCPEPUD7.js} +8 -8
  42. package/dist/chunk-GCPEPUD7.js.map +1 -0
  43. package/dist/{chunk-ISEJWBFF.js → chunk-GZZCHJDP.js} +2 -2
  44. package/dist/chunk-GZZCHJDP.js.map +1 -0
  45. package/dist/{chunk-UMQ6NBQR.js → chunk-ILWHLGIC.js} +2 -2
  46. package/dist/chunk-ILWHLGIC.js.map +1 -0
  47. package/dist/{chunk-IVAWP6HH.js → chunk-IOJDZJ72.js} +2 -2
  48. package/dist/chunk-IOJDZJ72.js.map +1 -0
  49. package/dist/{chunk-NBYKLUF6.js → chunk-KFPSJRHR.js} +2 -2
  50. package/dist/chunk-KFPSJRHR.js.map +1 -0
  51. package/dist/{chunk-O7SDRBVB.js → chunk-KM2YXO6V.js} +1 -1
  52. package/dist/chunk-KM2YXO6V.js.map +1 -0
  53. package/dist/{chunk-7F5KHLEP.js → chunk-L2C5UMKW.js} +4 -4
  54. package/dist/chunk-L2C5UMKW.js.map +1 -0
  55. package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
  56. package/dist/chunk-LFWYYWLV.js.map +1 -0
  57. package/dist/{chunk-APZDTY76.js → chunk-LQ55E7QI.js} +7 -7
  58. package/dist/chunk-LQ55E7QI.js.map +1 -0
  59. package/dist/{chunk-PAPMUAR5.js → chunk-MLU7NDAZ.js} +12 -12
  60. package/dist/chunk-MLU7NDAZ.js.map +1 -0
  61. package/dist/{chunk-H33M77DD.js → chunk-N4ENLZEU.js} +22 -19
  62. package/dist/chunk-N4ENLZEU.js.map +1 -0
  63. package/dist/{chunk-D4ELPBB5.js → chunk-NDMNMAKP.js} +43 -43
  64. package/dist/chunk-NDMNMAKP.js.map +1 -0
  65. package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
  66. package/dist/chunk-PSGTHJQU.js.map +1 -0
  67. package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
  68. package/dist/chunk-Q4ISQHUQ.js.map +1 -0
  69. package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
  70. package/dist/chunk-Q7PR2WGT.js.map +1 -0
  71. package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
  72. package/dist/chunk-QYOUQCC6.js.map +1 -0
  73. package/dist/{chunk-Z2MRSS2I.js → chunk-SLJI5BOM.js} +2 -2
  74. package/dist/chunk-SLJI5BOM.js.map +1 -0
  75. package/dist/{chunk-JKKU2WKG.js → chunk-TGZFDXNH.js} +4 -4
  76. package/dist/chunk-TGZFDXNH.js.map +1 -0
  77. package/dist/{chunk-FJCUKXGX.js → chunk-THI4PI4H.js} +2 -2
  78. package/dist/chunk-THI4PI4H.js.map +1 -0
  79. package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
  80. package/dist/chunk-UZZTWWGG.js.map +1 -0
  81. package/dist/{chunk-3NH2ARIS.js → chunk-WPTNMYYM.js} +2 -2
  82. package/dist/chunk-WPTNMYYM.js.map +1 -0
  83. package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
  84. package/dist/chunk-XN7S44U4.js.map +1 -0
  85. package/dist/{chunk-6WZODR3Z.js → chunk-XVEJAFKW.js} +5 -5
  86. package/dist/chunk-XVEJAFKW.js.map +1 -0
  87. package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
  88. package/dist/chunk-XXLCMSD3.js.map +1 -0
  89. package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
  90. package/dist/chunk-XZWMJLIT.js.map +1 -0
  91. package/dist/{chunk-4NHXLXSD.js → chunk-Y3K6KJMS.js} +2 -2
  92. package/dist/chunk-Y3K6KJMS.js.map +1 -0
  93. package/dist/{chunk-RK6VBLXP.js → chunk-ZSOS2KOR.js} +1 -1
  94. package/dist/chunk-ZSOS2KOR.js.map +1 -0
  95. package/dist/{claude-code-NMRKTAP7.js → claude-code-FS42PXZO.js} +4 -4
  96. package/dist/claude-code-FS42PXZO.js.map +1 -0
  97. package/dist/{claude-code-GRWO6XBZ.js → claude-code-QBWDT5RT.js} +11 -11
  98. package/dist/claude-code-QBWDT5RT.js.map +1 -0
  99. package/dist/cli/sdk.js +9 -8
  100. package/dist/cli/sdk.js.map +1 -1
  101. package/dist/cli.js +1 -1
  102. package/dist/cli.js.map +1 -1
  103. package/dist/{cline-25KW25IN.js → cline-JKEAYWVR.js} +6 -6
  104. package/dist/cline-JKEAYWVR.js.map +1 -0
  105. package/dist/{codebuddy-ZNYQACMF.js → codebuddy-EB6EYPC3.js} +7 -7
  106. package/dist/codebuddy-EB6EYPC3.js.map +1 -0
  107. package/dist/{codebuff-VR6JUDIY.js → codebuff-5RSXFU6G.js} +6 -6
  108. package/dist/codebuff-5RSXFU6G.js.map +1 -0
  109. package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
  110. package/dist/codebuff-6KWBW6CT.js.map +1 -0
  111. package/dist/{codex-CPNKTXG3.js → codex-ART46CDD.js} +9 -9
  112. package/dist/codex-ART46CDD.js.map +1 -0
  113. package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
  114. package/dist/codex-DKBAAUCO.js.map +1 -0
  115. package/dist/{continue-WU2IB5KS.js → continue-JHZCMSPH.js} +8 -8
  116. package/dist/continue-JHZCMSPH.js.map +1 -0
  117. package/dist/{copilot-cli-MZTAWMPW.js → copilot-cli-CLBJACQ5.js} +4 -4
  118. package/dist/copilot-cli-CLBJACQ5.js.map +1 -0
  119. package/dist/{copilot-cli-PAP377OM.js → copilot-cli-QVAU5CFM.js} +7 -7
  120. package/dist/copilot-cli-QVAU5CFM.js.map +1 -0
  121. package/dist/{crush-3AVQA4YU.js → crush-7TORBYUL.js} +7 -7
  122. package/dist/crush-7TORBYUL.js.map +1 -0
  123. package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
  124. package/dist/crush-A5C5USRL.js.map +1 -0
  125. package/dist/{cursor-DIW43TRZ.js → cursor-D5SWDVSW.js} +3 -3
  126. package/dist/cursor-D5SWDVSW.js.map +1 -0
  127. package/dist/cursor-W7I4QUFO.js +22 -0
  128. package/dist/{detect-6V4LC3MA.js → detect-JHSA3G3U.js} +4 -4
  129. package/dist/detect-JHSA3G3U.js.map +1 -0
  130. package/dist/{devin-QMEM3OFA.js → devin-SSR7S4WX.js} +7 -7
  131. package/dist/devin-SSR7S4WX.js.map +1 -0
  132. package/dist/{doctor-2YDUVSAU.js → doctor-CQJ7TZPA.js} +26 -26
  133. package/dist/doctor-CQJ7TZPA.js.map +1 -0
  134. package/dist/{droid-MOKV53J4.js → droid-45PJ7JGF.js} +7 -7
  135. package/dist/droid-45PJ7JGF.js.map +1 -0
  136. package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
  137. package/dist/droid-PFQ2PZSF.js.map +1 -0
  138. package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-24PFDK7D.js} +8 -8
  139. package/dist/gemini-cli-24PFDK7D.js.map +1 -0
  140. package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
  141. package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
  142. package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
  143. package/dist/goose-K2HKUTBK.js.map +1 -0
  144. package/dist/{goose-4EI5MYTI.js → goose-U5D3LCPC.js} +9 -9
  145. package/dist/goose-U5D3LCPC.js.map +1 -0
  146. package/dist/{grok-cli-QYQPDSVB.js → grok-cli-L2MHAPBY.js} +7 -7
  147. package/dist/grok-cli-L2MHAPBY.js.map +1 -0
  148. package/dist/{hermes-F75OSK3Y.js → hermes-2S5JUTTJ.js} +7 -7
  149. package/dist/hermes-2S5JUTTJ.js.map +1 -0
  150. package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
  151. package/dist/hermes-MF3NX4ZY.js.map +1 -0
  152. package/dist/{hook-OEVRDMXD.js → hook-VHICXHXL.js} +16 -16
  153. package/dist/hook-VHICXHXL.js.map +1 -0
  154. package/dist/index.js +5 -5
  155. package/dist/{install-5NMCUDG5.js → install-WLENR4KF.js} +192 -28
  156. package/dist/install-WLENR4KF.js.map +1 -0
  157. package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-Z4FC3RM3.js} +6 -6
  158. package/dist/jetbrains-copilot-Z4FC3RM3.js.map +1 -0
  159. package/dist/{junie-DIFN2P22.js → junie-ULGJJIHB.js} +6 -6
  160. package/dist/junie-ULGJJIHB.js.map +1 -0
  161. package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
  162. package/dist/kilo-SMHPOHJ5.js.map +1 -0
  163. package/dist/{kilo-EMZJBEBB.js → kilo-WWIJOVWD.js} +8 -8
  164. package/dist/kilo-WWIJOVWD.js.map +1 -0
  165. package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-QKE2EQLS.js} +8 -8
  166. package/dist/kilo-cli-QKE2EQLS.js.map +1 -0
  167. package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
  168. package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
  169. package/dist/{kimi-IQJOIY6B.js → kimi-JMUFTVSF.js} +7 -7
  170. package/dist/kimi-JMUFTVSF.js.map +1 -0
  171. package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
  172. package/dist/kimi-MSIDEMCI.js.map +1 -0
  173. package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
  174. package/dist/kiro-NYRRBIKW.js.map +1 -0
  175. package/dist/{kiro-BYX4HEAJ.js → kiro-UWUCRKR3.js} +7 -7
  176. package/dist/kiro-UWUCRKR3.js.map +1 -0
  177. package/dist/{leaderboard-XEKS7B3H.js → leaderboard-NJWKGMXP.js} +7 -7
  178. package/dist/leaderboard-NJWKGMXP.js.map +1 -0
  179. package/dist/{mimo-code-7R53QDHB.js → mimo-code-BPNYYIVM.js} +7 -7
  180. package/dist/mimo-code-BPNYYIVM.js.map +1 -0
  181. package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LT3DAJ3L.js} +6 -6
  182. package/dist/mistral-vibe-LT3DAJ3L.js.map +1 -0
  183. package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
  184. package/dist/mux-7PIJ7HUQ.js.map +1 -0
  185. package/dist/{mux-NYCNGL43.js → mux-AHNUAY5Z.js} +6 -6
  186. package/dist/mux-AHNUAY5Z.js.map +1 -0
  187. package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-RE5RZASZ.js} +7 -7
  188. package/dist/nemoclaw-RE5RZASZ.js.map +1 -0
  189. package/dist/{omp-562NK5BJ.js → omp-D2EXEPH7.js} +7 -7
  190. package/dist/omp-D2EXEPH7.js.map +1 -0
  191. package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-SSGBEUKX.js} +6 -6
  192. package/dist/open-interpreter-SSGBEUKX.js.map +1 -0
  193. package/dist/openclaw-IJ33K45M.js +17 -0
  194. package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
  195. package/dist/openclaw-KAFTNLR2.js.map +1 -0
  196. package/dist/{opencode-YS4WX2E2.js → opencode-YKUKZY3J.js} +7 -7
  197. package/dist/opencode-YKUKZY3J.js.map +1 -0
  198. package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
  199. package/dist/opencode-Z65JYILL.js.map +1 -0
  200. package/dist/{openhands-QMYIKNOS.js → openhands-SEZMCUC3.js} +7 -7
  201. package/dist/openhands-SEZMCUC3.js.map +1 -0
  202. package/dist/{package-G25GVTH2.js → package-JECVQEFT.js} +15 -15
  203. package/dist/package-JECVQEFT.js.map +1 -0
  204. package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
  205. package/dist/pi-DLKCPOJE.js.map +1 -0
  206. package/dist/{pi-644FIN3A.js → pi-YPYV2ULH.js} +6 -6
  207. package/dist/pi-YPYV2ULH.js.map +1 -0
  208. package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
  209. package/dist/qwen-code-26CSFDX6.js.map +1 -0
  210. package/dist/{qwen-code-STDMX7EM.js → qwen-code-ZHCLCYMC.js} +9 -9
  211. package/dist/qwen-code-ZHCLCYMC.js.map +1 -0
  212. package/dist/{roo-code-DLPIHHQ6.js → roo-code-7F4HYB34.js} +6 -6
  213. package/dist/roo-code-7F4HYB34.js.map +1 -0
  214. package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
  215. package/dist/roo-code-HSAE2CJU.js.map +1 -0
  216. package/dist/runtime/index.js +14 -14
  217. package/dist/sdk/index.js +6 -6
  218. package/dist/sdk/index.js.map +1 -1
  219. package/dist/sdk/test.js +11 -11
  220. package/dist/{serve-XTMIUHKK.js → serve-LYSBH43G.js} +16 -16
  221. package/dist/serve-LYSBH43G.js.map +1 -0
  222. package/dist/{status-VJAGO4Z7.js → status-PHKKGEW5.js} +11 -11
  223. package/dist/status-PHKKGEW5.js.map +1 -0
  224. package/dist/{statusline-EY2E2UGF.js → statusline-ZDD4OZ4F.js} +15 -15
  225. package/dist/statusline-ZDD4OZ4F.js.map +1 -0
  226. package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
  227. package/dist/synthetic-LUT6V6SD.js.map +1 -0
  228. package/dist/{telemetry-OLZQBADT.js → telemetry-HDGMI2BK.js} +10 -10
  229. package/dist/telemetry-HDGMI2BK.js.map +1 -0
  230. package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
  231. package/dist/trae-SIQI3C66.js.map +1 -0
  232. package/dist/{trae-UOYD6JZZ.js → trae-Y67HSDOW.js} +6 -6
  233. package/dist/trae-Y67HSDOW.js.map +1 -0
  234. package/dist/{uninstall-AZEUPAND.js → uninstall-RWO2GM2I.js} +21 -21
  235. package/dist/uninstall-RWO2GM2I.js.map +1 -0
  236. package/dist/{upgrade-CAUUHSSC.js → upgrade-YJCXNUEE.js} +21 -21
  237. package/dist/upgrade-YJCXNUEE.js.map +1 -0
  238. package/dist/{usage-3KHZ4RYZ.js → usage-IWO4RQN6.js} +4 -4
  239. package/dist/usage-IWO4RQN6.js.map +1 -0
  240. package/dist/{usage-event-TZWOZPRJ.js → usage-event-AFFKCLHE.js} +15 -15
  241. package/dist/usage-event-AFFKCLHE.js.map +1 -0
  242. package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-5CI5B3X6.js} +7 -7
  243. package/dist/vscode-copilot-5CI5B3X6.js.map +1 -0
  244. package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
  245. package/dist/warp-5YBSP7FP.js.map +1 -0
  246. package/dist/{warp-QYJGKURM.js → warp-INAKVGBV.js} +6 -6
  247. package/dist/warp-INAKVGBV.js.map +1 -0
  248. package/dist/{windsurf-75LLZ37C.js → windsurf-4HM57QAY.js} +6 -6
  249. package/dist/windsurf-4HM57QAY.js.map +1 -0
  250. package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
  251. package/dist/zed-XXCNYOIN.js.map +1 -0
  252. package/dist/{zed-HKAZXEJW.js → zed-YN5X37PQ.js} +7 -7
  253. package/dist/zed-YN5X37PQ.js.map +1 -0
  254. package/package.json +92 -88
  255. package/dist/action-Q7GGO4PA.js.map +0 -1
  256. package/dist/amazon-q-ETMAGY3D.js.map +0 -1
  257. package/dist/amp-67BZONGF.js.map +0 -1
  258. package/dist/amp-BQZOL5GR.js.map +0 -1
  259. package/dist/antigravity-EA4L225L.js.map +0 -1
  260. package/dist/antigravity-VXLDLTO7.js +0 -18
  261. package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
  262. package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
  263. package/dist/chunk-2Y2MZRLP.js.map +0 -1
  264. package/dist/chunk-3NH2ARIS.js.map +0 -1
  265. package/dist/chunk-4NHXLXSD.js.map +0 -1
  266. package/dist/chunk-5IOTP7LQ.js.map +0 -1
  267. package/dist/chunk-6TF7MHG5.js.map +0 -1
  268. package/dist/chunk-6WZODR3Z.js.map +0 -1
  269. package/dist/chunk-7F5KHLEP.js.map +0 -1
  270. package/dist/chunk-APZDTY76.js.map +0 -1
  271. package/dist/chunk-BEFVJ2OE.js.map +0 -1
  272. package/dist/chunk-D4ELPBB5.js.map +0 -1
  273. package/dist/chunk-FBJQGRET.js.map +0 -1
  274. package/dist/chunk-FJCUKXGX.js.map +0 -1
  275. package/dist/chunk-GZGLCCEE.js.map +0 -1
  276. package/dist/chunk-H33M77DD.js.map +0 -1
  277. package/dist/chunk-H53QZQJI.js.map +0 -1
  278. package/dist/chunk-I5UXHIDT.js.map +0 -1
  279. package/dist/chunk-ISEJWBFF.js.map +0 -1
  280. package/dist/chunk-IVAWP6HH.js.map +0 -1
  281. package/dist/chunk-JKKU2WKG.js.map +0 -1
  282. package/dist/chunk-KHO5WNTP.js.map +0 -1
  283. package/dist/chunk-MGTDQLJJ.js.map +0 -1
  284. package/dist/chunk-MQPNU2CY.js.map +0 -1
  285. package/dist/chunk-NBYKLUF6.js.map +0 -1
  286. package/dist/chunk-NLE2J5P4.js.map +0 -1
  287. package/dist/chunk-O7SDRBVB.js.map +0 -1
  288. package/dist/chunk-OP4KBXUO.js.map +0 -1
  289. package/dist/chunk-OPXAP3AC.js.map +0 -1
  290. package/dist/chunk-PAPMUAR5.js.map +0 -1
  291. package/dist/chunk-PW42ZXCB.js.map +0 -1
  292. package/dist/chunk-RK6VBLXP.js.map +0 -1
  293. package/dist/chunk-UM5S7DKK.js.map +0 -1
  294. package/dist/chunk-UMQ6NBQR.js.map +0 -1
  295. package/dist/chunk-XC5SJ35F.js.map +0 -1
  296. package/dist/chunk-XD7MY6HB.js.map +0 -1
  297. package/dist/chunk-YPAXJ7EW.js.map +0 -1
  298. package/dist/chunk-YT43MUSJ.js.map +0 -1
  299. package/dist/chunk-Z2MRSS2I.js.map +0 -1
  300. package/dist/claude-code-GRWO6XBZ.js.map +0 -1
  301. package/dist/claude-code-NMRKTAP7.js.map +0 -1
  302. package/dist/cline-25KW25IN.js.map +0 -1
  303. package/dist/codebuddy-ZNYQACMF.js.map +0 -1
  304. package/dist/codebuff-ETYT5UBL.js.map +0 -1
  305. package/dist/codebuff-VR6JUDIY.js.map +0 -1
  306. package/dist/codex-CPNKTXG3.js.map +0 -1
  307. package/dist/codex-YAORPCOD.js.map +0 -1
  308. package/dist/continue-WU2IB5KS.js.map +0 -1
  309. package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
  310. package/dist/copilot-cli-PAP377OM.js.map +0 -1
  311. package/dist/crush-3AVQA4YU.js.map +0 -1
  312. package/dist/crush-QBXLNN7T.js.map +0 -1
  313. package/dist/cursor-DIW43TRZ.js.map +0 -1
  314. package/dist/cursor-GSLUET3V.js +0 -22
  315. package/dist/detect-6V4LC3MA.js.map +0 -1
  316. package/dist/devin-QMEM3OFA.js.map +0 -1
  317. package/dist/doctor-2YDUVSAU.js.map +0 -1
  318. package/dist/droid-MOKV53J4.js.map +0 -1
  319. package/dist/droid-U3ZBLQFO.js.map +0 -1
  320. package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
  321. package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
  322. package/dist/goose-4EI5MYTI.js.map +0 -1
  323. package/dist/goose-64V46Y2W.js.map +0 -1
  324. package/dist/grok-cli-QYQPDSVB.js.map +0 -1
  325. package/dist/hermes-F3EGTK6N.js.map +0 -1
  326. package/dist/hermes-F75OSK3Y.js.map +0 -1
  327. package/dist/hook-OEVRDMXD.js.map +0 -1
  328. package/dist/install-5NMCUDG5.js.map +0 -1
  329. package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
  330. package/dist/junie-DIFN2P22.js.map +0 -1
  331. package/dist/kilo-EMZJBEBB.js.map +0 -1
  332. package/dist/kilo-SW7BH6EG.js.map +0 -1
  333. package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
  334. package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
  335. package/dist/kimi-5FHEXGZD.js.map +0 -1
  336. package/dist/kimi-IQJOIY6B.js.map +0 -1
  337. package/dist/kiro-BYX4HEAJ.js.map +0 -1
  338. package/dist/kiro-WGOI5CGT.js.map +0 -1
  339. package/dist/leaderboard-XEKS7B3H.js.map +0 -1
  340. package/dist/mimo-code-7R53QDHB.js.map +0 -1
  341. package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
  342. package/dist/mux-NYCNGL43.js.map +0 -1
  343. package/dist/mux-XM6AP35L.js.map +0 -1
  344. package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
  345. package/dist/omp-562NK5BJ.js.map +0 -1
  346. package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
  347. package/dist/openclaw-5EQFGOWL.js.map +0 -1
  348. package/dist/openclaw-7EYBQS4R.js +0 -17
  349. package/dist/opencode-QGP4ALQY.js.map +0 -1
  350. package/dist/opencode-YS4WX2E2.js.map +0 -1
  351. package/dist/openhands-QMYIKNOS.js.map +0 -1
  352. package/dist/package-G25GVTH2.js.map +0 -1
  353. package/dist/pi-644FIN3A.js.map +0 -1
  354. package/dist/pi-GGDQREVG.js.map +0 -1
  355. package/dist/qwen-code-GQILBJ5B.js.map +0 -1
  356. package/dist/qwen-code-STDMX7EM.js.map +0 -1
  357. package/dist/roo-code-DLPIHHQ6.js.map +0 -1
  358. package/dist/roo-code-SUWI4BBN.js.map +0 -1
  359. package/dist/serve-XTMIUHKK.js.map +0 -1
  360. package/dist/status-VJAGO4Z7.js.map +0 -1
  361. package/dist/statusline-EY2E2UGF.js.map +0 -1
  362. package/dist/synthetic-6FRA7P5Q.js.map +0 -1
  363. package/dist/telemetry-OLZQBADT.js.map +0 -1
  364. package/dist/trae-CPPYASR2.js.map +0 -1
  365. package/dist/trae-UOYD6JZZ.js.map +0 -1
  366. package/dist/uninstall-AZEUPAND.js.map +0 -1
  367. package/dist/upgrade-CAUUHSSC.js.map +0 -1
  368. package/dist/usage-3KHZ4RYZ.js.map +0 -1
  369. package/dist/usage-event-TZWOZPRJ.js.map +0 -1
  370. package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
  371. package/dist/warp-NRBBJQR2.js.map +0 -1
  372. package/dist/warp-QYJGKURM.js.map +0 -1
  373. package/dist/windsurf-75LLZ37C.js.map +0 -1
  374. package/dist/zed-HKAZXEJW.js.map +0 -1
  375. package/dist/zed-XJFOOKCO.js.map +0 -1
  376. /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-TWIWXTQH.js.map} +0 -0
  377. /package/dist/{cursor-GSLUET3V.js.map → cursor-W7I4QUFO.js.map} +0 -0
  378. /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-IJ33K45M.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  backupsDir,
5
5
  connectorDir,
6
6
  ensureDir
7
- } from "./chunk-GZGLCCEE.js";
7
+ } from "./chunk-QYOUQCC6.js";
8
8
 
9
9
  // src/core/managed-block.ts
10
10
  import { createHash } from "crypto";
@@ -493,4 +493,4 @@ export {
493
493
  hasMemoryLedger,
494
494
  recordMemoryTarget
495
495
  };
496
- //# sourceMappingURL=chunk-UMQ6NBQR.js.map
496
+ //# sourceMappingURL=chunk-ILWHLGIC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/managed-block.ts"],"sourcesContent":["/**\r\n * core/managed-block — the marker-fenced MANAGED BLOCK engine behind the\r\n * `memory` content surface.\r\n *\r\n * Memory/rules files (AGENTS.md, CLAUDE.md, GEMINI.md, …) are SHARED,\r\n * user-authored files — unlike commands/skills/subagents, agent-connector does\r\n * not own them. Every write therefore goes through this one module, which\r\n * implements idempotent, hash-stamped, uninstall-reversible blocks:\r\n *\r\n * <!-- agent-connector:begin <connectorId>/<name> hash=<sha256-12> -->\r\n * <!-- Managed by agent-connector for \"<connectorId>\". Do not edit … -->\r\n * …guidance…\r\n * <!-- agent-connector:end <connectorId>/<name> -->\r\n *\r\n * Design rules (each grounded in surveyed prior art — ansible blockinfile,\r\n * conda init, OMC mergeClaudeMd, Ruler, doctoc):\r\n * • UNIQUE marker per block: the blockId (`<connectorId>/<entryName>`) lives\r\n * ON the marker line, so multiple connectors coexist in one file and each\r\n * update path only ever touches its own pair (ansible's documented\r\n * multi-block rule). The shared `agent-connector` namespace token lets\r\n * doctor/uninstall enumerate every block with one scan.\r\n * • `hash=` is the first 12 hex chars of sha256 over the NORMALIZED inner\r\n * region (CRLF→LF + trim — stable under prettier/EOL converters): O(1)\r\n * idempotence (unchanged → skip, no mtime/git churn) and tamper detection\r\n * (actual inner hash ≠ recorded ⇒ the user edited inside the block ⇒ warn,\r\n * never clobber without an explicit force).\r\n * • IN-PLACE replacement (conda semantics): zero bytes outside the marker\r\n * pair ever change — no move-to-top, no user blank-line reflow.\r\n * • Line-anchored matching that explicitly tolerates `\\r` line ends and a\r\n * FENCE-AWARE scanner (markers quoted inside ``` / ~~~ code fences never\r\n * match) — fixes the CRLF duplicate-block and quoted-marker classes.\r\n * • First-begin / FIRST-end-after pairing (never last-end): a missing end\r\n * marker must not swallow a sibling connector's block.\r\n * • HTML-comment markers are CORRECT for CLAUDE.md: Claude Code strips HTML\r\n * comments from CLAUDE.md before context injection (verified against docs\r\n * and the 2.1.172 binary), so the markers and the do-not-edit notice are\r\n * INVISIBLE to the model there while remaining fully parseable by us for\r\n * sync/doctor/uninstall. On AGENTS.md hosts (which inline the whole file\r\n * into the prompt) the short notice doubles as an in-prompt \"do not edit\"\r\n * instruction to the host's own agent. A `commentStyle: \"hash\"` variant\r\n * exists for future non-markdown targets (`#`-prefixed marker lines).\r\n *\r\n * The module also owns the per-connector MEMORY OWNERSHIP LEDGER\r\n * (`connectorDir(id)/memory-state.json`): the markers in the file remain the\r\n * source of truth for uninstall (a teammate's machine with no ledger still\r\n * uninstalls via the prefix scan); the ledger only adds the created-vs-modified\r\n * distinction (file deletion rights) and doctor diagnostics.\r\n */\r\n\r\nimport { createHash } from \"node:crypto\";\r\nimport {\r\n chmodSync,\r\n copyFileSync,\r\n existsSync,\r\n readFileSync,\r\n renameSync,\r\n rmSync,\r\n statSync,\r\n writeFileSync,\r\n} from \"node:fs\";\r\nimport { basename, dirname, join } from \"node:path\";\r\n\r\nimport { assertNoSymlinkInPath, backupsDir, connectorDir, ensureDir } from \"./paths.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Grammar constants (shared with defineConnector's content validation)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Literal begin token. Connector memory content MUST NOT contain it. */\r\nexport const MANAGED_BLOCK_BEGIN_TOKEN = \"agent-connector:begin\";\r\n/** Literal end token. Connector memory content MUST NOT contain it. */\r\nexport const MANAGED_BLOCK_END_TOKEN = \"agent-connector:end\";\r\n\r\n/** Hard cap on one memory entry's content (ConnectorConfigError above this). */\r\nexport const MEMORY_CONTENT_HARD_CAP_BYTES = 16 * 1024;\r\n/**\r\n * Soft per-entry budget — exceeded → install-time `warn` ChangeRecord (memory\r\n * files are injected into EVERY prompt of EVERY session on the host; codex\r\n * additionally caps combined project docs at 32 KiB).\r\n */\r\nexport const MEMORY_CONTENT_SOFT_BUDGET_BYTES = 4 * 1024;\r\n\r\n/** Marker comment style: HTML comments (default, all v1 markdown targets) or `#` lines. */\r\nexport type BlockCommentStyle = \"html\" | \"hash\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Hashing / rendering\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** CRLF/CR → LF + trim: the normalization both hashing sides use. */\r\nfunction normalizeInner(s: string): string {\r\n return s.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").trim();\r\n}\r\n\r\n/** First 12 hex chars of sha256 over the NORMALIZED inner region. */\r\nexport function hashBlockContent(inner: string): string {\r\n return createHash(\"sha256\").update(normalizeInner(inner), \"utf8\").digest(\"hex\").slice(0, 12);\r\n}\r\n\r\n/** The conda-style in-block warning, adapted for the LLM era (see header). */\r\nfunction defaultNotice(connectorId: string): string {\r\n return (\r\n `Managed by agent-connector for \"${connectorId}\". Do not edit between these markers: ` +\r\n `run your connector package's upgrade/sync command to rewrite this block; ` +\r\n `run its uninstall command to remove it ` +\r\n `(framework fallback: \\`agent-connector uninstall ${connectorId}\\`).`\r\n );\r\n}\r\n\r\nfunction beginLine(blockId: string, hash: string, style: BlockCommentStyle): string {\r\n return style === \"hash\"\r\n ? `# ${MANAGED_BLOCK_BEGIN_TOKEN} ${blockId} hash=${hash}`\r\n : `<!-- ${MANAGED_BLOCK_BEGIN_TOKEN} ${blockId} hash=${hash} -->`;\r\n}\r\n\r\n/** END line is a fixed exact string (no hash) so a content change never strands it. */\r\nfunction endLine(blockId: string, style: BlockCommentStyle): string {\r\n return style === \"hash\"\r\n ? `# ${MANAGED_BLOCK_END_TOKEN} ${blockId}`\r\n : `<!-- ${MANAGED_BLOCK_END_TOKEN} ${blockId} -->`;\r\n}\r\n\r\nfunction noticeLine(notice: string, style: BlockCommentStyle): string {\r\n return style === \"hash\" ? `# ${notice}` : `<!-- ${notice} -->`;\r\n}\r\n\r\nexport interface RenderBlockOptions {\r\n /** `<connectorId>/<entryName>` (or the reserved `_shared/<name>` bridge prefix). */\r\n blockId: string;\r\n /** Connector id named in the default do-not-edit notice line. */\r\n connectorId: string;\r\n /** The guidance payload (plain markdown; normalized + trimmed on emit). */\r\n content: string;\r\n /** Marker comment style; default \"html\". */\r\n commentStyle?: BlockCommentStyle;\r\n /** Override the default notice line TEXT (without comment delimiters). */\r\n notice?: string;\r\n}\r\n\r\n/**\r\n * Render the block as LOGICAL lines (no EOL chars). The recorded `hash=`\r\n * covers the full inner region (notice line + content) so install and scan\r\n * compare like for like.\r\n */\r\nexport function renderBlockLines(opts: RenderBlockOptions): string[] {\r\n const style = opts.commentStyle ?? \"html\";\r\n const inner = [\r\n noticeLine(opts.notice ?? defaultNotice(opts.connectorId), style),\r\n ...normalizeInner(opts.content).split(\"\\n\"),\r\n ];\r\n const hash = hashBlockContent(inner.join(\"\\n\"));\r\n return [beginLine(opts.blockId, hash, style), ...inner, endLine(opts.blockId, style)];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Scanning (fence-aware, line-anchored, CRLF-tolerant)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n// Both comment styles are RECOGNIZED in one pass (a file may legitimately mix\r\n// styles only across rewrites; emission always uses the requested style).\r\nconst BEGIN_RE =\r\n /^(?:<!--\\s+agent-connector:begin\\s+(\\S+)\\s+hash=([0-9a-f]{12})\\s+-->|#\\s+agent-connector:begin\\s+(\\S+)\\s+hash=([0-9a-f]{12}))\\s*$/;\r\nconst END_RE = /^(?:<!--\\s+agent-connector:end\\s+(\\S+)\\s+-->|#\\s+agent-connector:end\\s+(\\S+))\\s*$/;\r\n\r\ninterface MarkerEvent {\r\n kind: \"begin\" | \"end\";\r\n blockId: string;\r\n /** Recorded hash (begin markers only). */\r\n hash?: string;\r\n /** Line index into the logical-lines array. */\r\n index: number;\r\n}\r\n\r\n/** Strip ONE trailing `\\r` — `raw.split(\"\\n\")` leaves it on CRLF files. */\r\nfunction stripCr(line: string): string {\r\n return line.endsWith(\"\\r\") ? line.slice(0, -1) : line;\r\n}\r\n\r\n/**\r\n * Scan logical lines for marker events, tracking ``` / ~~~ fence state so a\r\n * marker QUOTED inside a fenced code block never matches (the OMC class of\r\n * false positives, eliminated rather than just reduced).\r\n */\r\nfunction scanMarkers(logical: string[]): MarkerEvent[] {\r\n const events: MarkerEvent[] = [];\r\n let fence: { char: string; len: number } | null = null;\r\n for (let i = 0; i < logical.length; i++) {\r\n const line = logical[i]!;\r\n if (fence) {\r\n const close = line.match(/^ {0,3}(`{3,}|~{3,})\\s*$/);\r\n if (close && close[1]![0] === fence.char && close[1]!.length >= fence.len) fence = null;\r\n continue;\r\n }\r\n const open = line.match(/^ {0,3}(`{3,}|~{3,})/);\r\n if (open) {\r\n fence = { char: open[1]![0]!, len: open[1]!.length };\r\n continue;\r\n }\r\n const b = line.match(BEGIN_RE);\r\n if (b) {\r\n events.push({ kind: \"begin\", blockId: (b[1] ?? b[3])!, hash: (b[2] ?? b[4])!, index: i });\r\n continue;\r\n }\r\n const e = line.match(END_RE);\r\n if (e) events.push({ kind: \"end\", blockId: (e[1] ?? e[2])!, index: i });\r\n }\r\n return events;\r\n}\r\n\r\n/**\r\n * Logical lines of `raw` that sit OUTSIDE ``` / ~~~ code fences — for host\r\n * probes that must ignore quoted examples (e.g. the claude-code `@AGENTS.md`\r\n * import detection). Fence lines themselves are excluded.\r\n */\r\nexport function linesOutsideFences(raw: string): string[] {\r\n const body = raw.startsWith(\"\\uFEFF\") ? raw.slice(1) : raw;\r\n const logical = body.split(\"\\n\").map(stripCr);\r\n const out: string[] = [];\r\n let fence: { char: string; len: number } | null = null;\r\n for (const line of logical) {\r\n if (fence) {\r\n const close = line.match(/^ {0,3}(`{3,}|~{3,})\\s*$/);\r\n if (close && close[1]![0] === fence.char && close[1]!.length >= fence.len) fence = null;\r\n continue;\r\n }\r\n const open = line.match(/^ {0,3}(`{3,}|~{3,})/);\r\n if (open) {\r\n fence = { char: open[1]![0]!, len: open[1]!.length };\r\n continue;\r\n }\r\n out.push(line);\r\n }\r\n return out;\r\n}\r\n\r\ninterface BlockPair {\r\n blockId: string;\r\n begin: MarkerEvent;\r\n end: MarkerEvent;\r\n}\r\n\r\n/**\r\n * Pair markers per blockId: first begin, then the FIRST end at-or-after it.\r\n * A nested begin for the SAME id (inside an open pair) is treated as inner\r\n * content; unpaired markers are returned as strays. Per-id state means a\r\n * missing end for connector A can never swallow connector B's block.\r\n */\r\nfunction pairMarkers(events: MarkerEvent[]): { pairs: BlockPair[]; strays: MarkerEvent[] } {\r\n const open = new Map<string, MarkerEvent>();\r\n const pairs: BlockPair[] = [];\r\n const strays: MarkerEvent[] = [];\r\n for (const ev of events) {\r\n if (ev.kind === \"begin\") {\r\n if (!open.has(ev.blockId)) open.set(ev.blockId, ev);\r\n // else: a begin inside an open pair — inner content / abuse; the hash\r\n // check will flag the pair as drifted rather than guessing boundaries.\r\n } else {\r\n const b = open.get(ev.blockId);\r\n if (b) {\r\n pairs.push({ blockId: ev.blockId, begin: b, end: ev });\r\n open.delete(ev.blockId);\r\n } else {\r\n strays.push(ev);\r\n }\r\n }\r\n }\r\n for (const b of open.values()) strays.push(b);\r\n return { pairs, strays };\r\n}\r\n\r\n/** One discovered block (for doctor / refcount / uninstall enumeration). */\r\nexport interface ManagedBlockInfo {\r\n blockId: string;\r\n /** The hash recorded on the begin marker line. */\r\n recordedHash: string;\r\n /** Hash of the actual normalized inner region as found on disk. */\r\n actualHash: string;\r\n /** True when recordedHash ≠ actualHash — the user edited inside the block. */\r\n drifted: boolean;\r\n beginLine: number;\r\n endLine: number;\r\n}\r\n\r\n/** Enumerate every complete agent-connector block in `raw` (namespace scan). */\r\nexport function listManagedBlocks(raw: string): ManagedBlockInfo[] {\r\n const body = raw.startsWith(\"\\uFEFF\") ? raw.slice(1) : raw;\r\n const logical = body.split(\"\\n\").map(stripCr);\r\n const { pairs } = pairMarkers(scanMarkers(logical));\r\n return pairs.map((p) => {\r\n const inner = logical.slice(p.begin.index + 1, p.end.index).join(\"\\n\");\r\n const actualHash = hashBlockContent(inner);\r\n const recordedHash = p.begin.hash ?? \"\";\r\n return {\r\n blockId: p.blockId,\r\n recordedHash,\r\n actualHash,\r\n drifted: recordedHash !== actualHash,\r\n beginLine: p.begin.index,\r\n endLine: p.end.index,\r\n };\r\n });\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Pure text upsert / removal\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface UpsertBlockOptions extends RenderBlockOptions {\r\n /** Overwrite a USER-EDITED (drifted) block. Default false → warn + leave intact. */\r\n force?: boolean;\r\n}\r\n\r\nexport interface BlockTextResult {\r\n /** Resulting full text (=== input when nothing changed). */\r\n text: string;\r\n action: \"create\" | \"update\" | \"skip\" | \"warn\";\r\n detail: string;\r\n changed: boolean;\r\n /**\r\n * True when the rewrite was DESTRUCTIVE-ADJACENT (force-overwrote a drifted\r\n * block, collapsed duplicate pairs, or stripped stray markers) — the file\r\n * wrapper writes a timestamped backup before persisting such a change.\r\n */\r\n recovered: boolean;\r\n /** Hash of the (new) rendered inner region — recorded in the ledger. */\r\n hash: string;\r\n}\r\n\r\n/** Detect the file's dominant EOL; new/replaced lines are emitted with it. */\r\nfunction detectEol(body: string): \"\\n\" | \"\\r\\n\" {\r\n const crlf = (body.match(/\\r\\n/g) ?? []).length;\r\n const total = (body.match(/\\n/g) ?? []).length;\r\n return crlf > 0 && crlf >= total - crlf ? \"\\r\\n\" : \"\\n\";\r\n}\r\n\r\n/** Add `\\r` to logical lines when the file is CRLF (joined with \"\\n\" later). */\r\nfunction withEol(lines: string[], eol: \"\\n\" | \"\\r\\n\"): string[] {\r\n return eol === \"\\r\\n\" ? lines.map((l) => `${l}\\r`) : lines;\r\n}\r\n\r\n/**\r\n * Strip trailing newline(s) + whitespace-only line tails WITHOUT touching\r\n * trailing spaces on the last content line (bytes outside our block).\r\n */\r\nfunction trimTrailingBlank(body: string): string {\r\n let out = body;\r\n for (;;) {\r\n const m = out.match(/\\r?\\n[ \\t]*$/);\r\n if (!m) return out;\r\n out = out.slice(0, out.length - m[0].length);\r\n }\r\n}\r\n\r\n/** Append the rendered block at EOF with exactly ONE blank separator line. */\r\nfunction appendBlock(body: string, blockLines: string[], eol: \"\\n\" | \"\\r\\n\"): string {\r\n const blockText = blockLines.join(eol) + eol;\r\n if (body.trim() === \"\") return blockText;\r\n return trimTrailingBlank(body) + eol + eol + blockText;\r\n}\r\n\r\n/**\r\n * Idempotent upsert of one managed block into `raw` (pure; no I/O).\r\n * See the module header for the full algorithm contract.\r\n */\r\nexport function upsertBlockInText(raw: string, opts: UpsertBlockOptions): BlockTextResult {\r\n const bom = raw.startsWith(\"\\uFEFF\");\r\n const body = bom ? raw.slice(1) : raw;\r\n const eol = detectEol(body);\r\n const lines = body.split(\"\\n\"); // elements keep a trailing \"\\r\" on CRLF files\r\n const logical = lines.map(stripCr);\r\n\r\n const events = scanMarkers(logical).filter((e) => e.blockId === opts.blockId);\r\n const { pairs, strays } = pairMarkers(events);\r\n\r\n const newLines = renderBlockLines(opts);\r\n const newHash = hashBlockContent(newLines.slice(1, -1).join(\"\\n\"));\r\n const finish = (text: string, r: Omit<BlockTextResult, \"text\" | \"hash\">): BlockTextResult => ({\r\n text: bom ? `\\uFEFF${text}` : text,\r\n hash: newHash,\r\n ...r,\r\n });\r\n\r\n if (pairs.length > 0) {\r\n const { begin, end } = pairs[0]!;\r\n const recorded = begin.hash ?? \"\";\r\n const actualHash = hashBlockContent(logical.slice(begin.index + 1, end.index).join(\"\\n\"));\r\n const drifted = actualHash !== recorded;\r\n\r\n if (drifted && !opts.force) {\r\n return finish(body, {\r\n action: \"warn\",\r\n detail:\r\n `block ${opts.blockId} was edited inside the markers ` +\r\n `(recorded hash ${recorded}, found ${actualHash}); left untouched — ` +\r\n `re-run with --force to overwrite the in-block edits`,\r\n changed: false,\r\n recovered: false,\r\n });\r\n }\r\n\r\n const duplicates = pairs.slice(1);\r\n const needsWrite =\r\n drifted || recorded !== newHash || duplicates.length > 0 || strays.length > 0;\r\n if (!needsWrite) {\r\n return finish(body, {\r\n action: \"skip\",\r\n detail: `block ${opts.blockId} already up to date (hash ${newHash})`,\r\n changed: false,\r\n recovered: false,\r\n });\r\n }\r\n\r\n // Build the deletion set: the canonical pair region (replaced in place),\r\n // duplicate pairs (+ ONE adjacent blank line each), and stray marker LINES.\r\n const del = new Set<number>();\r\n for (let i = begin.index; i <= end.index; i++) del.add(i);\r\n for (const dup of duplicates) {\r\n for (let i = dup.begin.index; i <= dup.end.index; i++) del.add(i);\r\n reclaimBlankLine(logical, dup.begin.index, dup.end.index, del, lines.length);\r\n }\r\n for (const s of strays) del.add(s.index);\r\n\r\n const out: string[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if (i === begin.index) out.push(...withEol(newLines, eol));\r\n if (!del.has(i)) out.push(lines[i]!);\r\n }\r\n const notes: string[] = [];\r\n if (drifted) notes.push(\"in-block edits overwritten (--force; backup taken)\");\r\n if (duplicates.length > 0) notes.push(`${duplicates.length} duplicate pair(s) collapsed`);\r\n if (strays.length > 0) notes.push(`${strays.length} stray marker line(s) removed`);\r\n return finish(out.join(\"\\n\"), {\r\n action: \"update\",\r\n detail:\r\n `block ${opts.blockId} updated (hash ${recorded || \"?\"} → ${newHash})` +\r\n (notes.length > 0 ? `; ${notes.join(\"; \")}` : \"\"),\r\n changed: true,\r\n recovered: drifted || duplicates.length > 0 || strays.length > 0,\r\n });\r\n }\r\n\r\n if (strays.length > 0) {\r\n // Corruption recovery: strip ONLY the stray marker line(s) (never the text\r\n // between guessed boundaries), then proceed as not-found.\r\n const del = new Set(strays.map((s) => s.index));\r\n const cleaned = lines.filter((_, i) => !del.has(i)).join(\"\\n\");\r\n return finish(appendBlock(cleaned, newLines, eol), {\r\n action: \"create\",\r\n detail:\r\n `block ${opts.blockId} appended after recovery: ${strays.length} stray ` +\r\n `marker line(s) removed (backup taken)`,\r\n changed: true,\r\n recovered: true,\r\n });\r\n }\r\n\r\n // Not found → append at EOF (one blank separator; whole file when empty).\r\n return finish(appendBlock(body, newLines, eol), {\r\n action: \"create\",\r\n detail: `block ${opts.blockId} appended (hash ${newHash})`,\r\n changed: true,\r\n recovered: false,\r\n });\r\n}\r\n\r\n/**\r\n * Mark ONE blank line adjacent to a removed begin..end region for deletion\r\n * (prefer the line before; never the post-final-newline \"\" sentinel).\r\n */\r\nfunction reclaimBlankLine(\r\n logical: string[],\r\n beginIdx: number,\r\n endIdx: number,\r\n del: Set<number>,\r\n lineCount: number,\r\n): void {\r\n const before = beginIdx - 1;\r\n if (before >= 0 && !del.has(before) && logical[before]!.trim() === \"\") {\r\n del.add(before);\r\n return;\r\n }\r\n const after = endIdx + 1;\r\n // The final \"\" element of split(\"\\n\") on a newline-terminated file is the\r\n // trailing-newline sentinel, not a blank line — leave it alone.\r\n const isSentinel = after === lineCount - 1 && logical[after] === \"\";\r\n if (after < lineCount && !isSentinel && !del.has(after) && logical[after]!.trim() === \"\") {\r\n del.add(after);\r\n }\r\n}\r\n\r\nexport interface RemoveBlocksOptions {\r\n /** Remove the block with EXACTLY this id… */\r\n blockId?: string;\r\n /** …or every block whose id starts with this prefix (e.g. `<connectorId>/`). */\r\n blockIdPrefix?: string;\r\n}\r\n\r\nexport interface RemoveRecord {\r\n blockId: string;\r\n action: \"remove\" | \"warn\";\r\n detail: string;\r\n}\r\n\r\nexport interface RemoveBlocksResult {\r\n text: string;\r\n changed: boolean;\r\n records: RemoveRecord[];\r\n /** True when the remaining text is whitespace-only (file-deletion candidate). */\r\n fileNowEmpty: boolean;\r\n /** True when a drifted block was removed or stray markers were stripped. */\r\n recovered: boolean;\r\n}\r\n\r\n/**\r\n * Remove every matching managed block (begin..end inclusive + at most ONE\r\n * adjacent blank separator line — no blank-line accumulation across cycles).\r\n * Drifted blocks are removed WITH a warn (uninstall intent is explicit; the\r\n * file wrapper backs the file up first). Stray matching markers are stripped\r\n * line-only with a warn. Pure; no I/O.\r\n */\r\nexport function removeBlocksFromText(raw: string, match: RemoveBlocksOptions): RemoveBlocksResult {\r\n const bom = raw.startsWith(\"\\uFEFF\");\r\n const body = bom ? raw.slice(1) : raw;\r\n const lines = body.split(\"\\n\");\r\n const logical = lines.map(stripCr);\r\n\r\n const matches = (id: string): boolean =>\r\n (match.blockId !== undefined && id === match.blockId) ||\r\n (match.blockIdPrefix !== undefined && id.startsWith(match.blockIdPrefix));\r\n\r\n const { pairs, strays } = pairMarkers(scanMarkers(logical));\r\n const targetPairs = pairs.filter((p) => matches(p.blockId));\r\n const targetStrays = strays.filter((s) => matches(s.blockId));\r\n\r\n if (targetPairs.length === 0 && targetStrays.length === 0) {\r\n return { text: raw, changed: false, records: [], fileNowEmpty: false, recovered: false };\r\n }\r\n\r\n const del = new Set<number>();\r\n const records: RemoveRecord[] = [];\r\n let recovered = false;\r\n\r\n for (const p of targetPairs) {\r\n const recorded = p.begin.hash ?? \"\";\r\n const actualHash = hashBlockContent(\r\n logical.slice(p.begin.index + 1, p.end.index).join(\"\\n\"),\r\n );\r\n for (let i = p.begin.index; i <= p.end.index; i++) del.add(i);\r\n reclaimBlankLine(logical, p.begin.index, p.end.index, del, lines.length);\r\n if (actualHash !== recorded) {\r\n recovered = true;\r\n records.push({\r\n blockId: p.blockId,\r\n action: \"warn\",\r\n detail:\r\n `block ${p.blockId} had in-block edits (recorded hash ${recorded}, found ` +\r\n `${actualHash}); removed anyway (backup taken)`,\r\n });\r\n }\r\n records.push({ blockId: p.blockId, action: \"remove\", detail: `block ${p.blockId} removed` });\r\n }\r\n for (const s of targetStrays) {\r\n del.add(s.index);\r\n recovered = true;\r\n records.push({\r\n blockId: s.blockId,\r\n action: \"warn\",\r\n detail: `stray ${s.kind} marker for ${s.blockId} removed (marker line only)`,\r\n });\r\n }\r\n\r\n const text = lines.filter((_, i) => !del.has(i)).join(\"\\n\");\r\n return {\r\n text: bom ? `\\uFEFF${text}` : text,\r\n changed: true,\r\n records,\r\n fileNowEmpty: text.trim() === \"\",\r\n recovered,\r\n };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// File-level wrappers (atomic write, backups, dry-run)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface BlockFileChange {\r\n action: \"create\" | \"update\" | \"skip\" | \"warn\" | \"remove\";\r\n path: string;\r\n detail: string;\r\n /** True when the FILE did not exist and this upsert created it (ledger fact). */\r\n createdFile: boolean;\r\n /** Set when a pre-mutation backup was written (destructive/recovery paths only). */\r\n backupPath?: string;\r\n /** Hash of the rendered inner region (ledger / doctor). */\r\n hash: string;\r\n}\r\n\r\n/** Timestamped copy into backupsDir(); best-effort (null on failure). */\r\nfunction backupFile(path: string): string | null {\r\n try {\r\n ensureDir(backupsDir());\r\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\r\n const dest = join(backupsDir(), `memory-${stamp}-${basename(path)}`);\r\n copyFileSync(path, dest);\r\n return dest;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Atomic write: temp file in the SAME directory + rename, preserving the\r\n * destination's mode. Shrinks (does not eliminate) the read-modify-write race\r\n * with hosts that append to memory files mid-session (Claude `#` quick-memory).\r\n */\r\nfunction atomicWriteFile(path: string, text: string): void {\r\n const dir = dirname(path);\r\n ensureDir(dir);\r\n const tmp = join(dir, `.${basename(path)}.ac-tmp-${process.pid}-${Date.now()}`);\r\n try {\r\n writeFileSync(tmp, text, \"utf8\");\r\n try {\r\n chmodSync(tmp, statSync(path).mode);\r\n } catch {\r\n /* destination absent — default mode is fine */\r\n }\r\n renameSync(tmp, path);\r\n } catch (err) {\r\n rmSync(tmp, { force: true });\r\n throw err;\r\n }\r\n}\r\n\r\n/**\r\n * Upsert one managed block into the file at `path` (created when absent).\r\n * Re-reads immediately before computing+writing; honors dryRun (the action is\r\n * computed and reported, nothing is written).\r\n */\r\nexport function upsertManagedBlockFile(\r\n path: string,\r\n opts: UpsertBlockOptions & { dryRun: boolean },\r\n): BlockFileChange {\r\n let raw: string | null = null;\r\n if (existsSync(path)) {\r\n try {\r\n raw = readFileSync(path, \"utf8\");\r\n } catch {\r\n return {\r\n action: \"warn\",\r\n path,\r\n detail: `cannot read ${path}; memory block ${opts.blockId} not written`,\r\n createdFile: false,\r\n hash: \"\",\r\n };\r\n }\r\n }\r\n\r\n if (raw === null) {\r\n const lines = renderBlockLines(opts);\r\n const hash = hashBlockContent(lines.slice(1, -1).join(\"\\n\"));\r\n if (!opts.dryRun) atomicWriteFile(path, `${lines.join(\"\\n\")}\\n`);\r\n return {\r\n action: \"create\",\r\n path,\r\n detail: `created ${basename(path)} with block ${opts.blockId} (hash ${hash})`,\r\n createdFile: true,\r\n hash,\r\n };\r\n }\r\n\r\n const res = upsertBlockInText(raw, opts);\r\n const change: BlockFileChange = {\r\n action: res.action,\r\n path,\r\n detail: res.detail,\r\n createdFile: false,\r\n hash: res.hash,\r\n };\r\n if (res.changed && !opts.dryRun) {\r\n if (res.recovered) {\r\n const backup = backupFile(path);\r\n if (backup) change.backupPath = backup;\r\n }\r\n atomicWriteFile(path, res.text);\r\n }\r\n return change;\r\n}\r\n\r\n/**\r\n * Remove every matching managed block from the file at `path`. Missing file or\r\n * no matching markers → a single idempotent \"skip\". When the remaining content\r\n * is whitespace-only AND `deleteFileIfCreated` is true (the ledger recorded\r\n * that agent-connector created this file), the file itself is deleted (Ruler\r\n * revert semantics: delete only what we created).\r\n */\r\nexport function removeManagedBlocksFile(\r\n path: string,\r\n match: RemoveBlocksOptions,\r\n opts: { dryRun: boolean; deleteFileIfCreated?: boolean },\r\n): BlockFileChange[] {\r\n if (!existsSync(path)) {\r\n return [\r\n {\r\n action: \"skip\",\r\n path,\r\n detail: `${basename(path)} absent; nothing to remove`,\r\n createdFile: false,\r\n hash: \"\",\r\n },\r\n ];\r\n }\r\n let raw: string;\r\n try {\r\n raw = readFileSync(path, \"utf8\");\r\n } catch {\r\n return [\r\n {\r\n action: \"warn\",\r\n path,\r\n detail: `cannot read ${path}; managed blocks left in place`,\r\n createdFile: false,\r\n hash: \"\",\r\n },\r\n ];\r\n }\r\n\r\n const res = removeBlocksFromText(raw, match);\r\n if (!res.changed) {\r\n return [\r\n {\r\n action: \"skip\",\r\n path,\r\n detail: `no agent-connector blocks matching ${match.blockId ?? match.blockIdPrefix ?? \"?\"} in ${basename(path)}`,\r\n createdFile: false,\r\n hash: \"\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: BlockFileChange[] = [];\r\n let backupPath: string | undefined;\r\n if (!opts.dryRun) {\r\n if (res.recovered) backupPath = backupFile(path) ?? undefined;\r\n if (res.fileNowEmpty && opts.deleteFileIfCreated) {\r\n rmSync(path, { force: true });\r\n } else {\r\n atomicWriteFile(path, res.text);\r\n }\r\n }\r\n for (const r of res.records) {\r\n changes.push({\r\n action: r.action,\r\n path,\r\n detail: r.detail,\r\n createdFile: false,\r\n hash: \"\",\r\n ...(backupPath && r.action === \"warn\" ? { backupPath } : {}),\r\n });\r\n }\r\n if (res.fileNowEmpty && opts.deleteFileIfCreated) {\r\n changes.push({\r\n action: \"remove\",\r\n path,\r\n detail: `${basename(path)} deleted (agent-connector created it; only whitespace remained)`,\r\n createdFile: false,\r\n hash: \"\",\r\n });\r\n }\r\n return changes;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Memory ownership ledger — connectorDir(id)/memory-state.json\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface MemoryLedgerTarget {\r\n platform: string;\r\n scope: string;\r\n path: string;\r\n blockId: string;\r\n /** True when agent-connector CREATED the file (grants deletion rights). */\r\n createdFile: boolean;\r\n /** Inner-region hash we last wrote (doctor drift detection). */\r\n hash: string;\r\n}\r\n\r\nexport interface MemoryLedger {\r\n version: 1;\r\n targets: MemoryLedgerTarget[];\r\n}\r\n\r\nexport function memoryLedgerPath(connectorId: string): string {\r\n return join(connectorDir(connectorId), \"memory-state.json\");\r\n}\r\n\r\nexport function loadMemoryLedger(connectorId: string): MemoryLedger {\r\n const p = memoryLedgerPath(connectorId);\r\n if (!existsSync(p)) return { version: 1, targets: [] };\r\n try {\r\n const parsed = JSON.parse(readFileSync(p, \"utf8\")) as MemoryLedger;\r\n if (parsed && Array.isArray(parsed.targets)) return { version: 1, targets: parsed.targets };\r\n } catch {\r\n /* corrupt → fresh ledger; the markers remain the source of truth */\r\n }\r\n return { version: 1, targets: [] };\r\n}\r\n\r\n/** Persist the ledger; an EMPTY ledger deletes the file (no orphan state). */\r\nexport function saveMemoryLedger(connectorId: string, ledger: MemoryLedger): void {\r\n const p = memoryLedgerPath(connectorId);\r\n assertNoSymlinkInPath(p);\r\n if (ledger.targets.length === 0) {\r\n rmSync(p, { force: true });\r\n return;\r\n }\r\n ensureDir(dirname(p));\r\n writeFileSync(p, `${JSON.stringify(ledger, null, 2)}\\n`, \"utf8\");\r\n}\r\n\r\n/** True when the connector has any recorded memory targets (uninstall guard). */\r\nexport function hasMemoryLedger(connectorId: string): boolean {\r\n return loadMemoryLedger(connectorId).targets.length > 0;\r\n}\r\n\r\n/**\r\n * Upsert one ledger row (keyed by platform+scope+path+blockId). The\r\n * `createdFile` fact is STICKY: once we created the file, deletion rights\r\n * survive later syncs that found it present.\r\n */\r\nexport function recordMemoryTarget(ledger: MemoryLedger, t: MemoryLedgerTarget): void {\r\n const i = ledger.targets.findIndex(\r\n (x) =>\r\n x.platform === t.platform &&\r\n x.scope === t.scope &&\r\n x.path === t.path &&\r\n x.blockId === t.blockId,\r\n );\r\n if (i >= 0) {\r\n ledger.targets[i] = { ...t, createdFile: ledger.targets[i]!.createdFile || t.createdFile };\r\n } else {\r\n ledger.targets.push(t);\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ensureDir,
4
4
  telemetryPath
5
- } from "./chunk-GZGLCCEE.js";
5
+ } from "./chunk-QYOUQCC6.js";
6
6
 
7
7
  // src/telemetry/store.ts
8
8
  import { appendFileSync, existsSync, readFileSync } from "fs";
@@ -193,4 +193,4 @@ export {
193
193
  getTokenizer,
194
194
  inferModelFamily
195
195
  };
196
- //# sourceMappingURL=chunk-IVAWP6HH.js.map
196
+ //# sourceMappingURL=chunk-IOJDZJ72.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/telemetry/store.ts","../src/telemetry/types.ts","../src/telemetry/tokenizer.ts"],"sourcesContent":["/**\r\n * telemetry/store — the default NDJSON {@link TelemetryStore} implementation.\r\n *\r\n * Local-first, dependency-free persistence. One JSON object per line; appends\r\n * are atomic enough for concurrent multi-host writers (a single `appendFileSync`\r\n * of a `<line>\\n` chunk is written in one `write(2)` for small payloads, so\r\n * interleaving across processes does not corrupt individual records). A\r\n * SQLite/WAL backend is a drop-in upgrade behind the same interface.\r\n *\r\n * Telemetry stores AGGREGATE COUNTS ONLY — raw tool arguments/results are never\r\n * persisted (enforced by the {@link ToolEventRecord} shape, which carries no\r\n * content fields).\r\n *\r\n * Global kill switch: AGENT_CONNECTOR_TELEMETRY=0 makes `append` a no-op so a\r\n * disabled telemetry layer can never touch disk or break a host's tool call.\r\n */\r\n\r\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\r\nimport { dirname } from \"node:path\";\r\n\r\nimport { ensureDir, telemetryPath } from \"../core/paths.js\";\r\nimport { worstConfidence } from \"./types.js\";\r\nimport type {\r\n QueryFilter,\r\n RollupRow,\r\n TelemetryStore,\r\n ToolEventRecord,\r\n} from \"./types.js\";\r\n\r\n/** Options for {@link openStore}. */\r\nexport interface OpenStoreOptions {\r\n /** Override the NDJSON file path. Defaults to telemetryPath(\"ndjson\"). */\r\n path?: string;\r\n}\r\n\r\n// Confidence ranking + worst-of comparison live in ./types (the single source\r\n// of truth) so a new ConfidenceSource value orders correctly everywhere.\r\n\r\n/** Is `process.env.AGENT_CONNECTOR_TELEMETRY` an explicit off switch? */\r\nfunction telemetryDisabled(): boolean {\r\n return process.env.AGENT_CONNECTOR_TELEMETRY === \"0\";\r\n}\r\n\r\n/**\r\n * A unique-enough record id. Combines the wall clock with a caller-supplied\r\n * sequence so two records written in the same millisecond still differ.\r\n */\r\nexport function newRecordId(seq: number): string {\r\n return `${Date.now()}-${seq}`;\r\n}\r\n\r\nclass NdjsonStore implements TelemetryStore {\r\n private readonly path: string;\r\n\r\n constructor(path: string) {\r\n this.path = path;\r\n }\r\n\r\n append(record: ToolEventRecord): void {\r\n if (telemetryDisabled()) return;\r\n ensureDir(dirname(this.path));\r\n appendFileSync(this.path, `${JSON.stringify(record)}\\n`, \"utf8\");\r\n }\r\n\r\n query(filter: QueryFilter): ToolEventRecord[] {\r\n if (!existsSync(this.path)) return [];\r\n\r\n let text: string;\r\n try {\r\n text = readFileSync(this.path, \"utf8\");\r\n } catch {\r\n return [];\r\n }\r\n\r\n const out: ToolEventRecord[] = [];\r\n for (const line of text.split(\"\\n\")) {\r\n const trimmed = line.trim();\r\n if (trimmed === \"\") continue;\r\n\r\n let record: ToolEventRecord;\r\n try {\r\n record = JSON.parse(trimmed) as ToolEventRecord;\r\n } catch {\r\n continue; // skip malformed lines\r\n }\r\n\r\n if (matches(record, filter)) out.push(record);\r\n }\r\n return out;\r\n }\r\n\r\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[] {\r\n const groups = new Map<string, RollupRow>();\r\n\r\n for (const record of this.query(filter)) {\r\n const key = groupKey(record, by);\r\n const existing = groups.get(key);\r\n if (existing === undefined) {\r\n groups.set(key, {\r\n key,\r\n calls: 1,\r\n inputTokens: record.inputTokens,\r\n outputTokens: record.outputTokens,\r\n totalTokens: record.inputTokens + record.outputTokens,\r\n confidence: record.confidenceSource,\r\n lastTs: record.ts,\r\n });\r\n } else {\r\n existing.calls += 1;\r\n existing.inputTokens += record.inputTokens;\r\n existing.outputTokens += record.outputTokens;\r\n existing.totalTokens += record.inputTokens + record.outputTokens;\r\n existing.confidence = worstConfidence(\r\n existing.confidence,\r\n record.confidenceSource,\r\n );\r\n if (record.ts > existing.lastTs) existing.lastTs = record.ts;\r\n }\r\n }\r\n\r\n return [...groups.values()];\r\n }\r\n\r\n close(): void {\r\n /* no-op for the NDJSON backend */\r\n }\r\n}\r\n\r\n/** Does a record satisfy every set field of the filter? */\r\nfunction matches(record: ToolEventRecord, filter: QueryFilter): boolean {\r\n if (\r\n filter.connectorId !== undefined &&\r\n record.connectorId !== filter.connectorId\r\n ) {\r\n return false;\r\n }\r\n if (\r\n filter.projectKey !== undefined &&\r\n record.projectKey !== filter.projectKey\r\n ) {\r\n return false;\r\n }\r\n if (filter.sessionId !== undefined && record.sessionId !== filter.sessionId) {\r\n return false;\r\n }\r\n if (filter.toolName !== undefined && record.toolName !== filter.toolName) {\r\n return false;\r\n }\r\n if (filter.sinceMs !== undefined && record.ts < filter.sinceMs) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n/** Resolve the grouping key value for a record under the requested dimension. */\r\nfunction groupKey(\r\n record: ToolEventRecord,\r\n by: \"tool\" | \"session\" | \"project\",\r\n): string {\r\n switch (by) {\r\n case \"tool\":\r\n return record.toolName;\r\n case \"session\":\r\n return record.sessionId;\r\n case \"project\":\r\n return record.projectDir;\r\n }\r\n}\r\n\r\n/**\r\n * Open the default local telemetry store. NDJSON backend; no native deps. The\r\n * returned store is safe to use even when telemetry is disabled — `append`\r\n * becomes a no-op and reads of a missing file return empty results.\r\n */\r\nexport function openStore(opts?: OpenStoreOptions): TelemetryStore {\r\n const path = opts?.path ?? telemetryPath(\"ndjson\");\r\n return new NdjsonStore(path);\r\n}\r\n","/**\r\n * telemetry/types — runtime telemetry contracts.\r\n *\r\n * The framework measures the MCP server's OWN bytes (tool args in, tool result\r\n * out, tool-definition schemas) and tokenizes them locally — the only signal\r\n * identical across every host (no host reports per-tool usage to a server).\r\n * Records are aggregate counts only; raw arguments/results are NEVER stored.\r\n */\r\n\r\nimport type { PlatformId } from \"../core/types.js\";\r\n\r\n/** Provenance of a token count — surfaced so \"estimate\" is never read as \"exact\". */\r\nexport type ConfidenceSource =\r\n | \"tokenizer-exact\" // real BPE for a matching model family\r\n | \"tokenizer-calibrated\" // approx adjusted by a sampled Anthropic count_tokens factor\r\n | \"tokenizer-approx\" // real BPE used as a documented approximation (e.g. Anthropic)\r\n | \"heuristic\" // chars/4 fallback\r\n | \"host-native\"; // a host actually reported usage (e.g. Gemini AfterModel)\r\n\r\n/**\r\n * Confidence ranking, least-trustworthy (0) → most-trustworthy. The single\r\n * source of truth for ordering {@link ConfidenceSource} values; every\r\n * worst-of comparison across the telemetry subsystem (store rollup, both\r\n * leaderboards, report, measure) reads through {@link rankOf} /\r\n * {@link worstConfidence} so a new value orders correctly everywhere.\r\n *\r\n * heuristic < tokenizer-approx < tokenizer-calibrated < tokenizer-exact < host-native\r\n *\r\n * `tokenizer-calibrated` sits between the raw approximation and an exact\r\n * family-matched BPE count: it is an approximation nudged toward truth by a\r\n * sampled real count_tokens factor, so it is more trustworthy than bare approx\r\n * but still not the exact encoding for the target family.\r\n */\r\nexport const CONFIDENCE_RANK: Record<ConfidenceSource, number> = {\r\n heuristic: 0,\r\n \"tokenizer-approx\": 1,\r\n \"tokenizer-calibrated\": 2,\r\n \"tokenizer-exact\": 3,\r\n \"host-native\": 4,\r\n};\r\n\r\n/** Trust rank of a single {@link ConfidenceSource} (higher = more trustworthy). */\r\nexport function rankOf(c: ConfidenceSource): number {\r\n return CONFIDENCE_RANK[c];\r\n}\r\n\r\n/** Return whichever source is the worse (least-confident) of the two. */\r\nexport function worstConfidence(\r\n a: ConfidenceSource,\r\n b: ConfidenceSource,\r\n): ConfidenceSource {\r\n return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;\r\n}\r\n\r\nexport type ModelFamily = \"openai\" | \"anthropic\" | \"generic\";\r\n\r\nexport interface TokenCount {\r\n tokens: number;\r\n source: ConfidenceSource;\r\n}\r\n\r\n/** Pluggable tokenizer. Default impl uses gpt-tokenizer; fallback is chars/4. */\r\nexport interface Tokenizer {\r\n /** Count tokens for a string under the given model family. */\r\n count(text: string, family: ModelFamily): TokenCount;\r\n /** Count tokens for an arbitrary JSON value (serialized canonically first). */\r\n countValue(value: unknown, family: ModelFamily): TokenCount;\r\n}\r\n\r\n/**\r\n * What a record measures. DISTINCT origins that must never be summed:\r\n * • `call` — one per-MCP `tools/call` round-trip (serve-proxy bytes).\r\n * • `tool_defs` — the one-time `tools/list` schema overhead (serve-proxy).\r\n * • `model_turn` — a WHOLE-CONVERSATION host-native turn reported by the host\r\n * (e.g. Gemini/Antigravity AfterModel `usageMetadata`). This is NOT per-MCP:\r\n * it covers the entire model turn, so the MCP leaderboard EXCLUDES it (just\r\n * as it treats `tool_defs` specially) and it is surfaced as its own labeled\r\n * section rather than added to the per-MCP `call` totals or to the\r\n * usage-reader host-scan numbers.\r\n * • `hook` — one RUNTIME hook dispatch through the home-bin `hook`\r\n * entrypoint (src/runtime/hook-entrypoint). The framework tokenizes the\r\n * inbound normalized event payload the handler reads (input) and what the\r\n * handler returns that becomes context/decision (output). The per-item\r\n * `toolName` is the hook EVENT name (e.g. \"SessionStart\"). This is the\r\n * developer-axis \"hook\" surface — measured live, like `call`.\r\n */\r\nexport type EventScope = \"call\" | \"tool_defs\" | \"model_turn\" | \"hook\";\r\n\r\n/**\r\n * Which of the FIVE developer-axis surfaces a record (or footprint) belongs to.\r\n * `server`/`hook` are RUNTIME-measured surfaces that produce {@link ToolEventRecord}\r\n * store rows; `command`/`skill`/`subagent` are STATIC footprints computed from the\r\n * registered connector (the host loads them as context — we never intercept them,\r\n * so they are NEVER written as usage rows). OPTIONAL on the record so rows written\r\n * before this field existed (every legacy serve-proxy row) read as `server`.\r\n */\r\nexport type SurfaceKind = \"server\" | \"hook\" | \"command\" | \"skill\" | \"subagent\";\r\n\r\n/**\r\n * The install scope a wrapped server was deployed under, narrowed to the two\r\n * dimensions that matter for slicing telemetry: a `user`-global install vs a\r\n * `project`-local one. The framework's broader {@link InstallScope}\r\n * (`system|user|project|profile|managed`) is mapped down to this at wrap time —\r\n * everything that is not project-local reads as `user`. Optional on the record\r\n * so rows written before this field existed are treated as \"unknown\".\r\n */\r\nexport type TelemetryInstallScope = \"user\" | \"project\";\r\n\r\n/**\r\n * How the real MCP server underneath the proxy was launched. `npx`/`bunx`/`uvx`\r\n * are ephemeral package runners; `node`/`bun`/`deno` are interpreters running a\r\n * local script; `binary` is a resolved executable on PATH; `http` marks a remote\r\n * server reached over the network (no local launch); `unknown` is the honest\r\n * fallback. Optional on the record so older rows read as \"unknown\".\r\n */\r\nexport type LaunchMethod =\r\n | \"npx\"\r\n | \"bunx\"\r\n | \"uvx\"\r\n | \"node\"\r\n | \"binary\"\r\n | \"http\"\r\n | \"unknown\";\r\n\r\n/**\r\n * One telemetry row. Aggregate counts only — no content. `projectKey` is the\r\n * hashed stable project identity (git remote || normalized abs path); `projectDir`\r\n * is kept human-readable for reports but is the same partition.\r\n */\r\nexport interface ToolEventRecord {\r\n /** Monotonic-ish unique id (e.g. `${ts}-${seq}`). */\r\n id: string;\r\n /** Epoch milliseconds. */\r\n ts: number;\r\n connectorId: string;\r\n toolName: string;\r\n scope: EventScope;\r\n hostPlatform: PlatformId;\r\n sessionId: string;\r\n projectKey: string;\r\n projectDir: string;\r\n inputTokens: number;\r\n outputTokens: number;\r\n confidenceSource: ConfidenceSource;\r\n isError: boolean;\r\n /**\r\n * The (narrowed) install scope the wrapped server was deployed under — a\r\n * slicing dimension for global(user) vs project usage. OPTIONAL: rows written\r\n * before this field existed lack it and must be read as \"unknown\".\r\n */\r\n installScope?: TelemetryInstallScope;\r\n /**\r\n * How the real server was launched (npx/bunx/uvx/node/binary/http) — the\r\n * \"launch-method\" slicing dimension. OPTIONAL: older rows lack it → \"unknown\".\r\n */\r\n launchMethod?: LaunchMethod;\r\n /**\r\n * Which developer-axis surface produced this row. OPTIONAL and\r\n * backward-compatible: rows written before this field existed (every legacy\r\n * serve-proxy `call`/`tool_defs` row) lack it and MUST be read as `server`.\r\n * Runtime rows stamp it explicitly (`server` for the proxy, `hook` for the\r\n * hook runtime); the static command/skill/subagent surfaces never produce\r\n * store rows (they are reported as footprints — see surface-footprint.ts).\r\n */\r\n surfaceKind?: SurfaceKind;\r\n}\r\n\r\nexport interface QueryFilter {\r\n connectorId?: string;\r\n projectKey?: string;\r\n sessionId?: string;\r\n toolName?: string;\r\n /** Lower bound epoch ms (inclusive). */\r\n sinceMs?: number;\r\n}\r\n\r\n/** A grouped rollup row for reports. */\r\nexport interface RollupRow {\r\n /** The grouping key value (tool name, session id, or project dir). */\r\n key: string;\r\n calls: number;\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n /** Worst (least-confident) source seen in the group, for honest labeling. */\r\n confidence: ConfidenceSource;\r\n lastTs: number;\r\n}\r\n\r\n/**\r\n * Local-first telemetry store. The default NDJSON implementation appends one\r\n * line per record (append is atomic enough for concurrent multi-host writers);\r\n * a SQLite/WAL implementation is a drop-in upgrade behind this same interface.\r\n */\r\nexport interface TelemetryStore {\r\n append(record: ToolEventRecord): void;\r\n query(filter: QueryFilter): ToolEventRecord[];\r\n /** Group + aggregate by tool | session | project. */\r\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[];\r\n close(): void;\r\n}\r\n","/**\r\n * telemetry/tokenizer — the default {@link Tokenizer} implementation.\r\n *\r\n * Token counts are the only signal that is identical across every host: no host\r\n * reports a server's per-tool usage back to the server, so the framework\r\n * tokenizes its OWN bytes locally. We use gpt-tokenizer's `o200k_base` encoding\r\n * (the GPT-4o / o1 BPE) for all families:\r\n * • openai → exact BPE for the target family → \"tokenizer-exact\"\r\n * • anthropic → o200k_base used as a documented stand-in → \"tokenizer-approx\"\r\n * • generic → o200k_base used as a generic approximation → \"tokenizer-approx\"\r\n *\r\n * gpt-tokenizer is loaded LAZILY on first use, inside a try/catch. If the module\r\n * (or its BPE data) fails to load we degrade to a chars/4 heuristic that never\r\n * throws, so telemetry can never break a host's tool call.\r\n *\r\n * Pure + side-effect-free apart from the one-time lazy encoder load. No IO.\r\n */\r\n\r\nimport { createRequire } from \"node:module\";\r\n\r\nimport type {\r\n ConfidenceSource,\r\n ModelFamily,\r\n TokenCount,\r\n Tokenizer,\r\n} from \"./types.js\";\r\n\r\n/**\r\n * A CommonJS `require` bound to this module's URL. gpt-tokenizer ships its\r\n * encoding entry points as CJS (the package `exports` `require` condition maps\r\n * `./encoding/*` to `./cjs/encoding/*.js`), so we resolve them synchronously via\r\n * createRequire — this keeps the {@link Tokenizer} surface synchronous and works\r\n * under ESM / NodeNext without baking an absolute path.\r\n */\r\nconst requireCjs = createRequire(import.meta.url);\r\n\r\n/** Minimal shape we use from a gpt-tokenizer encoding module. */\r\ninterface EncodingModule {\r\n encode(text: string): number[];\r\n}\r\n\r\n/**\r\n * Lazy-load state for the shared o200k_base encoder.\r\n * - undefined → not attempted yet\r\n * - null → load attempted and failed (stick to the heuristic)\r\n * - object → loaded encoder\r\n */\r\nlet encoderState: EncodingModule | null | undefined;\r\n\r\n/** Average characters-per-token for the chars/4 fallback heuristic. */\r\nconst HEURISTIC_CHARS_PER_TOKEN = 4;\r\n\r\n/**\r\n * Lazily import the o200k_base encoding. Returns the encoder, or null if the\r\n * module/data could not be loaded (in which case callers use the heuristic).\r\n *\r\n * The dynamic import resolves to `gpt-tokenizer/esm/encoding/o200k_base.js` via\r\n * the package `exports` map and exposes a named `encode(text) => number[]`.\r\n */\r\nfunction loadEncoder(): EncodingModule | null {\r\n if (encoderState !== undefined) return encoderState;\r\n try {\r\n // Synchronous require keeps count() a sync function (per the Tokenizer\r\n // interface). The package's `require` export condition resolves this to\r\n // gpt-tokenizer/cjs/encoding/o200k_base.js, which exports a named `encode`.\r\n const mod = requireCjs(\"gpt-tokenizer/encoding/o200k_base\") as {\r\n encode?: (text: string) => number[];\r\n };\r\n if (mod && typeof mod.encode === \"function\") {\r\n encoderState = { encode: mod.encode.bind(mod) };\r\n } else {\r\n encoderState = null;\r\n }\r\n } catch {\r\n encoderState = null;\r\n }\r\n return encoderState;\r\n}\r\n\r\n/** Heuristic chars/4 count — used when no encoder is available. Never throws. */\r\nfunction heuristicCount(text: string): TokenCount {\r\n return {\r\n tokens: Math.ceil(text.length / HEURISTIC_CHARS_PER_TOKEN),\r\n source: \"heuristic\",\r\n };\r\n}\r\n\r\n/**\r\n * Confidence label for a successful BPE count: exact only for the OpenAI family\r\n * (the encoding actually matches), approximate for everything else.\r\n */\r\nfunction bpeSource(family: ModelFamily): ConfidenceSource {\r\n return family === \"openai\" ? \"tokenizer-exact\" : \"tokenizer-approx\";\r\n}\r\n\r\n/**\r\n * The default tokenizer. A single shared instance is fine: the encoder is\r\n * stateless across calls and lazy-loaded once.\r\n */\r\nclass DefaultTokenizer implements Tokenizer {\r\n count(text: string, family: ModelFamily): TokenCount {\r\n const encoder = loadEncoder();\r\n if (!encoder) return heuristicCount(text);\r\n try {\r\n return { tokens: encoder.encode(text).length, source: bpeSource(family) };\r\n } catch {\r\n // A per-call encode failure (e.g. a pathological input) still degrades\r\n // gracefully rather than breaking the host's tool call.\r\n return heuristicCount(text);\r\n }\r\n }\r\n\r\n countValue(value: unknown, family: ModelFamily): TokenCount {\r\n return this.count(canonicalString(value), family);\r\n }\r\n}\r\n\r\n/**\r\n * Canonical string form of an arbitrary JSON value for counting.\r\n * `undefined` (and anything that JSON.stringify drops to undefined, e.g. a bare\r\n * function or symbol) serializes to the empty string.\r\n */\r\nfunction canonicalString(value: unknown): string {\r\n if (typeof value === \"string\") return value;\r\n const json = JSON.stringify(value);\r\n return json ?? \"\";\r\n}\r\n\r\nconst SHARED_TOKENIZER: Tokenizer = new DefaultTokenizer();\r\n\r\n/** Return the default {@link Tokenizer} (shared singleton). */\r\nexport function getTokenizer(): Tokenizer {\r\n return SHARED_TOKENIZER;\r\n}\r\n\r\n/**\r\n * Infer the {@link ModelFamily} for token counting.\r\n *\r\n * An explicit hint other than \"auto\" wins outright. Otherwise we sniff the\r\n * client/host name: Anthropic/Claude → anthropic, OpenAI/GPT/Codex → openai,\r\n * Gemini and everything unrecognized → generic.\r\n */\r\nexport function inferModelFamily(\r\n clientName: string,\r\n hint: \"auto\" | ModelFamily,\r\n): ModelFamily {\r\n if (hint !== \"auto\") return hint;\r\n const name = clientName.toLowerCase();\r\n if (name.includes(\"claude\") || name.includes(\"anthropic\")) return \"anthropic\";\r\n if (\r\n name.includes(\"codex\") ||\r\n name.includes(\"gpt\") ||\r\n name.includes(\"openai\")\r\n ) {\r\n return \"openai\";\r\n }\r\n // gemini and all others fall through to the generic approximation.\r\n return \"generic\";\r\n}\r\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  assertNoSymlinkInPath,
4
4
  ensureDir
5
- } from "./chunk-GZGLCCEE.js";
5
+ } from "./chunk-QYOUQCC6.js";
6
6
 
7
7
  // src/core/config-patch-ledger.ts
8
8
  import { createHash, randomBytes } from "crypto";
@@ -176,4 +176,4 @@ export {
176
176
  removeLedgerOwner,
177
177
  dropLedgerEntry
178
178
  };
179
- //# sourceMappingURL=chunk-NBYKLUF6.js.map
179
+ //# sourceMappingURL=chunk-KFPSJRHR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config-patch-ledger.ts"],"sourcesContent":["/**\r\n * core/config-patch-ledger — the persisted, refcounted ownership ledger behind\r\n * the declarative `configPatch` surface, plus the shared configPatch helpers\r\n * (leaf-path grammar, JSON-value checks, the agent-connector namespace guard,\r\n * canonical hashing, and the manual-edit hint every skip-warn prints).\r\n *\r\n * WHY A LEDGER (and not the hooks-style recompute ownership): hook entries\r\n * embed the connector id in their command string, so uninstall can identify\r\n * \"mine\" by inspection. configPatch writes bare host keys (`true` is `true`) —\r\n * not attributable by inspection — and the same key may be needed by SEVERAL\r\n * connectors (a shared host feature flag). Without a persisted refcount,\r\n * connector A uninstalling would delete a key connector B still relies on.\r\n *\r\n * Contract (docs/ARCHITECTURE.md §4 \"configPatch\"):\r\n * • One global file at `<dataRoot>/state/config-patches.json`, written\r\n * ATOMICALLY (temp + rename) — shared across connectors because\r\n * refcounting requires a global view; survives a connector's record purge.\r\n * • Primary key per entry: (platform, file, key). `prior` is ALWAYS\r\n * `{ present: false }` — ownership only ever attaches to keys\r\n * agent-connector itself created (set-if-absent), so the blind\r\n * \"restore prior value\" footgun is structurally impossible.\r\n * • Uninstall removes a key ONLY when the releasing connector is the LAST\r\n * owner AND the current value still deep-equals `writtenValue` AND prior\r\n * was absent; any mismatch → skip-warn and leave the key in place.\r\n * • The ledger is advisory FOR DELETION ONLY: the worst desync outcome is a\r\n * skip-warn and a leftover key, never data loss.\r\n */\r\n\r\nimport { createHash, randomBytes } from \"node:crypto\";\r\nimport {\r\n existsSync,\r\n readFileSync,\r\n renameSync,\r\n rmSync,\r\n writeFileSync,\r\n} from \"node:fs\";\r\nimport { dirname, join } from \"node:path\";\r\n\r\nimport type { ConfigPatchDef, JsonValue, PlatformId } from \"./types.js\";\r\nimport { assertNoSymlinkInPath, ensureDir } from \"./paths.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Shared configPatch helpers (grammar / values / namespace guard / hints)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Grammar for ONE dotted-path segment: no dots-in-key, no array indices. */\r\nexport const CONFIG_PATCH_SEGMENT_RE = /^[A-Za-z0-9_-]+$/;\r\n\r\n/**\r\n * True when `key` is a valid dotted LEAF path: 1+ segments joined by \".\",\r\n * every segment matching {@link CONFIG_PATCH_SEGMENT_RE}.\r\n */\r\nexport function isValidConfigPatchKey(key: unknown): key is string {\r\n if (typeof key !== \"string\" || key.length === 0) return false;\r\n return key.split(\".\").every((seg) => CONFIG_PATCH_SEGMENT_RE.test(seg));\r\n}\r\n\r\n/**\r\n * True when `value` is JSON-serializable data (string / finite number /\r\n * boolean / null / arrays / plain objects thereof). Rejects undefined,\r\n * functions, symbols, bigints, NaN/Infinity (JSON.stringify silently corrupts\r\n * those), and class instances other than plain objects/arrays.\r\n */\r\nexport function isJsonValue(value: unknown): value is JsonValue {\r\n if (value === null) return true;\r\n switch (typeof value) {\r\n case \"string\":\r\n case \"boolean\":\r\n return true;\r\n case \"number\":\r\n return Number.isFinite(value);\r\n case \"object\":\r\n break;\r\n default:\r\n return false;\r\n }\r\n if (Array.isArray(value)) return value.every((v) => isJsonValue(v));\r\n const proto = Object.getPrototypeOf(value);\r\n if (proto !== Object.prototype && proto !== null) return false;\r\n return Object.values(value as Record<string, unknown>).every((v) => isJsonValue(v));\r\n}\r\n\r\n/**\r\n * GUARDED NAMESPACE: keys agent-connector already models are NOT patchable —\r\n * the proper surface must be used instead (otherwise a connector could smuggle\r\n * hooks past the nativeHooks modeling/reporting, or fight the server install).\r\n * Returns a human-readable violation pointing at the proper surface, or null.\r\n * Checked at defineConnector (statically knowable) AND re-enforced in the\r\n * adapter (meta-loaded connectors must not bypass it).\r\n */\r\nexport function configPatchNamespaceViolation(key: string): string | null {\r\n const head = key.split(\".\")[0] ?? \"\";\r\n if (head === \"hooks\") {\r\n return `\"${key}\" targets the hooks config agent-connector already models; declare it via \\`hooks\\` (normalized) or \\`platforms.<id>.nativeHooks\\` instead`;\r\n }\r\n if (\r\n head === \"mcpServers\" ||\r\n head === \"enableAllProjectMcpServers\" ||\r\n head === \"enabledMcpjsonServers\" ||\r\n head === \"disabledMcpjsonServers\"\r\n ) {\r\n return `\"${key}\" targets MCP server registration agent-connector already models; declare it via \\`server\\` / \\`platforms.<id>.server\\` / \\`extra\\` instead`;\r\n }\r\n if (head === \"statusLine\") {\r\n return `\"${key}\" is modeled by the statusline surface; declare it via \\`statusline: { render }\\` instead`;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * The exact manual edit a user can perform when a patch is skipped (conflict,\r\n * drift, unsupported host, denylist…). Printed in every skip-warn so a single\r\n * declaration doubles as its own documented manual step.\r\n */\r\nexport function configPatchManualEdit(patch: ConfigPatchDef): string {\r\n const docs = patch.docsUrl ? ` — see ${patch.docsUrl}` : \"\";\r\n return (\r\n `manual edit if wanted: set ${patch.key} = ${describeJsonValue(patch.value)} ` +\r\n `(${patch.reason})${docs}`\r\n );\r\n}\r\n\r\n/** Compact single-line JSON rendering for diffs/warnings (\"<absent>\" for undefined). */\r\nexport function describeJsonValue(value: JsonValue | undefined): string {\r\n if (value === undefined) return \"<absent>\";\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n return String(value);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Canonical JSON / deep equality\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Canonical (sorted-keys) JSON of a value — the hash/equality basis. */\r\nexport function canonicalJson(value: JsonValue | undefined): string {\r\n if (value === undefined) return \"undefined\";\r\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\r\n if (Array.isArray(value)) {\r\n return `[${value.map((v) => canonicalJson(v)).join(\",\")}]`;\r\n }\r\n const keys = Object.keys(value).sort();\r\n const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(value[k])}`);\r\n return `{${parts.join(\",\")}}`;\r\n}\r\n\r\n/** sha256 of the canonical JSON — the fast path; deep-equal is the authority. */\r\nexport function hashJsonValue(value: JsonValue): string {\r\n return createHash(\"sha256\").update(canonicalJson(value)).digest(\"hex\");\r\n}\r\n\r\n/** Order-insensitive (object keys) deep equality over JSON data. */\r\nexport function jsonDeepEquals(\r\n a: JsonValue | undefined,\r\n b: JsonValue | undefined,\r\n): boolean {\r\n return canonicalJson(a) === canonicalJson(b);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Ledger shape + persistence\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One connector's registration on a ledger entry. */\r\nexport interface ConfigPatchOwner {\r\n connectorId: string;\r\n connectorVersion: string;\r\n installedAt: string;\r\n}\r\n\r\n/** Refcounted ownership record for one (platform, file, key). */\r\nexport interface ConfigPatchLedgerEntry {\r\n platform: PlatformId;\r\n /** Absolute path of the file actually written (scope-resolved at install time). */\r\n file: string;\r\n /** Dotted leaf path — (platform, file, key) is the primary key. */\r\n key: string;\r\n writtenValue: JsonValue;\r\n /** sha256 of canonical (sorted-keys) JSON of writtenValue (fast path only). */\r\n writtenValueHash: string;\r\n /** Always { present: false } — ownership ONLY attaches to keys we created. */\r\n prior: { present: false };\r\n owners: ConfigPatchOwner[];\r\n}\r\n\r\nexport interface ConfigPatchLedger {\r\n version: 1;\r\n entries: ConfigPatchLedgerEntry[];\r\n}\r\n\r\n/** Ledger file path under the framework data-root. */\r\nexport function configPatchLedgerPath(dataRoot: string): string {\r\n return join(dataRoot, \"state\", \"config-patches.json\");\r\n}\r\n\r\n/**\r\n * Load the ledger; a missing or corrupt file degrades to an empty ledger\r\n * (advisory-for-deletion-only: a lost ledger can never cause data loss, only\r\n * retained keys + skip-warns).\r\n */\r\nexport function loadConfigPatchLedger(dataRoot: string): ConfigPatchLedger {\r\n const path = configPatchLedgerPath(dataRoot);\r\n if (!existsSync(path)) return { version: 1, entries: [] };\r\n try {\r\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as ConfigPatchLedger;\r\n if (!parsed || !Array.isArray(parsed.entries)) return { version: 1, entries: [] };\r\n return { version: 1, entries: parsed.entries.filter(isPlausibleEntry) };\r\n } catch {\r\n return { version: 1, entries: [] };\r\n }\r\n}\r\n\r\n/** Minimal shape sanity for a persisted entry (corrupt rows are dropped). */\r\nfunction isPlausibleEntry(e: unknown): e is ConfigPatchLedgerEntry {\r\n if (e == null || typeof e !== \"object\") return false;\r\n const entry = e as Partial<ConfigPatchLedgerEntry>;\r\n return (\r\n typeof entry.platform === \"string\" &&\r\n typeof entry.file === \"string\" &&\r\n typeof entry.key === \"string\" &&\r\n Array.isArray(entry.owners)\r\n );\r\n}\r\n\r\n/**\r\n * Persist the ledger ATOMICALLY: write to a temp file in the same directory,\r\n * then rename over the target (rename is atomic on the same filesystem).\r\n */\r\nexport function saveConfigPatchLedger(dataRoot: string, ledger: ConfigPatchLedger): void {\r\n const path = configPatchLedgerPath(dataRoot);\r\n assertNoSymlinkInPath(path);\r\n ensureDir(dirname(path));\r\n const tmpFile = `${path}.${process.pid}-${randomBytes(4).toString(\"hex\")}.tmp`;\r\n try {\r\n writeFileSync(tmpFile, `${JSON.stringify(ledger, null, 2)}\\n`, \"utf8\");\r\n renameSync(tmpFile, path);\r\n } catch (err) {\r\n rmSync(tmpFile, { force: true });\r\n throw err;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Ledger operations (pure in-memory; callers load → mutate → save)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Find the entry for (platform, file, key), or undefined. */\r\nexport function findLedgerEntry(\r\n ledger: ConfigPatchLedger,\r\n platform: PlatformId,\r\n file: string,\r\n key: string,\r\n): ConfigPatchLedgerEntry | undefined {\r\n return ledger.entries.find(\r\n (e) => e.platform === platform && e.file === file && e.key === key,\r\n );\r\n}\r\n\r\n/** Every entry on `platform` owned (at least in part) by `connectorId`. */\r\nexport function ledgerEntriesOwnedBy(\r\n ledger: ConfigPatchLedger,\r\n platform: PlatformId,\r\n connectorId: string,\r\n): ConfigPatchLedgerEntry[] {\r\n return ledger.entries.filter(\r\n (e) => e.platform === platform && e.owners.some((o) => o.connectorId === connectorId),\r\n );\r\n}\r\n\r\n/** Create a fresh entry (prior is absent BY CONSTRUCTION) and append it. */\r\nexport function createLedgerEntry(\r\n ledger: ConfigPatchLedger,\r\n init: {\r\n platform: PlatformId;\r\n file: string;\r\n key: string;\r\n value: JsonValue;\r\n connectorId: string;\r\n connectorVersion: string;\r\n },\r\n): ConfigPatchLedgerEntry {\r\n const entry: ConfigPatchLedgerEntry = {\r\n platform: init.platform,\r\n file: init.file,\r\n key: init.key,\r\n writtenValue: init.value,\r\n writtenValueHash: hashJsonValue(init.value),\r\n prior: { present: false },\r\n owners: [\r\n {\r\n connectorId: init.connectorId,\r\n connectorVersion: init.connectorVersion,\r\n installedAt: new Date().toISOString(),\r\n },\r\n ],\r\n };\r\n ledger.entries.push(entry);\r\n return entry;\r\n}\r\n\r\n/**\r\n * Register `connectorId` as a (co-)owner. Returns true when added, false when\r\n * it was already an owner (idempotent re-install → no ledger mutation).\r\n */\r\nexport function addLedgerOwner(\r\n entry: ConfigPatchLedgerEntry,\r\n connectorId: string,\r\n connectorVersion: string,\r\n): boolean {\r\n if (entry.owners.some((o) => o.connectorId === connectorId)) return false;\r\n entry.owners.push({\r\n connectorId,\r\n connectorVersion,\r\n installedAt: new Date().toISOString(),\r\n });\r\n return true;\r\n}\r\n\r\n/**\r\n * Remove `connectorId` from the owners. Returns whether an owner was removed\r\n * and whether the entry is now ownerless (last owner out).\r\n */\r\nexport function removeLedgerOwner(\r\n entry: ConfigPatchLedgerEntry,\r\n connectorId: string,\r\n): { removed: boolean; lastOwner: boolean } {\r\n const before = entry.owners.length;\r\n entry.owners = entry.owners.filter((o) => o.connectorId !== connectorId);\r\n return { removed: entry.owners.length < before, lastOwner: entry.owners.length === 0 };\r\n}\r\n\r\n/** Drop an entry from the ledger (identity match). */\r\nexport function dropLedgerEntry(\r\n ledger: ConfigPatchLedger,\r\n entry: ConfigPatchLedgerEntry,\r\n): void {\r\n ledger.entries = ledger.entries.filter((e) => e !== entry);\r\n}\r\n"],"mappings":"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","names":[]}
@@ -196,4 +196,4 @@ export {
196
196
  walkFiles,
197
197
  listSubdirs
198
198
  };
199
- //# sourceMappingURL=chunk-O7SDRBVB.js.map
199
+ //# sourceMappingURL=chunk-KM2YXO6V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/paths.ts"],"sourcesContent":["/**\r\n * usage/paths — per-platform HOST storage-root resolution + small fs walkers.\r\n *\r\n * This is the read-only complement to core/paths.ts: where that module resolves\r\n * the FRAMEWORK data-root (~/.agent-connector), this one resolves each agent\r\n * CLI's OWN native storage roots (~/.qwen, ~/.claude, …) so the usage readers\r\n * can parse them. It NEVER writes; it only enumerates files.\r\n *\r\n * Ported from tokscale paths.rs (the tokscale cache dir) and from each reader's\r\n * storage-path spec (docs/research/usage-readers.json). Resolution order is\r\n * uniform across platforms:\r\n * 1. an explicit AGENT_CONNECTOR_<PLATFORM>_DIR env override (verbatim, when\r\n * non-empty — an empty string is treated as unset so we never resolve to \"\");\r\n * 2. the platform-specific default for the current OS.\r\n *\r\n * The \"tokscale cache dir\" (~/.config/tokscale, or AGENT_CONNECTOR_TOKSCALE_DIR)\r\n * is where synced readers (cursor/antigravity/antigravity-cli/trae/warp) look for\r\n * a local cache that a separate tokscale run may have produced — we read it if\r\n * present, but we never create or sync it. (Antigravity's own native store is\r\n * protobuf .pb with no public schema, so it too is synced-cache-or-skip.)\r\n */\r\n\r\nimport {\r\n existsSync,\r\n readdirSync,\r\n statSync,\r\n} from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { isAbsolute, join, resolve } from \"node:path\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Home / env helpers\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Expand a leading \"~\" (and \"~/\") to the user's home directory. */\r\nexport function expandHome(p: string): string {\r\n if (p === \"~\") return homedir();\r\n if (p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\r\n return join(homedir(), p.slice(2));\r\n }\r\n return p;\r\n}\r\n\r\n/**\r\n * Read an env override, treating empty/blank as unset (mirrors tokscale's\r\n * is_config_dir_overridden contract: an empty override must NOT resolve to \"\").\r\n * Relative paths are resolved against the process CWD; \"~\" is expanded.\r\n */\r\nfunction envOverride(name: string): string | undefined {\r\n const raw = process.env[name];\r\n if (raw == null || raw.trim() === \"\") return undefined;\r\n const expanded = expandHome(raw.trim());\r\n return isAbsolute(expanded) ? expanded : resolve(expanded);\r\n}\r\n\r\n/** $XDG_DATA_HOME (when set & non-empty) else ~/.local/share. */\r\nfunction xdgDataHome(): string {\r\n const xdg = process.env.XDG_DATA_HOME;\r\n if (xdg && xdg.trim() !== \"\") return resolve(expandHome(xdg.trim()));\r\n return join(homedir(), \".local\", \"share\");\r\n}\r\n\r\n/** $XDG_CONFIG_HOME (when set & non-empty) else ~/.config. */\r\nfunction xdgConfigHome(): string {\r\n const xdg = process.env.XDG_CONFIG_HOME;\r\n if (xdg && xdg.trim() !== \"\") return resolve(expandHome(xdg.trim()));\r\n return join(homedir(), \".config\");\r\n}\r\n\r\n/** Windows %LOCALAPPDATA% else ~/AppData/Local. */\r\nfunction localAppData(): string {\r\n const v = process.env.LOCALAPPDATA;\r\n if (v && v.trim() !== \"\") return resolve(v.trim());\r\n return join(homedir(), \"AppData\", \"Local\");\r\n}\r\n\r\nconst isMac = process.platform === \"darwin\";\r\nconst isWin = process.platform === \"win32\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// tokscale cache dir (for synced readers)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Resolve the tokscale config dir (where synced caches live). Ported from\r\n * tokscale paths.rs get_config_dir():\r\n * 1. AGENT_CONNECTOR_TOKSCALE_DIR override (verbatim, non-empty);\r\n * 2. macOS: $HOME/.config/tokscale (NOT ~/Library/Application Support);\r\n * 3. Linux: $XDG_CONFIG_HOME/tokscale or ~/.config/tokscale;\r\n * 4. Windows: %APPDATA%/tokscale (config_dir equivalent) else ~/.config/tokscale.\r\n *\r\n * We use AGENT_CONNECTOR_TOKSCALE_DIR (not TOKSCALE_CONFIG_DIR) so the framework\r\n * never accidentally couples to a tokscale install's hermetic test env.\r\n */\r\nexport function tokscaleConfigDir(): string {\r\n const override = envOverride(\"AGENT_CONNECTOR_TOKSCALE_DIR\");\r\n if (override) return override;\r\n if (isMac) return join(homedir(), \".config\", \"tokscale\");\r\n if (isWin) {\r\n const appData = process.env.APPDATA;\r\n if (appData && appData.trim() !== \"\") return join(resolve(appData.trim()), \"tokscale\");\r\n return join(homedir(), \".config\", \"tokscale\");\r\n }\r\n return join(xdgConfigHome(), \"tokscale\");\r\n}\r\n\r\n/** The directory a synced platform's local cache lives under, e.g. `<tokscale>/cursor-cache`. */\r\nexport function tokscaleCacheDir(name: string): string {\r\n return join(tokscaleConfigDir(), name);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Antigravity native store roots (DETECTION ONLY — not parsed for usage)\r\n//\r\n// CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\r\n// the native Antigravity store is `~/.gemini/antigravity/conversations/<uuid>.pb`\r\n// — PROTOBUF with no public schema — and `brain/<uuid>/` holds only media +\r\n// `*.metadata.json`. There are NO `transcript*.jsonl` files. The `agy` CLI shares\r\n// this SAME `~/.gemini/antigravity/` dir (no separate config/storage dir). So the\r\n// native dir is used only for platform DETECTION (in the adapters); the usage\r\n// readers do NOT parse it (protobuf, unreadable) — they are SYNCED readers that\r\n// read the tokscale synced-cache instead (see hostRoots → antigravity-cache).\r\n//\r\n// These helpers are retained for any detection caller and honor the standard\r\n// AGENT_CONNECTOR_<PLATFORM>_DIR override (prepended, preferred when set).\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The native Antigravity store root for DETECTION: `~/.gemini/antigravity/`\r\n * (CONFIRMED). Its `conversations/<uuid>.pb` payloads are protobuf with no public\r\n * schema, so this dir is NOT parsed for usage — only its presence is a signal.\r\n *\r\n * Honors AGENT_CONNECTOR_ANTIGRAVITY_DIR (verbatim, when set) as the override.\r\n */\r\nexport function antigravityNativeRoots(): string[] {\r\n const out: string[] = [];\r\n const override = envOverride(\"AGENT_CONNECTOR_ANTIGRAVITY_DIR\");\r\n if (override) out.push(override);\r\n out.push(join(homedir(), \".gemini\", \"antigravity\"));\r\n return out;\r\n}\r\n\r\n/**\r\n * The native store root for the Antigravity CLI (`agy`) for DETECTION. CONFIRMED:\r\n * `agy` has NO separate dir — it SHARES the IDE's `~/.gemini/antigravity/` (also\r\n * protobuf, not parsed for usage).\r\n *\r\n * Honors AGENT_CONNECTOR_ANTIGRAVITY_CLI_DIR (verbatim, when set) as the override.\r\n */\r\nexport function antigravityCliNativeRoots(): string[] {\r\n const out: string[] = [];\r\n const override = envOverride(\"AGENT_CONNECTOR_ANTIGRAVITY_CLI_DIR\");\r\n if (override) out.push(override);\r\n out.push(join(homedir(), \".gemini\", \"antigravity\"));\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Per-platform host roots\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Candidate host storage roots for a platform, most-preferred first. Readers\r\n * iterate the returned list and use the first that exists (each variant is the\r\n * OS-correct default; the env override, when present, is prepended).\r\n *\r\n * Only the platforms this subsystem currently ships a reader for are listed in\r\n * detail; the table is structured so a new platform is one new case. Returning\r\n * `[]` (no candidate root) makes a reader fail-open to zero records.\r\n */\r\nexport function hostRoots(platformId: string): string[] {\r\n const out: string[] = [];\r\n const override = envOverride(`AGENT_CONNECTOR_${platformId.toUpperCase().replace(/-/g, \"_\")}_DIR`);\r\n if (override) out.push(override);\r\n\r\n switch (platformId) {\r\n case \"qwen-code\":\r\n // ~/.qwen/projects/*/chats/*.jsonl\r\n out.push(join(homedir(), \".qwen\", \"projects\"));\r\n break;\r\n case \"claude-code\":\r\n out.push(join(homedir(), \".claude\", \"projects\"));\r\n break;\r\n case \"codex\":\r\n out.push(join(homedir(), \".codex\", \"sessions\"));\r\n break;\r\n case \"gemini-cli\":\r\n out.push(join(homedir(), \".gemini\", \"tmp\"));\r\n break;\r\n case \"pi\":\r\n out.push(join(homedir(), \".pi\", \"agent\", \"sessions\"));\r\n break;\r\n case \"kimi\":\r\n // TWO Moonshot products share the \"kimi\" id with DIFFERENT session trees:\r\n // - Kimi Code (newer, @moonshot-ai/kimi-code): ~/.kimi-code/sessions/...\r\n // (deeper tree, see readers/kimi.ts) — the product our adapter targets.\r\n // - Kimi CLI (older): ~/.kimi/sessions/<GROUP>/<UUID>/wire.jsonl\r\n // Both are scanned (Kimi Code preferred first). $KIMI_CODE_HOME relocates\r\n // the Kimi Code home and is honored ahead of these in readers/kimi.ts.\r\n out.push(join(homedir(), \".kimi-code\", \"sessions\"));\r\n out.push(join(homedir(), \".kimi\", \"sessions\"));\r\n break;\r\n case \"copilot-cli\":\r\n out.push(join(xdgDataHome(), \"Copilot\", \"telemetry\"));\r\n out.push(join(xdgConfigHome(), \"copilot\", \"telemetry\"));\r\n break;\r\n case \"amp\":\r\n out.push(join(xdgDataHome(), \"amp\", \"threads\"));\r\n break;\r\n case \"droid\":\r\n out.push(join(homedir(), \".factory\", \"sessions\"));\r\n break;\r\n case \"mux\":\r\n out.push(join(homedir(), \".mux\", \"sessions\"));\r\n break;\r\n case \"kiro\":\r\n out.push(join(homedir(), \".kiro\", \"sessions\", \"cli\"));\r\n break;\r\n case \"opencode\":\r\n out.push(join(xdgDataHome(), \"opencode\", \"opencode.db\"));\r\n break;\r\n case \"goose\":\r\n if (isMac) out.push(join(homedir(), \"Library\", \"Application Support\", \"goose\", \"sessions\", \"sessions.db\"));\r\n out.push(join(xdgDataHome(), \"goose\", \"sessions\", \"sessions.db\"));\r\n out.push(join(xdgDataHome(), \"Block\", \"goose\", \"sessions\", \"sessions.db\"));\r\n break;\r\n case \"hermes\":\r\n out.push(join(homedir(), \".hermes\", \"state.db\"));\r\n break;\r\n case \"kilo-cli\":\r\n out.push(join(xdgDataHome(), \"kilo\", \"kilo.db\"));\r\n break;\r\n case \"crush\":\r\n out.push(join(homedir(), \".cache\", \"crush\", \"crush.db\"));\r\n break;\r\n case \"zed\":\r\n if (isMac) out.push(join(homedir(), \"Library\", \"Application Support\", \"Zed\", \"threads\", \"threads.db\"));\r\n else if (isWin) out.push(join(localAppData(), \"Zed\", \"threads\", \"threads.db\"));\r\n else out.push(join(xdgDataHome(), \"zed\", \"threads\", \"threads.db\"));\r\n break;\r\n // Synced platforms: local cache under the tokscale config dir.\r\n case \"cursor\":\r\n out.push(tokscaleCacheDir(\"cursor-cache\"));\r\n break;\r\n case \"antigravity\":\r\n // SYNCED: the native store is protobuf (.pb, no public schema) and NOT\r\n // parseable, so the reader reads only the tokscale synced-cache mirror if a\r\n // separate tokscale run produced one (else [] → \"requires sync\"). The\r\n // native `~/.gemini/antigravity/` dir is used only for detection.\r\n out.push(tokscaleCacheDir(\"antigravity-cache\"));\r\n break;\r\n case \"antigravity-cli\":\r\n // SYNCED: `agy` shares the IDE's protobuf (.pb) store (no schema), so the\r\n // reader reads only the tokscale synced-cache mirror if present (else [] →\r\n // \"requires sync\"). Shares the IDE's antigravity-cache name (same data).\r\n out.push(tokscaleCacheDir(\"antigravity-cache\"));\r\n break;\r\n case \"trae\":\r\n out.push(tokscaleCacheDir(\"trae-cache\"));\r\n break;\r\n case \"warp\":\r\n out.push(tokscaleCacheDir(\"warp-cache\"));\r\n break;\r\n default:\r\n break;\r\n }\r\n return out;\r\n}\r\n\r\n/** First existing host root for a platform, or undefined when none is present. */\r\nexport function firstExistingRoot(platformId: string): string | undefined {\r\n for (const root of hostRoots(platformId)) {\r\n if (existsSync(root)) return root;\r\n }\r\n return undefined;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Directory / glob walkers (tolerant — never throw)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Is the path an existing directory? Tolerant (false on any stat error). */\r\nexport function isDir(p: string): boolean {\r\n try {\r\n return statSync(p).isDirectory();\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Is the path an existing regular file? Tolerant (false on any stat error). */\r\nexport function isFile(p: string): boolean {\r\n try {\r\n return statSync(p).isFile();\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Recursively list every file under `root` whose name matches `predicate`.\r\n * Tolerant: an unreadable directory is skipped, never thrown. Returns absolute\r\n * paths in directory-walk order. A non-existent root yields [].\r\n */\r\nexport function walkFiles(\r\n root: string,\r\n predicate: (name: string, absPath: string) => boolean,\r\n maxDepth = 12,\r\n): string[] {\r\n const out: string[] = [];\r\n if (!isDir(root)) return out;\r\n\r\n const stack: Array<{ dir: string; depth: number }> = [{ dir: root, depth: 0 }];\r\n while (stack.length > 0) {\r\n const { dir, depth } = stack.pop() as { dir: string; depth: number };\r\n let entries: import(\"node:fs\").Dirent[];\r\n try {\r\n entries = readdirSync(dir, { withFileTypes: true });\r\n } catch {\r\n continue; // unreadable dir → skip\r\n }\r\n for (const entry of entries) {\r\n const abs = join(dir, entry.name);\r\n if (entry.isDirectory()) {\r\n if (depth < maxDepth) stack.push({ dir: abs, depth: depth + 1 });\r\n } else if (entry.isFile() && predicate(entry.name, abs)) {\r\n out.push(abs);\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * List immediate subdirectory names of `dir` (not recursive). Tolerant: returns\r\n * [] for a missing/unreadable directory.\r\n */\r\nexport function listSubdirs(dir: string): string[] {\r\n if (!isDir(dir)) return [];\r\n try {\r\n return readdirSync(dir, { withFileTypes: true })\r\n .filter((e) => e.isDirectory())\r\n .map((e) => join(dir, e.name));\r\n } catch {\r\n return [];\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  compileMatcher,
4
4
  eventMatcherSubject
5
- } from "./chunk-I5UXHIDT.js";
5
+ } from "./chunk-GCPEPUD7.js";
6
6
  import {
7
7
  SURFACE_PREDICATES,
8
8
  hostCanFireEvent
9
- } from "./chunk-3NH2ARIS.js";
9
+ } from "./chunk-WPTNMYYM.js";
10
10
  import {
11
11
  allAdapters,
12
12
  loadAdapter
13
- } from "./chunk-D4ELPBB5.js";
13
+ } from "./chunk-NDMNMAKP.js";
14
14
 
15
15
  // src/sdk/test-harness.ts
16
16
  function declaredGlobalSurfaces(connector) {
@@ -383,4 +383,4 @@ export {
383
383
  simulate,
384
384
  explainHooks
385
385
  };
386
- //# sourceMappingURL=chunk-7F5KHLEP.js.map
386
+ //# sourceMappingURL=chunk-L2C5UMKW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sdk/test-harness.ts"],"sourcesContent":["/**\r\n * sdk/test-harness — the SHIPPED, OFFLINE behavioral harness.\r\n *\r\n * Two read-only tools a connector author runs WITHOUT installing anything or\r\n * touching the host's filesystem:\r\n *\r\n * • `explain(connector)` — the static per-host × per-DECLARED-surface matrix:\r\n * for every registered host and every surface the connector actually ships,\r\n * is it `native` (the host can honor it), `disabled` (the connector opted\r\n * out for that host via `platforms[host].<surface> === false`), or\r\n * `skip-warn` (the host cannot honor it → install would skip-warn)?\r\n *\r\n * • `simulate(connector, opts)` — runs the REAL adapter parse → handler →\r\n * format chain INLINE against a host-shaped raw payload and reports whether\r\n * the host actually HONORS the connector's response (the trustworthy part:\r\n * it surfaces the documented degradations, e.g. Codex silently dropping a\r\n * UserPromptSubmit \"context\" injection that has no stdout path).\r\n *\r\n * It deliberately does NOT import the runtime entrypoints (runHook /\r\n * runStatusline) — those load the REGISTERED connector from disk and record\r\n * telemetry. The harness instead re-implements the tiny parse→handler→format\r\n * chain against the in-memory connector + the adapter, mirroring the runtime's\r\n * input tolerance (empty/malformed payload → `{}`) and its fail-safe envelope.\r\n */\r\n\r\nimport type {\r\n HookEventName,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PlatformOverride,\r\n ResolvedConnector,\r\n} from \"../core/types.js\";\r\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\r\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\r\nimport {\r\n compileMatcher,\r\n eventMatcherSubject,\r\n} from \"../runtime/hook-entrypoint.js\";\r\nimport type { SurfaceName } from \"./introspect.js\";\r\nimport { hostCanFireEvent, SURFACE_PREDICATES } from \"./introspect.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explain — the static per-host × per-declared-surface matrix\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One cell of the {@link explain} matrix: a host × a declared surface. */\r\nexport interface ExplainRow {\r\n host: PlatformId;\r\n surface: SurfaceName;\r\n /**\r\n * - \"native\" — the host's capabilities pass the surface predicate.\r\n * - \"disabled\" — the connector opted out for this host\r\n * (`platforms[host].<surface> === false`).\r\n * - \"skip-warn\" — the host cannot honor it; install would skip-warn.\r\n */\r\n support: \"native\" | \"skip-warn\" | \"disabled\";\r\n reason: string;\r\n}\r\n\r\n/**\r\n * The GLOBAL surfaces a connector declares (these apply to every host — only\r\n * declared surfaces get a matrix row). The two platform-scoped surfaces\r\n * (`configPatch` / `nativeHooks`) are deliberately EXCLUDED here: they are\r\n * declared under one specific `platforms[host]`, so each is added PER HOST in\r\n * {@link explain} (see {@link platformScopedSurfacesFor}) — adding them globally\r\n * would falsely promise a configPatch row on hosts that never declared it.\r\n */\r\nfunction declaredGlobalSurfaces(connector: ResolvedConnector): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (connector.server) out.push(\"server\");\r\n if (Object.keys(connector.hooks).length > 0) out.push(\"hooks\");\r\n if (connector.commands.length > 0) out.push(\"commands\");\r\n if (connector.skills.length > 0) out.push(\"skills\");\r\n if (connector.subagents.length > 0) out.push(\"subagents\");\r\n if (connector.memory.length > 0) out.push(\"memory\");\r\n if (connector.statusline) out.push(\"statusline\");\r\n if (connector.actions.length > 0) out.push(\"actions\");\r\n return out;\r\n}\r\n\r\n/**\r\n * The platform-scoped surfaces (`configPatch` / `nativeHooks`) THIS host\r\n * actually declares — keyed off the host's own `platforms[host]` override, so a\r\n * configPatch declared under claude-code never shows up as a row for codex.\r\n */\r\nfunction platformScopedSurfacesFor(\r\n override: PlatformOverride | undefined,\r\n): SurfaceName[] {\r\n const out: SurfaceName[] = [];\r\n if (!override) return out;\r\n if ((override.configPatch?.length ?? 0) > 0) out.push(\"configPatch\");\r\n if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {\r\n out.push(\"nativeHooks\");\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Is the surface explicitly DISABLED for this host? Mirrors the per-host opt-out\r\n * the installer honors: a boolean-or-object surface override is \"disabled\" only\r\n * when set to the literal `false` (the object form is a tuning, not an opt-out).\r\n * `configPatch` / `nativeHooks` have no boolean opt-out form — declaring them is\r\n * the opt-in, so they are never \"disabled\".\r\n */\r\nfunction isDisabledForHost(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n surface: SurfaceName,\r\n): boolean {\r\n const override = connector.platforms[host];\r\n if (!override) return false;\r\n switch (surface) {\r\n case \"server\":\r\n return override.server === false;\r\n case \"hooks\":\r\n return override.hooks === false;\r\n case \"commands\":\r\n return override.commands === false;\r\n case \"skills\":\r\n return override.skills === false;\r\n case \"subagents\":\r\n return override.subagents === false;\r\n case \"memory\":\r\n return override.memory === false;\r\n case \"statusline\":\r\n return override.statusline === false;\r\n case \"actions\":\r\n case \"configPatch\":\r\n case \"nativeHooks\":\r\n // No per-host boolean opt-out form (declaring them is the opt-in).\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * The static support matrix: every registered host × every surface the\r\n * connector declares. Rows are sorted by host then surface for stable output.\r\n * Pure read of the adapter capabilities — installs nothing, writes nothing.\r\n */\r\nexport async function explain(connector: ResolvedConnector): Promise<ExplainRow[]> {\r\n const globalSurfaces = declaredGlobalSurfaces(connector);\r\n const adapters = await allAdapters();\r\n const rows: ExplainRow[] = [];\r\n\r\n for (const adapter of adapters) {\r\n // Per-host surface set = the global surfaces every host gets a row for, plus\r\n // the platform-scoped surfaces THIS host actually declares (configPatch /\r\n // nativeHooks live under platforms[host], so they are host-local).\r\n const override = connector.platforms[adapter.id];\r\n const surfaces = [\r\n ...globalSurfaces,\r\n ...platformScopedSurfacesFor(override),\r\n ];\r\n for (const surface of surfaces) {\r\n if (isDisabledForHost(connector, adapter.id, surface)) {\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: \"disabled\",\r\n reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`,\r\n });\r\n continue;\r\n }\r\n // The hooks surface is judged against the connector's SPECIFIC declared\r\n // events, not the coarse \"host has a hook runtime\" predicate — otherwise a\r\n // Stop-only connector falsely shows `native` on a PreToolUse-only host\r\n // (the host CAN fire a hook, just not the one this connector declared).\r\n if (surface === \"hooks\") {\r\n rows.push(hooksRow(connector, adapter.id, adapter.capabilities));\r\n continue;\r\n }\r\n const native = SURFACE_PREDICATES[surface](adapter.capabilities);\r\n rows.push({\r\n host: adapter.id,\r\n surface,\r\n support: native ? \"native\" : \"skip-warn\",\r\n reason: explainReason(adapter.id, surface, native),\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n\r\n/**\r\n * The per-DECLARED-event hooks verdict for one host. A connector's hooks surface\r\n * is `native` only when EVERY event it declares can fire on the host; if any\r\n * declared event has no native equivalent the row is `skip-warn` and the reason\r\n * names the specific dead events (the same events install skip-warns). This is\r\n * the fix for the OR-across-all-events false green: a Stop-only connector on a\r\n * host that cannot fire Stop now reports skip-warn, not native.\r\n *\r\n * Reuses {@link hostCanFireEvent} — the single per-event source of truth shared\r\n * with {@link SURFACE_PREDICATES} and simulate's runtime verdict — so explain\r\n * never re-derives the per-event rules.\r\n */\r\nfunction hooksRow(\r\n connector: ResolvedConnector,\r\n host: PlatformId,\r\n capabilities: PlatformCapabilities,\r\n): ExplainRow {\r\n const declared = connector.hookEvents;\r\n const dead = declared.filter((e) => !hostCanFireEvent(capabilities, e));\r\n if (dead.length === 0) {\r\n return {\r\n host,\r\n surface: \"hooks\",\r\n support: \"native\",\r\n reason: `${host} can fire every declared hook event (${declared.join(\", \")})`,\r\n };\r\n }\r\n const fireable = declared.filter((e) => hostCanFireEvent(capabilities, e));\r\n const reason =\r\n fireable.length === 0\r\n ? `${host} cannot fire any declared hook event (${dead.join(\", \")}); install skip-warns each`\r\n : `${host} cannot fire ${dead.join(\", \")} (install skip-warns these); honors ${fireable.join(\", \")}`;\r\n return { host, surface: \"hooks\", support: \"skip-warn\", reason };\r\n}\r\n\r\n/**\r\n * The human-readable reason for a native/skip-warn cell. `server` is a special\r\n * case: the predicate is CAPABILITY-based (the host advertises ≥1 MCP\r\n * transport), but advertising a transport does NOT guarantee a writable\r\n * registration — e.g. jetbrains-copilot advertises stdio yet its installServer\r\n * skip-warns because MCP is managed in IDE settings. So the server reason states\r\n * the verdict is capability-based and registration may be host-managed, rather\r\n * than promising a write. (A precise per-adapter installServer dry-run is a\r\n * Phase-2 enhancement — until then this avoids over-promising.)\r\n */\r\nfunction explainReason(host: PlatformId, surface: SurfaceName, native: boolean): string {\r\n if (surface === \"server\") {\r\n return native\r\n ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)`\r\n : `${host} advertises no MCP transport; install skip-warns`;\r\n }\r\n return native\r\n ? `${host} natively supports ${surface}`\r\n : `${host} cannot honor ${surface}; install skip-warns`;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// simulate — the real adapter parse → handler → format chain, inline\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** What to drive through the harness: one host, one surface, one raw payload. */\r\nexport interface SimulateOptions {\r\n surface: \"hooks\" | \"statusline\";\r\n host: PlatformId | string;\r\n /** REQUIRED when `surface === \"hooks\"`: which normalized lifecycle event. */\r\n event?: HookEventName;\r\n /** The host-shaped RAW payload (what the host would pipe in on stdin). */\r\n input: unknown;\r\n}\r\n\r\n/** The harness verdict: did the host actually honor the connector's response? */\r\nexport interface SimulateResult {\r\n /** True when the host's native reply carries the connector's intent. */\r\n honored: boolean;\r\n /** The host-native stdout the adapter would emit (when there is one). */\r\n hostReply?: string;\r\n /** Human-readable why — names the documented degradation when honored:false. */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * Tolerantly coerce the raw payload, mirroring the runtime's parseStdin: a\r\n * string is JSON-parsed (empty/malformed → `{}`); a nullish value → `{}`; any\r\n * other value (already-parsed object) is passed through untouched.\r\n */\r\nfunction coerceInput(input: unknown): unknown {\r\n if (input == null) return {};\r\n if (typeof input === \"string\") {\r\n const trimmed = input.trim();\r\n if (trimmed === \"\") return {};\r\n try {\r\n return JSON.parse(trimmed);\r\n } catch {\r\n return {};\r\n }\r\n }\r\n return input;\r\n}\r\n\r\n/** Normalize a handler's return into a concrete response (void/non-object → `{}`). */\r\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\r\n if (value == null || typeof value !== \"object\") return {};\r\n return value;\r\n}\r\n\r\n/**\r\n * The decoded shape of a host's native hook reply on stdout. Every json-stdio\r\n * adapter writes a SUBSET of these fields (claude-code / codex share the\r\n * `hookSpecificOutput` envelope; Stop-class continuation uses the TOP-LEVEL\r\n * `decision:\"block\"`). We read the DECODED fields — never substring-match the\r\n * serialized JSON, which a path/newline/quote in the payload would break.\r\n */\r\ninterface ParsedReply {\r\n /** Top-level Stop-class continuation: {\"decision\":\"block\",\"reason\"}. */\r\n decision?: unknown;\r\n reason?: unknown;\r\n /** Top-level Claude context channel (no adapter emits it yet; future-proofing). */\r\n systemMessage?: unknown;\r\n hookSpecificOutput?: {\r\n hookEventName?: unknown;\r\n additionalContext?: unknown;\r\n permissionDecision?: unknown;\r\n permissionDecisionReason?: unknown;\r\n updatedInput?: unknown;\r\n /** PermissionRequest nested envelope: decision:{behavior:\"allow\"|\"deny\", updatedInput?}. */\r\n decision?: { behavior?: unknown; updatedInput?: unknown };\r\n };\r\n}\r\n\r\n/**\r\n * Tolerantly decode a reply's stdout into {@link ParsedReply}. Empty / absent /\r\n * unparseable stdout is a legitimate \"no payload\" signal (the adapter wrote a\r\n * bare exit-0) → `undefined`, the honest \"this decision had no stdout path\"\r\n * verdict input.\r\n */\r\nfunction parseReplyStdout(stdout: string | undefined): ParsedReply | undefined {\r\n if (stdout == null || stdout.trim() === \"\") return undefined;\r\n try {\r\n const value = JSON.parse(stdout);\r\n return value != null && typeof value === \"object\"\r\n ? (value as ParsedReply)\r\n : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/** Structural deep-equal for plain JSON values (objects/arrays/primitives). */\r\nfunction deepEqual(a: unknown, b: unknown): boolean {\r\n if (a === b) return true;\r\n if (typeof a !== typeof b || a == null || b == null) return false;\r\n if (typeof a !== \"object\") return false;\r\n const aArr = Array.isArray(a);\r\n if (aArr !== Array.isArray(b)) return false;\r\n if (aArr) {\r\n const x = a as unknown[];\r\n const y = b as unknown[];\r\n return x.length === y.length && x.every((v, i) => deepEqual(v, y[i]));\r\n }\r\n const x = a as Record<string, unknown>;\r\n const y = b as Record<string, unknown>;\r\n const xk = Object.keys(x);\r\n const yk = Object.keys(y);\r\n return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));\r\n}\r\n\r\n/**\r\n * Decide whether the host HONORED a decision-bearing response, judging the\r\n * PARSED reply against each adapter's ACTUAL (event, decision) contract — never\r\n * by substring-matching the serialized JSON. `allow`/no-decision is always\r\n * pass-through honored; every other decision is honored only when the decoded\r\n * reply carries that exact intent, and the verdict names the documented\r\n * degradation when it does not.\r\n */\r\nfunction judgeHookHonor(\r\n host: string,\r\n event: HookEventName,\r\n response: HookResponse,\r\n stdout: string | undefined,\r\n): { honored: boolean; reason: string } {\r\n const decision = response.decision;\r\n if (decision === undefined || decision === \"allow\") {\r\n return { honored: true, reason: \"pass-through allow\" };\r\n }\r\n\r\n const reply = parseReplyStdout(stdout);\r\n const hso = reply?.hookSpecificOutput;\r\n\r\n if (decision === \"context\") {\r\n // Honored only when the DECODED additionalContext equals what the handler\r\n // asked to inject (claude-code's hookSpecificOutput.additionalContext, or a\r\n // future top-level systemMessage). codex only emits this on\r\n // SessionStart/PostToolUse — every other event silently drops it.\r\n const want = response.additionalContext;\r\n const got = hso?.additionalContext ?? reply?.systemMessage;\r\n if (reply && got === want) {\r\n return { honored: true, reason: `${host} injects context on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops context on ${event} (no stdout path)`,\r\n };\r\n }\r\n\r\n if (decision === \"deny\") {\r\n if (!reply) {\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n // A real block: PreToolUse's permissionDecision:\"deny\", or the\r\n // PermissionRequest nested decision.behavior:\"deny\".\r\n if (hso?.permissionDecision === \"deny\" || hso?.decision?.behavior === \"deny\") {\r\n return { honored: true, reason: `${host} blocks ${event}` };\r\n }\r\n // Stop-class continuation: top-level decision:\"block\" KEEPS the session /\r\n // subagent running (persistence) — the SEMANTIC OPPOSITE of a block. Honored\r\n // (the reply carried the intent), but the reason must not say \"blocks\".\r\n if (reply.decision === \"block\") {\r\n return {\r\n honored: true,\r\n reason: `${host} continues the session/subagent on ${event} (persistence) — NOT a block`,\r\n };\r\n }\r\n // SubagentStart / PostToolUseFailure DEGRADE a deny to a context note — the\r\n // spawn/failure is not actually blocked.\r\n if (hso?.additionalContext != null) {\r\n return {\r\n honored: false,\r\n reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\r\n };\r\n }\r\n\r\n if (decision === \"ask\") {\r\n if (hso?.permissionDecision === \"ask\") {\r\n return { honored: true, reason: `${host} asks on ${event}` };\r\n }\r\n // On PermissionRequest, ask/void falls through to the host's NATIVE dialog\r\n // (the dialog IS the ask), which the adapter expresses as a bare exit-0.\r\n if (event === \"PermissionRequest\" && !reply) {\r\n return {\r\n honored: true,\r\n reason: `${host} shows the native confirmation dialog on ${event} (the ask)`,\r\n };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops ask on ${event} (no ask path)`,\r\n };\r\n }\r\n\r\n // decision === \"modify\": honored only when the decoded updatedInput deep-equals\r\n // what the handler asked to write (hookSpecificOutput.updatedInput, or the\r\n // PermissionRequest nested decision.updatedInput).\r\n const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;\r\n if (reply && wrote !== undefined && deepEqual(wrote, response.updatedInput)) {\r\n return { honored: true, reason: `${host} rewrites tool input on ${event}` };\r\n }\r\n return {\r\n honored: false,\r\n reason: `${host} drops modify on ${event} (modify not honored)`,\r\n };\r\n}\r\n\r\n/**\r\n * Run the connector against ONE host-shaped payload through the REAL adapter\r\n * path and report whether the host honors the response. Self-contained (does not\r\n * touch the registered-connector loader or telemetry) and fail-safe: any throw\r\n * in the chain degrades to `honored:false` with the error message, never rejects.\r\n */\r\nexport async function simulate(\r\n connector: ResolvedConnector,\r\n opts: SimulateOptions,\r\n): Promise<SimulateResult> {\r\n try {\r\n const adapter = await loadAdapter(opts.host);\r\n if (!adapter) {\r\n return { honored: false, reason: `unknown host \"${opts.host}\"` };\r\n }\r\n\r\n if (opts.surface === \"statusline\") {\r\n if (\r\n !(adapter.capabilities.supportsStatusline ?? false) ||\r\n !adapter.parseStatusInput ||\r\n !adapter.formatStatusOutput\r\n ) {\r\n return { honored: false, reason: `${opts.host} has no statusline surface` };\r\n }\r\n // Guarding on the TOP-LEVEL render is sufficient: a `hosts`-only statusline\r\n // (a hosts: map with no top-level render) is rejected at defineConnector, so\r\n // a connector that targets `opts.host` only via hosts[] still has a top-level\r\n // render here. (If that author-time invariant ever relaxes, revisit this.)\r\n if (!connector.statusline || typeof connector.statusline.render !== \"function\") {\r\n return { honored: false, reason: \"connector declares no statusline\" };\r\n }\r\n const ctx = adapter.parseStatusInput(coerceInput(opts.input));\r\n ctx.connectorId = connector.id;\r\n // Defensive: parseStatusInput now sets ctx.capabilities (it is required on\r\n // StatuslineContext), but a future adapter could omit it — backfill from\r\n // the adapter so render() can always read ctx.capabilities.\r\n if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;\r\n // Per-host render override, mirroring the runtime: hosts[host].render wins\r\n // over the top-level render; an unlisted host falls back to top-level.\r\n const perHost = connector.statusline.hosts?.[opts.host as PlatformId]?.render;\r\n const render =\r\n typeof perHost === \"function\" ? perHost : connector.statusline.render;\r\n const rendered = await render(ctx);\r\n const line = rendered == null ? \"\" : String(rendered);\r\n const reply = adapter.formatStatusOutput(line);\r\n return {\r\n honored: true,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: \"rendered\",\r\n };\r\n }\r\n\r\n // surface === \"hooks\"\r\n const event = opts.event;\r\n if (!event) {\r\n return { honored: false, reason: \"event is required when surface === \\\"hooks\\\"\" };\r\n }\r\n if (!adapter.parseEvent || !adapter.formatReply) {\r\n return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };\r\n }\r\n\r\n const evt = adapter.parseEvent(event, coerceInput(opts.input));\r\n evt.connectorId = connector.id;\r\n // Mirror the runtime: runHook backfills evt.capabilities from the adapter so\r\n // a handler reading evt.capabilities behaves the same here. (scope/telemetry\r\n // are runtime-only — recovered from the registered meta / store, which the\r\n // offline harness deliberately never touches — so they stay undefined.)\r\n evt.capabilities = adapter.capabilities;\r\n\r\n // Guarding on the TOP-LEVEL handler is sufficient: a `hosts`-only hook (a\r\n // hosts: map with no top-level handler) is rejected at defineConnector, so a\r\n // host targeted only via hosts[] still has a top-level handler here. (If that\r\n // author-time invariant ever relaxes, revisit this.)\r\n const definition = connector.hooks[event];\r\n if (!definition || typeof definition.handler !== \"function\") {\r\n return { honored: false, reason: `connector declares no ${event} handler` };\r\n }\r\n\r\n // Matcher filtering, in lockstep with runHook: when the connector's matcher\r\n // is set and the event's subject (tool name, or agent type for Subagent*\r\n // events) does not match, the host never runs the handler — so the harness\r\n // must NOT run it either, and reports the (honored) exclusion. Reuses the\r\n // runtime helpers directly so the two stay byte-for-byte aligned.\r\n const subject = eventMatcherSubject(evt as NormalizedEvent);\r\n if (subject !== undefined) {\r\n const re = compileMatcher(definition.matcher);\r\n if (re && !re.test(subject)) {\r\n return {\r\n honored: true,\r\n reason: `matcher excludes ${subject} (handler not run)`,\r\n };\r\n }\r\n }\r\n\r\n // Per-host handler override, mirroring runHook: hosts[host].handler wins\r\n // over the top-level handler; an unlisted host falls back to top-level.\r\n const perHost = definition.hosts?.[opts.host as PlatformId]?.handler;\r\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\r\n const response = normalizeResponse(\r\n await handler(evt as never),\r\n );\r\n const reply = adapter.formatReply(event, response);\r\n const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);\r\n return {\r\n honored: verdict.honored,\r\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\r\n reason: verdict.reason,\r\n };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const verb = opts.surface === \"statusline\" ? \"render\" : \"handler\";\r\n return { honored: false, reason: `${verb} threw: ${message}` };\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// explainHooks — the per-(host, event) honor verdict (powers doctor --explain)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * One per-(host, event) cell of {@link explainHooks}.\r\n *\r\n * - \"honored\" — the host fires the event AND carries the connector's response\r\n * intent (simulate's runtime verdict, e.g. a real deny/block).\r\n * - \"degraded\" — the host fires the event but silently DROPS the response's\r\n * decision (e.g. codex fires UserPromptSubmit but has no stdout\r\n * path for a context injection, so the injection vanishes).\r\n * - \"dropped\" — the host cannot fire the event at all (no native equivalent /\r\n * mcp-only host); install skip-warns it and it never runs.\r\n */\r\nexport interface HookEventVerdict {\r\n host: PlatformId;\r\n event: HookEventName;\r\n verdict: \"honored\" | \"degraded\" | \"dropped\";\r\n /** The simulate()-grade reason string (names the documented degradation). */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * A subject (tool name / agent type) that SATISFIES the connector's matcher for\r\n * this event, so a matcher-gated hook actually runs and the verdict reflects the\r\n * real honor — not a spurious \"matcher excludes my arbitrary synthetic tool\".\r\n * A plain-literal matcher (no regex metacharacters — the common case, e.g.\r\n * `acme_query`) IS the subject. A missing/empty matcher matches everything, and\r\n * a complex regex falls back to the generic subject (then an honest \"matcher\r\n * excludes\" verdict surfaces — the dev's matcher genuinely would not fire here).\r\n */\r\nfunction matchingSubject(matcher: string | undefined, fallback: string): string {\r\n if (!matcher || matcher === \"\") return fallback;\r\n return /^[\\w.-]+$/.test(matcher) ? matcher : fallback;\r\n}\r\n\r\n/**\r\n * A minimal host-shaped synthetic payload so the real parse→handler→format\r\n * chain has something to run on. The harness coerces a `{}` to a valid event,\r\n * but tool/subagent events read a subject (tool name / agent type) the connector\r\n * matcher may filter on — so we plant a subject that SATISFIES the connector's\r\n * declared matcher ({@link matchingSubject}) to exercise the honor path.\r\n */\r\nfunction syntheticPayloadFor(\r\n event: HookEventName,\r\n matcher: string | undefined,\r\n): Record<string, unknown> {\r\n switch (event) {\r\n case \"PreToolUse\":\r\n case \"PostToolUse\":\r\n case \"PostToolUseFailure\":\r\n case \"PermissionRequest\": {\r\n const tool = matchingSubject(matcher, \"Bash\");\r\n return { tool_name: tool, tool_input: { command: \"echo hi\" } };\r\n }\r\n case \"SubagentStart\":\r\n case \"SubagentStop\": {\r\n const agent = matchingSubject(matcher, \"general\");\r\n return { agent_type: agent, subagent_type: agent };\r\n }\r\n case \"UserPromptSubmit\":\r\n return { prompt: \"hello\" };\r\n default:\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * The per-(host, event) hook honor matrix for the connector's DECLARED events\r\n * across `hosts` — the trustworthy detail behind `doctor --explain`.\r\n *\r\n * For each (host, declared event): when the host cannot fire the event\r\n * ({@link hostCanFireEvent} is false, OR the host has no hook runtime at all)\r\n * the cell is `dropped`; otherwise it RUNS the real chain via {@link simulate}\r\n * and reports `honored` (simulate honored the response) or `degraded` (the host\r\n * fired but dropped the decision). Reuses the per-event signal + simulate's\r\n * verdict — no per-event honor logic is re-derived here.\r\n */\r\nexport async function explainHooks(\r\n connector: ResolvedConnector,\r\n hosts: (PlatformId | string)[],\r\n): Promise<HookEventVerdict[]> {\r\n const events = connector.hookEvents;\r\n const rows: HookEventVerdict[] = [];\r\n\r\n for (const host of hosts) {\r\n const adapter = await loadAdapter(host);\r\n if (!adapter) {\r\n for (const event of events) {\r\n rows.push({\r\n host: host as PlatformId,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `unknown host \"${host}\"`,\r\n });\r\n }\r\n continue;\r\n }\r\n\r\n // An mcp-only host (no hook runtime) drops every declared event.\r\n const hasHookRuntime = !!adapter.parseEvent && !!adapter.formatReply;\r\n\r\n for (const event of events) {\r\n if (!hasHookRuntime) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no hook runtime (mcp-only) — ${event} never fires`,\r\n });\r\n continue;\r\n }\r\n if (!hostCanFireEvent(adapter.capabilities, event)) {\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: \"dropped\",\r\n reason: `${adapter.id} has no ${event} equivalent — install skip-warns it; never fires`,\r\n });\r\n continue;\r\n }\r\n const sim = await simulate(connector, {\r\n surface: \"hooks\",\r\n host: adapter.id,\r\n event,\r\n input: syntheticPayloadFor(event, connector.hooks[event]?.matcher),\r\n });\r\n rows.push({\r\n host: adapter.id,\r\n event,\r\n verdict: sim.honored ? \"honored\" : \"degraded\",\r\n reason: sim.reason,\r\n });\r\n }\r\n }\r\n\r\n rows.sort((a, b) =>\r\n a.host === b.host ? a.event.localeCompare(b.event) : a.host.localeCompare(b.host),\r\n );\r\n return rows;\r\n}\r\n"],"mappings":"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","names":["x","y","perHost","reply"]}
@@ -40,4 +40,4 @@ export {
40
40
  idKey,
41
41
  LineBuffer
42
42
  };
43
- //# sourceMappingURL=chunk-OP4KBXUO.js.map
43
+ //# sourceMappingURL=chunk-LFWYYWLV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/telemetry/jsonrpc.ts"],"sourcesContent":["/**\r\n * telemetry/jsonrpc — shared MCP stdio wire primitives.\r\n *\r\n * The MCP stdio transport is NEWLINE-DELIMITED JSON-RPC: one single-line JSON\r\n * object per message, terminated by `\\n` (no Content-Length framing). These\r\n * primitives — the message shape, the id key, the line buffer — are used by BOTH\r\n * the telemetry proxy (which tees + measures) and `doctor --probe` (which sends\r\n * a real initialize/ping/tools-list handshake), so framing + id-correlation are\r\n * byte-identical across the two paths. Extracted here rather than duplicated.\r\n */\r\n\r\nimport { StringDecoder } from \"node:string_decoder\";\r\n\r\n/** Any newline-delimited JSON-RPC message — request, response, or notification. */\r\nexport interface JsonRpcMessage {\r\n jsonrpc?: unknown;\r\n id?: unknown;\r\n method?: unknown;\r\n params?: unknown;\r\n result?: unknown;\r\n error?: unknown;\r\n}\r\n\r\nexport function isObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === \"object\" && v !== null;\r\n}\r\n\r\n/** JSON-RPC ids are number | string; normalize to a Map key. `null` → no key. */\r\nexport function idKey(id: unknown): string | null {\r\n if (typeof id === \"string\") return `s:${id}`;\r\n if (typeof id === \"number\") return `n:${id}`;\r\n return null;\r\n}\r\n\r\n/**\r\n * Buffer a byte stream into complete `\\n`-terminated lines and invoke `onLine`\r\n * for each. Carriage returns are tolerated (some hosts emit CRLF). This is a\r\n * pure side-channel: it never owns or mutates the bytes being forwarded — the\r\n * caller has already written the original chunk downstream verbatim.\r\n */\r\nexport class LineBuffer {\r\n private buf = \"\";\r\n private readonly onLine: (line: string) => void;\r\n // Incremental UTF-8 decoder: `data` events split on arbitrary byte\r\n // boundaries, so a multi-byte sequence (emoji/CJK in a tool arg or result)\r\n // can straddle two chunks. StringDecoder holds back a trailing partial\r\n // sequence until it completes, keeping the measured copy byte-accurate.\r\n // (The forwarded stream is untouched — this is only the tee copy.)\r\n private readonly decoder = new StringDecoder(\"utf8\");\r\n\r\n constructor(onLine: (line: string) => void) {\r\n this.onLine = onLine;\r\n }\r\n\r\n push(chunk: Buffer): void {\r\n this.buf += this.decoder.write(chunk);\r\n let nl = this.buf.indexOf(\"\\n\");\r\n while (nl !== -1) {\r\n // Slice off one line (without the newline), tolerate a trailing CR.\r\n let line = this.buf.slice(0, nl);\r\n if (line.endsWith(\"\\r\")) line = line.slice(0, -1);\r\n if (line.length > 0) this.onLine(line);\r\n this.buf = this.buf.slice(nl + 1);\r\n nl = this.buf.indexOf(\"\\n\");\r\n }\r\n }\r\n}\r\n"],"mappings":";;;AAWA,SAAS,qBAAqB;AAYvB,SAAS,SAAS,GAA0C;AACjE,SAAO,OAAO,MAAM,YAAY,MAAM;AACxC;AAGO,SAAS,MAAM,IAA4B;AAChD,MAAI,OAAO,OAAO,SAAU,QAAO,KAAK,EAAE;AAC1C,MAAI,OAAO,OAAO,SAAU,QAAO,KAAK,EAAE;AAC1C,SAAO;AACT;AAQO,IAAM,aAAN,MAAiB;AAAA,EACd,MAAM;AAAA,EACG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,IAAI,cAAc,MAAM;AAAA,EAEnD,YAAY,QAAgC;AAC1C,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,KAAK,OAAqB;AACxB,SAAK,OAAO,KAAK,QAAQ,MAAM,KAAK;AACpC,QAAI,KAAK,KAAK,IAAI,QAAQ,IAAI;AAC9B,WAAO,OAAO,IAAI;AAEhB,UAAI,OAAO,KAAK,IAAI,MAAM,GAAG,EAAE;AAC/B,UAAI,KAAK,SAAS,IAAI,EAAG,QAAO,KAAK,MAAM,GAAG,EAAE;AAChD,UAAI,KAAK,SAAS,EAAG,MAAK,OAAO,IAAI;AACrC,WAAK,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAChC,WAAK,KAAK,IAAI,QAAQ,IAAI;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  CURSOR_EVENT_MAP,
4
4
  CURSOR_HOOKS_VERSION
5
- } from "./chunk-6WZODR3Z.js";
5
+ } from "./chunk-XVEJAFKW.js";
6
6
  import {
7
7
  writeTomlString
8
- } from "./chunk-OPXAP3AC.js";
8
+ } from "./chunk-XN7S44U4.js";
9
9
  import {
10
10
  MCPB_MANIFEST_VERSION,
11
11
  MCP_SERVER_SCHEMA_URL,
@@ -14,22 +14,22 @@ import {
14
14
  isConcreteSemver,
15
15
  isPlaceholderVersion,
16
16
  registryTransportType
17
- } from "./chunk-5IOTP7LQ.js";
17
+ } from "./chunk-7ZFK2WEO.js";
18
18
  import {
19
19
  renderCommandMd,
20
20
  renderSkillMd,
21
21
  renderSubagentMd
22
- } from "./chunk-MGTDQLJJ.js";
22
+ } from "./chunk-3FINQ2ZF.js";
23
23
  import {
24
24
  buildHomeBinHookCommand,
25
25
  buildServeWrapperCommand,
26
26
  shouldWrapForTelemetry
27
- } from "./chunk-4NHXLXSD.js";
27
+ } from "./chunk-Y3K6KJMS.js";
28
28
  import {
29
29
  ensureDir,
30
30
  firstSymlinkInPath,
31
31
  homeBinPath
32
- } from "./chunk-GZGLCCEE.js";
32
+ } from "./chunk-QYOUQCC6.js";
33
33
 
34
34
  // src/core/package.ts
35
35
  import { existsSync as existsSync2, readFileSync } from "fs";
@@ -1112,4 +1112,4 @@ export {
1112
1112
  packageConnectorAll,
1113
1113
  installInstructions
1114
1114
  };
1115
- //# sourceMappingURL=chunk-APZDTY76.js.map
1115
+ //# sourceMappingURL=chunk-LQ55E7QI.js.map