@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/antigravity/index.ts"],"sourcesContent":["/**\r\n * adapters/antigravity — Google Antigravity (IDE) platform adapter.\r\n *\r\n * Antigravity is a Gemini-family host that, as of the 2.0 line, exposes a real\r\n * lifecycle-hook system on top of MCP — so this adapter is `json-stdio` (not the\r\n * old `mcp-only` framing): the host pipes a JSON payload to a hook command on\r\n * stdin and reads a JSON control object back, the same paradigm as Claude Code /\r\n * Gemini CLI. It installs the MCP server, lifecycle hooks, slash-command\r\n * Workflows, and Agent Skills.\r\n *\r\n * CONFIRMED-BY-INSTALL (2026-06-03, docs/research/antigravity-paths-confirmed.md):\r\n * a real machine with Antigravity IDE + `agy` CLI v1.0.0 shows the canonical user\r\n * customization root is `~/.gemini/antigravity/` — `~/.gemini/antigravity/mcp_config.json`\r\n * EXISTS and `~/.gemini/config/mcp_config.json` does NOT. So the fresh-install\r\n * default user MCP path is `~/.gemini/antigravity/mcp_config.json` (candidate[0]);\r\n * the `config/` and `antigravity-cli/` paths remain probed fallbacks only (honored\r\n * when a pre-existing config is found there, never the fresh default).\r\n *\r\n * STILL MEDIUM-CONFIDENCE / PATH-PROBING: hooks.json and the global skills dir were\r\n * NOT present on the observed install, so those locations stay corroborated-but-not-\r\n * byte-verified. Every user-scope path is PROBED at runtime — prefer a candidate\r\n * that already exists on disk, else fall back to the canonical path — and the doctor\r\n * flags each probed path with \"verify for your Antigravity version.\" We NEVER\r\n * hard-code a single guessed path, and any unsupported event or surface warn-skips\r\n * (never throws) at install time.\r\n *\r\n * Native config formats:\r\n * - MCP config (JSONC; we WRITE plain JSON), root key \"mcpServers\". stdio is\r\n * `{ command, args, env }`; a remote server is `{ serverUrl, headers }`\r\n * (NOTE: the key is `serverUrl`, NOT `url` and NOT Gemini CLI's `httpUrl`).\r\n * user scope → prefer existing of:\r\n * ~/.gemini/antigravity/mcp_config.json (CONFIRMED canonical)\r\n * ~/.gemini/config/mcp_config.json (probed fallback)\r\n * ~/.gemini/antigravity-cli/mcp_config.json (probed fallback)\r\n * else default to candidate[0] (antigravity/).\r\n * project scope → <projectDir>/.agents/mcp_config.json\r\n * - Hooks: a SEPARATE hooks.json in the customization dir (NOT the mcp_config),\r\n * project → <projectDir>/.agents/hooks.json\r\n * user → <resolvedUserConfigDir>/hooks.json\r\n * Shape: { hooks: { <Event>: [ { matcher, hooks:[{ type:\"command\", command }] } ] } }.\r\n * Supported events: PreToolUse, PostToolUse, SessionStart, Stop. All other\r\n * normalized events warn-skip. I/O fields are camelCase (see parse/format).\r\n * - Commands = \"Workflows\": markdown `.md` (body = the prompt; an optional\r\n * leading description line). Project prefer existing <proj>/.agent/workflows\r\n * or <proj>/.agents/workflows (default .agent/workflows per launch docs);\r\n * user ~/.gemini/antigravity/global_workflows.\r\n * - Skills = Agent Skills (uniform SKILL.md). Project\r\n * <proj>/.agents/skills/<name>/SKILL.md; user PROBE\r\n * ~/.gemini/antigravity-cli/skills then ~/.gemini/skills (NEVER\r\n * ~/.gemini/antigravity/skills — reportedly broken). NOTE: the global skills\r\n * dir was NOT present on the confirmed install, so it stays medium-confidence\r\n * + doctor \"verify for your version.\"\r\n * - Subagents: declarative subagents exist only inside a plugin bundle, which\r\n * agent-connector does not emit; supportsSubagents is false and the\r\n * BaseAdapter skip/warn default applies.\r\n *\r\n * Env handling: Antigravity (Gemini family) documents no `${env:VAR}` token of\r\n * the framework's syntax, so env/header/url refs are resolved to literals at\r\n * install time via resolveEnvRefsDeep — the safe default matching the Gemini CLI\r\n * adapter.\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { dirname, join } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n CommandDef,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionStartEvent,\r\n StopEvent,\r\n Transport,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n buildHomeBinHookCommand,\r\n buildWrappedStdio,\r\n buildUsageEventCommand,\r\n isHomeBinHookCommand,\r\n isHostNativeUsageEnabled,\r\n isUsageEventCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"antigravity\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Known user-scope MCP config candidates, in preference order. We pick the first\r\n * that already exists on disk; otherwise we default to candidate[0]\r\n * (~/.gemini/antigravity/mcp_config.json — CONFIRMED canonical on a real install:\r\n * docs/research/antigravity-paths-confirmed.md). The `config/` and CLI-only\r\n * `antigravity-cli/` paths are kept ONLY as probed fallbacks so a pre-existing\r\n * config in either is still honored, but neither is ever the fresh-install default.\r\n */\r\nconst USER_CONFIG_CANDIDATES = [\r\n [\".gemini\", \"antigravity\", \"mcp_config.json\"],\r\n [\".gemini\", \"config\", \"mcp_config.json\"],\r\n [\".gemini\", \"antigravity-cli\", \"mcp_config.json\"],\r\n] as const;\r\n\r\n/**\r\n * Antigravity's supported lifecycle hook events. These happen to share the\r\n * normalized PascalCase names 1:1; everything else has no Antigravity equivalent\r\n * and is reported as a warn/skip at install time.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"SessionStart\",\r\n \"Stop\",\r\n]);\r\n\r\n/**\r\n * Native hooks.json key for the OPT-IN host-native turn-usage hook (enricher 4a).\r\n * Antigravity shares the Gemini-family `AfterModel` model-turn hook, whose payload\r\n * carries `usageMetadata`. This is a host-native-only sink (no connector handler);\r\n * it is installed ONLY when host-native capture is enabled and records a DISTINCT\r\n * `model_turn` telemetry row that is never summed with the per-MCP `serve` rows.\r\n */\r\nconst USAGE_HOOK_EVENT = \"AfterModel\";\r\n\r\n/** A single Antigravity native hook registration entry (nested, Claude-shaped). */\r\ninterface AntigravityHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of Antigravity's hooks.json (only the parts we touch). */\r\ninterface AntigravityHooksFile {\r\n hooks?: Record<string, AntigravityHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Native MCP server entry shapes Antigravity accepts under `mcpServers`.\r\n * A stdio server is `{ command, args, env }`; a remote server is\r\n * `{ serverUrl, headers }` — the key is `serverUrl` (NOT `url`).\r\n */\r\ninterface AntigravityStdioServer {\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n}\r\ninterface AntigravityHttpServer {\r\n serverUrl: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/**\r\n * Raw Antigravity hook stdin payload (camelCase wire fields).\r\n *\r\n * SOURCE-VERIFIED against Google's official Antigravity hooks docs\r\n * (antigravity.google/docs/hooks) and corroborated by production integrations\r\n * (Cisco defenseclaw, camjac251/tool-gates, ntd4996/agentpet): Antigravity does\r\n * NOT use the flat Claude-Code shape. It carries no `hook_event_name`; the tool\r\n * descriptor is NESTED under `toolCall` (`toolCall.name` / `toolCall.args`); the\r\n * stable session id is `conversationId`; and the workspace roots are a string\r\n * array `workspacePaths` (first entry is the project dir). PostToolUse sends NO\r\n * tool fields — only `stepIdx`, an `error` string (empty/absent on success), and\r\n * the common fields.\r\n */\r\ninterface AntigravityToolCall {\r\n name?: string;\r\n args?: Record<string, unknown>;\r\n}\r\ninterface AntigravityWireInput {\r\n connector?: unknown;\r\n /** PreToolUse: nested tool descriptor (`toolCall.name`, `toolCall.args`). */\r\n toolCall?: AntigravityToolCall;\r\n toolOutput?: string;\r\n /** PostToolUse: runtime error message — empty/absent on success. */\r\n error?: string;\r\n /** Stable session id (the active agent conversation UUID). */\r\n conversationId?: string;\r\n /** Mounted workspace roots; the first entry is the project dir. */\r\n workspacePaths?: string[];\r\n source?: string;\r\n reason?: string;\r\n stopHookActive?: boolean;\r\n}\r\n\r\nexport class AntigravityAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name: string = \"Google Antigravity\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\r\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\r\n supportsMemory: true,\r\n // Antigravity 2.0 fires PreToolUse / PostToolUse / SessionStart / Stop.\r\n preToolUse: true,\r\n postToolUse: true,\r\n sessionStart: true,\r\n stop: true,\r\n // No documented equivalents — kept false; the corresponding normalized\r\n // events warn-skip at install time.\r\n preCompact: false,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n notification: false,\r\n // Newer events: no documented permission-dialog, tool-failure, or subagent\r\n // lifecycle hooks on Antigravity (IDE or `agy` CLI — the CLI adapter\r\n // inherits this surface), so permissionRequest / postToolUseFailure /\r\n // subagentStart / subagentStop stay unset — install reports the standard\r\n // skip-warn for them.\r\n // Transform-category hooks can rewrite tool input and tool output; the\r\n // SessionStart hook can inject additional context.\r\n canModifyArgs: true,\r\n canModifyOutput: true,\r\n canInjectSessionContext: true,\r\n // Antigravity registers stdio, SSE, and Streamable HTTP MCP servers.\r\n transports: [\"stdio\", \"sse\", \"http\"],\r\n // Content surfaces: Workflows (markdown commands) + Agent Skills (SKILL.md).\r\n // Declarative subagents exist only inside a plugin bundle (not emitted), so\r\n // subagents are unsupported and inherit the BaseAdapter skip/warn default.\r\n supportsCommands: true,\r\n supportsSkills: true,\r\n supportsSubagents: false,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userConfig = this.resolveUserConfigPath();\r\n const userDir = join(homedir(), \".gemini\");\r\n const projectConfig = join(projectDir, \".agents\", \"mcp_config.json\");\r\n\r\n const userInstalled =\r\n existsSync(userDir) ||\r\n this.userConfigCandidates().some((parts) => existsSync(join(homedir(), ...parts)));\r\n const projInstalled = existsSync(projectConfig);\r\n const installed = userInstalled || projInstalled;\r\n\r\n // Report the scope/path that actually matched, so a project-only install\r\n // isn't misreported as a (non-existent) user install.\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projectConfig : userConfig;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? scope === \"project\"\r\n ? `found project Antigravity config at ${projectConfig}`\r\n : `found Antigravity config under ${userDir}`\r\n : `no Antigravity config at ${userDir} or ${projectConfig}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n /**\r\n * The lifecycle hook events this host actually writes; every other normalized\r\n * event warn-skips at install time. Defaults to the IDE's\r\n * {@link SUPPORTED_EVENTS}. Overridable by forks whose underlying binary\r\n * recognizes a DIFFERENT event set (e.g. the `agy` CLI, which does NOT\r\n * recognize SessionStart). Kept in lock-step with the per-event capability\r\n * flags so the docs hooks-matrix and the fleet-wide never-silent-drop contract\r\n * stay consistent.\r\n */\r\n protected supportedHookEvents(): ReadonlySet<HookEventName> {\r\n return SUPPORTED_EVENTS;\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n if (ctx.scope === \"project\") return join(ctx.projectDir, \".agents\");\r\n // User scope: parent dir of the resolved user mcp_config.json.\r\n return dirname(this.resolveUserConfigPath());\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".agents\", \"mcp_config.json\")\r\n : this.resolveUserConfigPath();\r\n }\r\n\r\n /**\r\n * Hooks live in a SEPARATE hooks.json in the customization dir — NOT the\r\n * mcp_config.json. Project → <proj>/.agents/hooks.json; user →\r\n * <resolvedUserConfigDir>/hooks.json (the dir holding the resolved user MCP\r\n * config). Path is medium-confidence and surfaced by the doctor.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"hooks.json\");\r\n }\r\n\r\n /**\r\n * The user-scope MCP config candidates, in preference order. Overridable by\r\n * forks (e.g. the Antigravity CLI) that resolve a different user-scope root\r\n * while reusing resolveUserConfigPath's prefer-existing-else-candidate[0] logic.\r\n */\r\n protected userConfigCandidates(): ReadonlyArray<readonly string[]> {\r\n return USER_CONFIG_CANDIDATES;\r\n }\r\n\r\n /**\r\n * Pick the user-scope mcp_config.json: prefer a candidate that already exists,\r\n * else default to candidate[0] (the CONFIRMED canonical ~/.gemini/antigravity path).\r\n */\r\n protected resolveUserConfigPath(): string {\r\n const home = homedir();\r\n const candidates = this.userConfigCandidates();\r\n for (const parts of candidates) {\r\n const p = join(home, ...parts);\r\n if (existsSync(p)) return p;\r\n }\r\n const fallback = candidates[0];\r\n if (fallback === undefined) {\r\n // Defensive: a non-empty candidate list is an invariant, but never throw.\r\n return join(home, \".gemini\", \"antigravity\", \"mcp_config.json\");\r\n }\r\n return join(home, ...fallback);\r\n }\r\n\r\n /**\r\n * Resolve the Workflows (slash-command) dir, prefer-existing-else-canonical.\r\n * project → prefer existing <proj>/.agent/workflows or <proj>/.agents/workflows;\r\n * default .agent/workflows (launch-era singular `.agent`).\r\n * user → ~/.gemini/antigravity/global_workflows.\r\n */\r\n protected resolveWorkflowsDir(ctx: InstallContext): string {\r\n if (ctx.scope === \"project\") {\r\n const singular = join(ctx.projectDir, \".agent\", \"workflows\");\r\n const plural = join(ctx.projectDir, \".agents\", \"workflows\");\r\n if (existsSync(singular)) return singular;\r\n if (existsSync(plural)) return plural;\r\n return singular;\r\n }\r\n return join(homedir(), \".gemini\", \"antigravity\", \"global_workflows\");\r\n }\r\n\r\n /**\r\n * Resolve the Agent Skills dir, prefer-existing-else-canonical.\r\n * project → <proj>/.agents/skills (plural — matches mcp/skills convention).\r\n * user → prefer existing ~/.gemini/antigravity-cli/skills, else\r\n * ~/.gemini/skills. NEVER ~/.gemini/antigravity/skills\r\n * (reportedly broken), so it is not a candidate.\r\n */\r\n protected resolveSkillsDir(ctx: InstallContext): string {\r\n if (ctx.scope === \"project\") {\r\n return join(ctx.projectDir, \".agents\", \"skills\");\r\n }\r\n const home = homedir();\r\n const cliSkills = join(home, \".gemini\", \"antigravity-cli\", \"skills\");\r\n const sharedSkills = join(home, \".gemini\", \"skills\");\r\n if (existsSync(cliSkills)) return cliSkills;\r\n if (existsSync(sharedSkills)) return sharedSkills;\r\n return cliSkills;\r\n }\r\n\r\n // ── MCP server install / uninstall ───────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[this.id]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? `server registration disabled for ${this.id}`\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /** Render a normalized ServerDef into Antigravity's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): AntigravityStdioServer | AntigravityHttpServer {\r\n const transport: Transport = server.transport;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> --scope <scope> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // Antigravity (Gemini family) has no documented native interpolation token,\r\n // so resolve every ${env:VAR} to a literal at install time.\r\n const entry: AntigravityStdioServer = { command: resolveEnvRefsDeep(command) };\r\n if (args.length > 0) entry.args = resolveEnvRefsDeep(args);\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // sse / http (and any other remote transport) — Antigravity registers a URL\r\n // under the `serverUrl` key (NOT `url`).\r\n const entry: AntigravityHttpServer = { serverUrl: resolveEnvRefsDeep(server.url ?? \"\") };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Antigravity documents no native interpolation\r\n * token, so resolve `${env:VAR}` references to literals at install time.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n // ── Hook install / uninstall ─────────────────────────────────────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[this.id]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `hooks disabled for ${this.id}` }];\r\n }\r\n // The opt-in host-native usage hook (4a) needs no connector handler, so it may\r\n // be installed even when the connector declares no normalized hook events.\r\n const hostNative = isHostNativeUsageEnabled(connector.telemetry);\r\n if (connector.hookEvents.length === 0 && !hostNative) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no hooks\" }];\r\n }\r\n\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n // OVERWRITE GUARD: never clobber a present-but-unparseable hooks.json.\r\n if (this.isPresentButUnparseable(hooksPath)) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: hooksPath,\r\n detail: `existing ${hooksPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\r\n },\r\n ];\r\n }\r\n\r\n const file = this.readJson<AntigravityHooksFile>(hooksPath) ?? {};\r\n const __skip = this.malformedHookRootSkip(hooksPath, (file as Record<string, unknown>).hooks);\r\n if (__skip) return [__skip];\r\n const hooks = (file.hooks ??= {});\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n const supportedEvents = this.supportedHookEvents();\r\n for (const event of connector.hookEvents) {\r\n if (!supportedEvents.has(event)) {\r\n // No Antigravity equivalent for this normalized event — report.\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: hooksPath,\r\n detail: `${event} has no Antigravity hook equivalent — skipped`,\r\n });\r\n continue;\r\n }\r\n\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, this.id, event, connector.id);\r\n const matcher = connector.hooks[event]?.matcher ?? \"\";\r\n const entry: AntigravityHookEntry = {\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n\r\n const bucket = (hooks[event] ??= []);\r\n const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: `hooks.${event} already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hooksPath,\r\n detail: `hooks.${event}`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hooksPath,\r\n detail: `hooks.${event}`,\r\n });\r\n }\r\n mutated = true;\r\n }\r\n\r\n // OPT-IN host-native usage hook (4a): register the AfterModel `usage-event`\r\n // sink when enabled. Records a DISTINCT `model_turn` row (whole-conversation),\r\n // never summed with the per-MCP `serve`-proxy rows.\r\n if (hostNative) {\r\n if (this.installUsageHook(hooks, ctx, hooksPath, changes)) mutated = true;\r\n }\r\n\r\n if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);\r\n return changes;\r\n }\r\n\r\n /**\r\n * Register the opt-in AfterModel host-native usage hook (4a) into `hooks`.\r\n * Idempotent (byte-identical → skip, drift → update); returns true on mutation.\r\n * The command is the hidden `usage-event` entrypoint (NOT a connector hook), so\r\n * it carries an empty matcher. Reads `this.id` so the CLI fork installs its own\r\n * `usage-event antigravity-cli …` variant.\r\n */\r\n private installUsageHook(\r\n hooks: Record<string, AntigravityHookEntry[]>,\r\n ctx: InstallContext,\r\n hooksPath: string,\r\n changes: ChangeRecord[],\r\n ): boolean {\r\n const command = buildUsageEventCommand(ctx.homeBinPath, this.id, ctx.connector.id);\r\n const entry: AntigravityHookEntry = {\r\n matcher: \"\",\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n const bucket = (hooks[USAGE_HOOK_EVENT] ??= []);\r\n const existingIdx = bucket.findIndex((e) =>\r\n (e.hooks ?? []).some((h) => isUsageEventCommand(h.command, ctx.homeBinPath, ctx.connector.id)),\r\n );\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage) already registered`,\r\n });\r\n return false;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hooksPath,\r\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage)`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hooksPath,\r\n detail: `hooks.${USAGE_HOOK_EVENT} (host-native usage)`,\r\n });\r\n }\r\n return true;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const file = this.readJson<AntigravityHooksFile>(hooksPath);\r\n const hooks = file?.hooks;\r\n if (!file || !hooks) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: \"no hooks section present\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of Object.keys(hooks)) {\r\n const bucket = hooks[event];\r\n if (!Array.isArray(bucket)) continue;\r\n\r\n // Strip our hook command from each entry; drop entries left empty so we\r\n // never remove another connector's (or the user's own) hook commands.\r\n const next: AntigravityHookEntry[] = [];\r\n let removed = 0;\r\n for (const e of bucket) {\r\n const innerBefore = e.hooks?.length ?? 0;\r\n const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));\r\n removed += innerBefore - inner.length;\r\n if (inner.length > 0) next.push({ matcher: e.matcher ?? \"\", hooks: inner });\r\n }\r\n\r\n if (removed > 0) {\r\n if (next.length > 0) hooks[event] = next;\r\n else delete hooks[event];\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path: hooksPath,\r\n detail: `hooks.${event} (${removed})`,\r\n });\r\n mutated = true;\r\n }\r\n }\r\n\r\n if (mutated) this.writeJson(hooksPath, file, ctx.dryRun);\r\n if (changes.length === 0) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: \"no matching hook entries\",\r\n });\r\n }\r\n return changes;\r\n }\r\n\r\n private entryHasOurCommand(entry: AntigravityHookEntry, ctx: InstallContext): boolean {\r\n return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));\r\n }\r\n\r\n /**\r\n * True when a hook command is ONE OF OURS for this connector — the universal\r\n * `hook` dispatcher OR the opt-in `usage-event` sink. Both are anchored on the\r\n * connector id (see isHomeBinHookCommand) so a shared-prefix id can't collide,\r\n * and so uninstall reverses the AfterModel usage hook too.\r\n */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return (\r\n isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id) ||\r\n isUsageEventCommand(command, ctx.homeBinPath, ctx.connector.id)\r\n );\r\n }\r\n\r\n // ── Content surfaces: commands (Workflows) / skills ───────────────────────\r\n // CONTENT-ONLY: pure native-file writers under the probed workflows / skills\r\n // dirs. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each\r\n // method is idempotent (byte-identical → skip) via BaseAdapter.writeContentFile\r\n // and reversible via removeContentFile. Honors platforms[\"antigravity\"]\r\n // per-surface false to skip. Commands are markdown Workflows (NOT TOML, unlike\r\n // gemini-cli); skills are the uniform Claude-compatible SKILL.md. Subagents are\r\n // unsupported (plugin-bundle-only) and inherit the BaseAdapter skip/warn.\r\n\r\n /** Native Workflow file path: <workflowsDir>/<name>.md. */\r\n private commandPath(ctx: InstallContext, name: string): string {\r\n return join(this.resolveWorkflowsDir(ctx), `${name}.md`);\r\n }\r\n /** Native skill dir: <skillsDir>/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.resolveSkillsDir(ctx), name);\r\n }\r\n\r\n // ── Commands (Workflows) ──────────────────────────────────────────────────\r\n\r\n override installCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[this.id]?.commands === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `commands disabled for ${this.id}` }];\r\n }\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.writeContentFile(this.commandPath(ctx, cmd.name), this.renderCommand(cmd), ctx.dryRun),\r\n );\r\n }\r\n\r\n override uninstallCommands(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.commands.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no commands\" }];\r\n }\r\n return connector.commands.map((cmd) =>\r\n this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n /**\r\n * Render a command to an Antigravity Workflow: a plain markdown `.md` whose\r\n * body is the command prompt, optionally preceded by a leading description\r\n * line. No frontmatter / TOML (unlike gemini-cli).\r\n */\r\n private renderCommand(cmd: CommandDef): string {\r\n const body = cmd.prompt.endsWith(\"\\n\") ? cmd.prompt : `${cmd.prompt}\\n`;\r\n if (cmd.description !== undefined && cmd.description !== \"\") {\r\n return `${cmd.description}\\n\\n${body}`;\r\n }\r\n return body;\r\n }\r\n\r\n // ── Skills ────────────────────────────────────────────────────────────────\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[this.id]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir.\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const mcpPath = this.getServerConfigPath(ctx);\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n const workflowsDir = this.resolveWorkflowsDir(ctx);\r\n const skillsDir = this.resolveSkillsDir(ctx);\r\n\r\n const checks: HealthCheck[] = [\r\n {\r\n name: `${this.name}: mcp_config.json present`,\r\n check: () =>\r\n existsSync(mcpPath)\r\n ? { status: \"OK\", detail: `${mcpPath} (path probed; verify for your Antigravity version)` }\r\n : { status: \"FAIL\", detail: `not found: ${mcpPath} (path probed; verify for your Antigravity version)` },\r\n },\r\n {\r\n name: `${this.name}: server entry registered`,\r\n check: () => {\r\n if (!ctx.connector.server) {\r\n return { status: \"OK\", detail: \"no MCP server declared\" };\r\n }\r\n const cfg = this.readJson<{ [k: string]: Record<string, unknown> }>(mcpPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !bucket) {\r\n return { status: \"FAIL\", detail: `no ${MCP_ROOT_KEY} in ${mcpPath}` };\r\n }\r\n return connectorId in bucket\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}.${connectorId} present` }\r\n : {\r\n status: \"FAIL\",\r\n detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${mcpPath}`,\r\n };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hooks.json hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<AntigravityHooksFile>(hooksPath);\r\n if (!file) {\r\n return {\r\n status: \"FAIL\",\r\n detail: `not found: ${hooksPath} (path probed; verify for your Antigravity version)`,\r\n };\r\n }\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: `hook command present in ${hooksPath}` }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hooksPath}` };\r\n },\r\n },\r\n ];\r\n\r\n // Content-surface checks: only assert presence of the files this connector\r\n // declares (skip silently for surfaces it never asked for). The probed dirs\r\n // are noted so doctor output makes the medium-confidence path explicit.\r\n for (const cmd of ctx.connector.commands) {\r\n const p = join(workflowsDir, `${cmd.name}.md`);\r\n checks.push({\r\n name: `${this.name}: workflow ${cmd.name} present`,\r\n check: () =>\r\n existsSync(p)\r\n ? { status: \"OK\", detail: `${p} (workflows dir probed; verify for your version)` }\r\n : { status: \"FAIL\", detail: `not found: ${p} (workflows dir probed; verify for your version)` },\r\n });\r\n }\r\n for (const skill of ctx.connector.skills) {\r\n const p = join(skillsDir, skill.name, \"SKILL.md\");\r\n checks.push({\r\n name: `${this.name}: skill ${skill.name} present`,\r\n check: () =>\r\n existsSync(p)\r\n ? { status: \"OK\", detail: `${p} (skills dir probed; verify for your version)` }\r\n : { status: \"FAIL\", detail: `not found: ${p} (skills dir probed; verify for your version)` },\r\n });\r\n }\r\n return checks;\r\n }\r\n\r\n // ── Runtime: parse Antigravity stdin JSON → normalized event ──────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as AntigravityWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n // Stable session id is `conversationId` (NOT a flat `sessionId`).\r\n const sessionId = typeof input.conversationId === \"string\" ? input.conversationId : \"\";\r\n // Project dir is the first mounted workspace root (`workspacePaths[0]`), a\r\n // string array — NOT a flat `cwd` string.\r\n const projectDir =\r\n Array.isArray(input.workspacePaths) && typeof input.workspacePaths[0] === \"string\"\r\n ? input.workspacePaths[0]\r\n : undefined;\r\n\r\n const base = {\r\n hostPlatform: this.id,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n // Tool descriptor is nested under `toolCall` (`.name` / `.args`).\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.toolCall?.name ?? \"\",\r\n toolInput: input.toolCall?.args ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n // Antigravity's PostToolUse carries no tool fields — only `stepIdx`,\r\n // an `error` string, and the common fields. Derive isError from a\r\n // non-empty error string and surface it as the failure output.\r\n const error = typeof input.error === \"string\" ? input.error : \"\";\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: \"\",\r\n toolInput: {},\r\n ...(error.length > 0\r\n ? { isError: true, toolOutput: error }\r\n : input.toolOutput !== undefined\r\n ? { toolOutput: input.toolOutput }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n const ev: StopEvent = {\r\n ...base,\r\n ...(typeof input.stopHookActive === \"boolean\"\r\n ? { stopHookActive: input.stopHookActive }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n default: {\r\n // Antigravity only delivers the four SUPPORTED_EVENTS. If the runtime\r\n // dispatches anything else, surface it loudly rather than mis-parse.\r\n throw new Error(`unsupported ${this.id} hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Antigravity native hook reply ──────────\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // deny → Antigravity blocks the action via a top-level `decision:\"deny\"` +\r\n // reason (the Decide category).\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // ask → Antigravity has no native \"ask\"; degrade to deny to stay fail-safe.\r\n if (decision === \"ask\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Action requires user confirmation (security policy)\",\r\n });\r\n }\r\n\r\n // modify → Transform category. camelCase: rewrite PreToolUse input via\r\n // updatedInput; rewrite PostToolUse output via updatedOutput.\r\n if (decision === \"modify\") {\r\n if (event === \"PreToolUse\" && response.updatedInput) {\r\n return this.stdout({ updatedInput: response.updatedInput });\r\n }\r\n if (event === \"PostToolUse\" && response.updatedOutput !== undefined) {\r\n return this.stdout({ updatedOutput: response.updatedOutput });\r\n }\r\n // Nothing to apply; fall through to allow.\r\n }\r\n\r\n // context → inject soft guidance via additionalContext (the SessionStart\r\n // context-injection path).\r\n if (decision === \"context\" && response.additionalContext) {\r\n return this.stdout({ additionalContext: response.additionalContext });\r\n }\r\n\r\n // allow / void / unsupported-degradation → pass through with exit 0.\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\nexport const adapter = new AntigravityAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -30,4 +30,4 @@ export {
30
30
  localAppData,
31
31
  codexConfigHome
32
32
  };
33
- //# sourceMappingURL=chunk-UM5S7DKK.js.map
33
+ //# sourceMappingURL=chunk-C747DYNT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/host-paths.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────\r\n// host-paths.ts — shared OS user-config-dir base resolvers\r\n//\r\n// Several adapters resolve the SAME per-OS \"user config base\" directory with\r\n// the same logic (XDG on posix; %APPDATA%/%LOCALAPPDATA% on Windows, each with\r\n// a ~/AppData fallback). These helpers consolidate that resolution so the\r\n// per-host adapters only carry their host-specific SUFFIX. The result is\r\n// byte-identical for absolute/clean config-dir inputs (the realistic case);\r\n// a non-canonical env value (relative, trailing slash, `..`) is normalized via\r\n// `resolve()` rather than passed through bare — a safe canonicalization.\r\n//\r\n// Scope note: these are the USER-CONFIG-DIR bases (where a host keeps its\r\n// settings/config files). The agent-connector *data-root* paths (our own\r\n// per-project state) live in core/paths.ts — do NOT conflate the two.\r\n//\r\n// Host-specific config-home resolvers that are SHARED across modules (e.g.\r\n// `codexConfigHome`, used by BOTH the codex adapter and marketplace detection)\r\n// DO live here, so the writer and the probe can never disagree on the dir.\r\n// Single-caller host envs (PI_CODING_AGENT_DIR, OPENCLAW_*) stay in their own\r\n// adapter.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nimport { homedir } from \"node:os\";\r\nimport { join, resolve } from \"node:path\";\r\n\r\n/** $XDG_CONFIG_HOME when set & non-empty, else ~/.config. */\r\nexport function xdgConfigHome(): string {\r\n const xdg = process.env.XDG_CONFIG_HOME;\r\n return xdg && xdg.trim() !== \"\" ? resolve(xdg) : join(homedir(), \".config\");\r\n}\r\n\r\n/** %APPDATA% (Roaming) when set & non-empty, else ~/AppData/Roaming. */\r\nexport function roamingAppData(): string {\r\n const appData = process.env.APPDATA;\r\n return appData && appData.trim() !== \"\"\r\n ? resolve(appData)\r\n : join(homedir(), \"AppData\", \"Roaming\");\r\n}\r\n\r\n/** %LOCALAPPDATA% when set & non-empty, else ~/AppData/Local. */\r\nexport function localAppData(): string {\r\n const local = process.env.LOCALAPPDATA;\r\n return local && local.trim() !== \"\"\r\n ? resolve(local)\r\n : join(homedir(), \"AppData\", \"Local\");\r\n}\r\n\r\n/** Codex's config home: $CODEX_HOME (tilde-expanded, then resolved) when set\r\n * & non-empty, else ~/.codex. Shared by the codex adapter (writer) and the\r\n * marketplace detection probe so they never disagree on the config dir. */\r\nexport function codexConfigHome(): string {\r\n const env = process.env.CODEX_HOME;\r\n if (env && env.trim() !== \"\") {\r\n if (env.startsWith(\"~\")) return join(homedir(), env.replace(/^~[/\\\\]?/, \"\"));\r\n return resolve(env);\r\n }\r\n return join(homedir(), \".codex\");\r\n}\r\n"],"mappings":";;;AAsBA,SAAS,eAAe;AACxB,SAAS,MAAM,eAAe;AAGvB,SAAS,gBAAwB;AACtC,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO,OAAO,IAAI,KAAK,MAAM,KAAK,QAAQ,GAAG,IAAI,KAAK,QAAQ,GAAG,SAAS;AAC5E;AAGO,SAAS,iBAAyB;AACvC,QAAM,UAAU,QAAQ,IAAI;AAC5B,SAAO,WAAW,QAAQ,KAAK,MAAM,KACjC,QAAQ,OAAO,IACf,KAAK,QAAQ,GAAG,WAAW,SAAS;AAC1C;AAGO,SAAS,eAAuB;AACrC,QAAM,QAAQ,QAAQ,IAAI;AAC1B,SAAO,SAAS,MAAM,KAAK,MAAM,KAC7B,QAAQ,KAAK,IACb,KAAK,QAAQ,GAAG,WAAW,OAAO;AACxC;AAKO,SAAS,kBAA0B;AACxC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,OAAO,IAAI,KAAK,MAAM,IAAI;AAC5B,QAAI,IAAI,WAAW,GAAG,EAAG,QAAO,KAAK,QAAQ,GAAG,IAAI,QAAQ,YAAY,EAAE,CAAC;AAC3E,WAAO,QAAQ,GAAG;AAAA,EACpB;AACA,SAAO,KAAK,QAAQ,GAAG,QAAQ;AACjC;","names":[]}
@@ -3,44 +3,44 @@ import {
3
3
  LineBuffer,
4
4
  idKey,
5
5
  isObject
6
- } from "./chunk-OP4KBXUO.js";
6
+ } from "./chunk-LFWYYWLV.js";
7
7
  import {
8
8
  spawnChild
9
- } from "./chunk-Z2MRSS2I.js";
9
+ } from "./chunk-SLJI5BOM.js";
10
10
  import {
11
11
  detectRuntimeHost
12
- } from "./chunk-ISEJWBFF.js";
12
+ } from "./chunk-GZZCHJDP.js";
13
13
  import {
14
14
  attachLazyTelemetryUsage,
15
15
  buildTelemetryAccessor,
16
16
  measureToolCall,
17
17
  measureToolDefs
18
- } from "./chunk-I5UXHIDT.js";
19
- import {
20
- log
21
- } from "./chunk-KHO5WNTP.js";
18
+ } from "./chunk-GCPEPUD7.js";
22
19
  import {
23
20
  getTokenizer,
24
21
  inferModelFamily,
25
22
  newRecordId,
26
23
  openStore
27
- } from "./chunk-IVAWP6HH.js";
24
+ } from "./chunk-IOJDZJ72.js";
25
+ import {
26
+ log
27
+ } from "./chunk-4GH2KA7V.js";
28
28
  import {
29
29
  loadRegisteredConnector,
30
30
  readRegisteredMeta
31
- } from "./chunk-PW42ZXCB.js";
31
+ } from "./chunk-3EP5V2F4.js";
32
32
  import {
33
33
  REGISTERED_PLATFORM_IDS,
34
34
  loadAdapter
35
- } from "./chunk-D4ELPBB5.js";
35
+ } from "./chunk-NDMNMAKP.js";
36
36
  import {
37
37
  detectLaunchMethod
38
- } from "./chunk-4NHXLXSD.js";
38
+ } from "./chunk-Y3K6KJMS.js";
39
39
  import {
40
40
  dataRoot,
41
41
  ensureDir,
42
42
  projectIdentity
43
- } from "./chunk-GZGLCCEE.js";
43
+ } from "./chunk-QYOUQCC6.js";
44
44
 
45
45
  // src/runtime/serve.ts
46
46
  import { randomUUID } from "crypto";
@@ -706,4 +706,4 @@ export {
706
706
  runStatusline,
707
707
  runAction
708
708
  };
709
- //# sourceMappingURL=chunk-YPAXJ7EW.js.map
709
+ //# sourceMappingURL=chunk-FHYR5Q5T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/serve.ts","../src/telemetry/calibration.ts","../src/telemetry/proxy.ts","../src/runtime/usage-event.ts","../src/runtime/statusline-entrypoint.ts","../src/runtime/action-entrypoint.ts"],"sourcesContent":["/**\r\n * runtime/serve — the `agent-connector serve` telemetry-wrapping entrypoint.\r\n *\r\n * When a stdio MCP server opts into transparent telemetry, its host config is\r\n * rewritten (by the adapter, via core/spawn.buildServeWrapperCommand) to launch\r\n * <homeBin> serve --connector <id> -- <realCommand> <realArgs...>\r\n * instead of the server directly. The CLI parses those flags and calls\r\n * {@link runServe}, which stands up the per-session telemetry context (store,\r\n * tokenizer, project identity, host detection) and hands off to the proxy.\r\n *\r\n * The proxy (telemetry/proxy.runServeProxy) forwards bytes VERBATIM in both\r\n * directions and measures aggregate per-tool token counts out of band, so this\r\n * wrapper is transparent to both the host and the real server. It resolves with\r\n * the child server's exit code, which the CLI uses as its own.\r\n */\r\n\r\nimport { randomUUID } from \"node:crypto\";\r\n\r\nimport { detectRuntimeHost } from \"../adapters/detect.js\";\r\nimport { REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\r\nimport { readRegisteredMeta } from \"../core/load-connector.js\";\r\nimport { projectIdentity } from \"../core/paths.js\";\r\nimport { detectLaunchMethod } from \"../core/spawn.js\";\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport { runServeProxy } from \"../telemetry/proxy.js\";\r\nimport { openStore } from \"../telemetry/store.js\";\r\nimport { getTokenizer } from \"../telemetry/tokenizer.js\";\r\nimport type { TelemetryInstallScope } from \"../telemetry/types.js\";\r\n\r\n/** Flags the CLI hands to {@link runServe}. */\r\nexport interface RunServeOptions {\r\n /** Connector id from `--connector <id>`; selects telemetry config + stamps records. */\r\n connectorId: string;\r\n /** The real MCP server executable to spawn (everything after `--`). */\r\n serverCommand: string;\r\n /** Arguments passed to that executable. */\r\n serverArgs: string[];\r\n /**\r\n * Install scope from `--scope <user|project>`. OPTIONAL: absent for configs\r\n * written before scope plumbing existed — left unstamped so older rows read\r\n * as \"unknown\". Stamped verbatim onto every telemetry record when present.\r\n */\r\n installScope?: TelemetryInstallScope;\r\n /**\r\n * Install TARGET platform id from `--host <platformId>` (baked into the\r\n * wrapper at install time). OPTIONAL: when a recognized platform id, it\r\n * OVERRIDES runtime env detection so headless spawns stamp hostPlatform\r\n * correctly (detectRuntimeHost only knows env markers for a few hosts and\r\n * otherwise mis-attributes). Absent/unrecognized → fall back to detection.\r\n */\r\n hostPlatformOverride?: PlatformId;\r\n /**\r\n * Framework data-root from `--data-dir <path>` (baked into the wrapper at\r\n * install time, only for a NON-DEFAULT root). OPTIONAL: when present, it pins\r\n * the data-root the WHOLE child resolves — connector record lookup AND the\r\n * telemetry store — by populating AGENT_CONNECTOR_DATA_DIR before anything\r\n * reads it. This is the env-independent path for hosts (codex) that strip the\r\n * MCP child's environment and would otherwise drop the override, leaving the\r\n * child to resolve the default `~/.agent-connector` and fail \"not registered\".\r\n * Absent → resolve the data-root the usual way (env or default).\r\n */\r\n dataDir?: string;\r\n}\r\n\r\n/**\r\n * Launch the developer's real MCP server under the telemetry-wrapping proxy.\r\n *\r\n * Resolves with the child server's exit code (propagated as this process's exit\r\n * code by the CLI). The session id comes from AGENT_CONNECTOR_SESSION when the\r\n * host sets it (empty otherwise); the host platform is detected from runtime env\r\n * markers; the project identity partitions telemetry by the stable project key.\r\n */\r\n/**\r\n * Resolve a session id for telemetry attribution. Prefers a host-provided\r\n * session/thread marker (so records from the same host session group together);\r\n * falls back to a per-process UUID — each `serve` invocation is one server\r\n * session, a sensible default granularity — so `--by session` rollups are never\r\n * collapsed into a single empty-keyed group.\r\n */\r\nfunction resolveSessionId(env: NodeJS.ProcessEnv = process.env): string {\r\n return (\r\n env.AGENT_CONNECTOR_SESSION ||\r\n env.CLAUDE_SESSION_ID ||\r\n env.CODEX_THREAD_ID ||\r\n env.CURSOR_TRACE_ID ||\r\n env.MCP_SESSION_ID ||\r\n `proc-${randomUUID()}`\r\n );\r\n}\r\n\r\nexport async function runServe(opts: RunServeOptions): Promise<number> {\r\n const {\r\n connectorId,\r\n serverCommand,\r\n serverArgs,\r\n installScope,\r\n hostPlatformOverride,\r\n dataDir,\r\n } = opts;\r\n\r\n // Pin the data-root BEFORE anything resolves it (the connector record lookup\r\n // below and openStore later both read dataRoot()). The wrapper passes\r\n // --data-dir only for a non-default root on a host that may strip the child\r\n // env (codex) — populating AGENT_CONNECTOR_DATA_DIR here makes the whole child\r\n // resolve that root regardless of whether the host propagated the env var.\r\n if (dataDir !== undefined && dataDir.trim() !== \"\") {\r\n process.env.AGENT_CONNECTOR_DATA_DIR = dataDir;\r\n }\r\n\r\n const connector = readRegisteredMeta(connectorId);\r\n if (!connector) {\r\n throw new Error(\r\n `Connector \"${connectorId}\" is not registered. Run an install/register step first.`,\r\n );\r\n }\r\n\r\n const id = projectIdentity(process.cwd());\r\n // Prefer the install TARGET platform baked into the wrapper (--host), but only\r\n // when it is a KNOWN registered platform id — a bad/unknown value falls back to\r\n // runtime env detection so a config typo can never poison hostPlatform.\r\n const hostPlatform: PlatformId =\r\n hostPlatformOverride !== undefined &&\r\n REGISTERED_PLATFORM_IDS.has(hostPlatformOverride)\r\n ? hostPlatformOverride\r\n : detectRuntimeHost().platform;\r\n const sessionId = resolveSessionId();\r\n\r\n // `serve` only ever wraps a locally-spawned stdio server (a remote/http server\r\n // is never launched through the proxy), so isRemote is always false here.\r\n const launchMethod = detectLaunchMethod(serverCommand, serverArgs);\r\n\r\n const store = openStore({});\r\n const tok = getTokenizer();\r\n\r\n return runServeProxy({\r\n connectorId,\r\n command: serverCommand,\r\n args: serverArgs,\r\n store,\r\n tokenizer: tok,\r\n modelFamilyHint: connector.telemetry.modelFamilyHint,\r\n hostPlatform,\r\n sessionId,\r\n projectKey: id.key,\r\n projectDir: id.dir,\r\n measureToolDefs: connector.telemetry.measureToolDefs,\r\n installScope,\r\n launchMethod,\r\n });\r\n}\r\n","/**\r\n * telemetry/calibration — OPT-IN Anthropic count_tokens calibration.\r\n *\r\n * The default tokenizer counts every family with the OpenAI o200k_base BPE, so\r\n * an Anthropic session's counts are an honest-but-approximate stand-in\r\n * (\"tokenizer-approx\"). This module lets a developer OPT IN to calibrating that\r\n * approximation against Anthropic's real `count_tokens` endpoint: we sample a\r\n * tiny fraction of tool calls off-box, learn a per-family correction factor\r\n * (rolling mean of exact/approx), and apply it to the locally-tokenized counts\r\n * so they land much closer to the host's real numbers — labeled honestly as\r\n * \"tokenizer-calibrated\".\r\n *\r\n * PRIVACY + SAFETY — load-bearing invariants:\r\n * • OPT-IN only. Nothing here runs unless BOTH AGENT_CONNECTOR_CALIBRATE\r\n * contains \"anthropic\" AND ANTHROPIC_API_KEY is set (see isCalibrationEnabled).\r\n * • Sampling only — shouldSample() rate-limits hard so we never hammer the API.\r\n * • Content leaves the box ONLY on a sampled call, ONLY when opted in. We store\r\n * a single scalar factor + sample count — NEVER the sampled text itself.\r\n * • Fail-open everywhere: a network/parse/IO error returns null / passes the\r\n * approximation through unchanged and NEVER throws into a tool call or hook.\r\n *\r\n * No new dependencies: uses Node's global fetch (Node >= 18) and node:fs.\r\n */\r\n\r\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { dataRoot, ensureDir } from \"../core/paths.js\";\r\nimport type { ConfidenceSource, ModelFamily } from \"./types.js\";\r\n\r\n// ── Env switches ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Is opt-in Anthropic calibration enabled for this process?\r\n *\r\n * Requires BOTH:\r\n * • AGENT_CONNECTOR_CALIBRATE — a comma-separated allowlist that INCLUDES\r\n * `anthropic` (so a future `AGENT_CONNECTOR_CALIBRATE=openai,anthropic` can\r\n * opt other families in without touching this gate), and\r\n * • ANTHROPIC_API_KEY — a non-empty key to authenticate the off-box call.\r\n *\r\n * Either missing → disabled (the privacy-safe default).\r\n */\r\nexport function isCalibrationEnabled(): boolean {\r\n const families = (process.env.AGENT_CONNECTOR_CALIBRATE || \"\")\r\n .split(\",\")\r\n .map((s) => s.trim())\r\n .filter((s) => s !== \"\");\r\n const key = (process.env.ANTHROPIC_API_KEY || \"\").trim();\r\n return families.includes(\"anthropic\") && key !== \"\";\r\n}\r\n\r\n// ── Factor store (dataRoot/calibration.json) ────────────────────────────────\r\n\r\n/** One learned correction factor for a model family: factor + sample count. */\r\nexport interface CalibrationFactor {\r\n /** Multiply an approx count by this to estimate the real count. */\r\n factor: number;\r\n /** How many samples have folded into the rolling mean. */\r\n samples: number;\r\n}\r\n\r\n/** The on-disk factor map, keyed by model family. */\r\nexport type CalibrationFactors = Partial<Record<ModelFamily, CalibrationFactor>>;\r\n\r\n/** Path to the learned-factor store: dataRoot()/calibration.json. */\r\nexport function calibrationPath(): string {\r\n return join(dataRoot(), \"calibration.json\");\r\n}\r\n\r\n/**\r\n * Clamp a measured exact/approx ratio into a sane range. A pathological sample\r\n * (e.g. an empty-string approx of 0 → Infinity, or a parse glitch) must never\r\n * poison the rolling mean, so ratios are pinned to [0.2, 5].\r\n */\r\nconst RATIO_MIN = 0.2;\r\nconst RATIO_MAX = 5;\r\n\r\nfunction clampRatio(ratio: number): number | null {\r\n if (!Number.isFinite(ratio) || ratio <= 0) return null;\r\n if (ratio < RATIO_MIN) return RATIO_MIN;\r\n if (ratio > RATIO_MAX) return RATIO_MAX;\r\n return ratio;\r\n}\r\n\r\n/**\r\n * Load the learned factors from disk. Fail-open: a missing/unreadable/malformed\r\n * file returns an empty map rather than throwing.\r\n */\r\nexport function loadFactors(): CalibrationFactors {\r\n const path = calibrationPath();\r\n if (!existsSync(path)) return {};\r\n try {\r\n const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\r\n if (parsed === null || typeof parsed !== \"object\") return {};\r\n const out: CalibrationFactors = {};\r\n for (const [family, value] of Object.entries(parsed as Record<string, unknown>)) {\r\n if (value === null || typeof value !== \"object\") continue;\r\n const rec = value as Record<string, unknown>;\r\n const factor = rec[\"factor\"];\r\n const samples = rec[\"samples\"];\r\n if (\r\n typeof factor === \"number\" &&\r\n Number.isFinite(factor) &&\r\n factor > 0 &&\r\n typeof samples === \"number\" &&\r\n Number.isFinite(samples) &&\r\n samples >= 0\r\n ) {\r\n (out as Record<string, CalibrationFactor>)[family] = {\r\n factor,\r\n samples,\r\n };\r\n }\r\n }\r\n return out;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\n/**\r\n * Fold one observed exact/approx `ratio` into the rolling mean for `family` and\r\n * persist it. A running average converges to the true mean while staying cheap\r\n * and stateless across processes (each writer reads the prior count, blends in\r\n * the new sample, writes back). Fail-open: any read/write/clamp problem leaves\r\n * the store untouched and never throws.\r\n */\r\nexport function recordSample(family: ModelFamily, ratio: number): void {\r\n const clamped = clampRatio(ratio);\r\n if (clamped === null) return;\r\n try {\r\n const factors = loadFactors();\r\n const prior = factors[family];\r\n let next: CalibrationFactor;\r\n if (prior === undefined || prior.samples <= 0) {\r\n next = { factor: clamped, samples: 1 };\r\n } else {\r\n const samples = prior.samples + 1;\r\n // Running average: new mean = old mean + (sample - old mean) / n.\r\n const factor = prior.factor + (clamped - prior.factor) / samples;\r\n next = { factor, samples };\r\n }\r\n const updated: CalibrationFactors = { ...factors, [family]: next };\r\n const path = calibrationPath();\r\n ensureDir(dataRoot());\r\n writeFileSync(path, `${JSON.stringify(updated, null, 2)}\\n`, \"utf8\");\r\n } catch {\r\n /* fail-open: a calibration write must never break a tool call */\r\n }\r\n}\r\n\r\n// ── Anthropic count_tokens (off-box, sampled, fail-open) ─────────────────────\r\n\r\n/** Default model used for count_tokens when no family→model map applies. */\r\nconst DEFAULT_ANTHROPIC_MODEL = \"claude-3-5-sonnet-latest\";\r\n\r\n/** Anthropic count_tokens endpoint. */\r\nconst COUNT_TOKENS_URL = \"https://api.anthropic.com/v1/messages/count_tokens\";\r\n\r\n/** The single field we read out of a successful response. */\r\ninterface CountTokensResponse {\r\n input_tokens?: unknown;\r\n}\r\n\r\n/**\r\n * Ask Anthropic's `count_tokens` endpoint for the exact token count of `text`\r\n * under `model`. Returns the integer count, or `null` on ANY failure (no key,\r\n * non-200, network error, unparseable body) — fail-open by contract.\r\n *\r\n * Only ever called on a SAMPLED tool call when calibration is opted in, so the\r\n * sampled text leaves the box only under explicit operator consent.\r\n */\r\nexport async function countTokensAnthropic(\r\n text: string,\r\n model: string = DEFAULT_ANTHROPIC_MODEL,\r\n): Promise<number | null> {\r\n const apiKey = (process.env.ANTHROPIC_API_KEY || \"\").trim();\r\n if (apiKey === \"\") return null;\r\n try {\r\n const res = await fetch(COUNT_TOKENS_URL, {\r\n method: \"POST\",\r\n headers: {\r\n \"x-api-key\": apiKey,\r\n \"anthropic-version\": \"2023-06-01\",\r\n \"content-type\": \"application/json\",\r\n },\r\n body: JSON.stringify({\r\n model,\r\n messages: [{ role: \"user\", content: text }],\r\n }),\r\n });\r\n if (!res.ok) return null;\r\n const body = (await res.json()) as CountTokensResponse;\r\n const n = body.input_tokens;\r\n if (typeof n === \"number\" && Number.isFinite(n) && n >= 0) {\r\n return n;\r\n }\r\n return null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ── Sampling (rate-limit so we never hammer the API) ─────────────────────────\r\n\r\n/**\r\n * Sample roughly 1 in {@link SAMPLE_EVERY} calls, with a hard per-minute cap so\r\n * a burst of tool calls can never fan out into a burst of API calls. Both\r\n * counters are in-memory (per process) — calibration is a best-effort,\r\n * low-volume background signal, not a per-call requirement.\r\n */\r\nconst SAMPLE_EVERY = 20;\r\nconst MAX_SAMPLES_PER_MINUTE = 10;\r\n\r\nlet callCounter = 0;\r\nlet windowStartMs = 0;\r\nlet samplesThisWindow = 0;\r\n\r\n/**\r\n * Should THIS call fire an off-box count_tokens sample? Combines a 1-in-N\r\n * counter with a rolling per-minute cap. Pure in-memory bookkeeping; never\r\n * throws, never blocks.\r\n */\r\nexport function shouldSample(now: number = Date.now()): boolean {\r\n // Roll the per-minute window.\r\n if (now - windowStartMs >= 60_000) {\r\n windowStartMs = now;\r\n samplesThisWindow = 0;\r\n }\r\n callCounter += 1;\r\n if (callCounter % SAMPLE_EVERY !== 0) return false;\r\n if (samplesThisWindow >= MAX_SAMPLES_PER_MINUTE) return false;\r\n samplesThisWindow += 1;\r\n return true;\r\n}\r\n\r\n/** Reset the in-memory sampling counters. Test-only seam; cheap + side-effect-free. */\r\nexport function _resetSamplingForTest(): void {\r\n callCounter = 0;\r\n windowStartMs = 0;\r\n samplesThisWindow = 0;\r\n}\r\n\r\n// ── Apply a learned factor to a local approximation ──────────────────────────\r\n\r\n/** Result of {@link applyCalibration}: the adjusted count + its honest source. */\r\nexport interface CalibratedCount {\r\n tokens: number;\r\n source: Extract<ConfidenceSource, \"tokenizer-approx\" | \"tokenizer-calibrated\">;\r\n}\r\n\r\n/**\r\n * Adjust a locally-tokenized `approxTokens` count by the learned factor for\r\n * `family`. When a stored factor exists for the anthropic family, return the\r\n * scaled count labeled \"tokenizer-calibrated\"; otherwise pass the approximation\r\n * through unchanged as \"tokenizer-approx\". Pure: reads the factor store but\r\n * mutates nothing.\r\n */\r\nexport function applyCalibration(\r\n approxTokens: number,\r\n family: ModelFamily,\r\n factors: CalibrationFactors = loadFactors(),\r\n): CalibratedCount {\r\n if (family === \"anthropic\") {\r\n const f = factors[family];\r\n if (f !== undefined && f.samples > 0 && Number.isFinite(f.factor) && f.factor > 0) {\r\n return {\r\n tokens: Math.max(0, Math.round(approxTokens * f.factor)),\r\n source: \"tokenizer-calibrated\",\r\n };\r\n }\r\n }\r\n return { tokens: approxTokens, source: \"tokenizer-approx\" };\r\n}\r\n\r\n/** The model used for a family's count_tokens sample (anthropic → default model). */\r\nexport function modelForFamily(_family: ModelFamily): string {\r\n return DEFAULT_ANTHROPIC_MODEL;\r\n}\r\n","/**\r\n * telemetry/proxy — the telemetry-wrapping MCP stdio proxy.\r\n *\r\n * `agent-connector serve <command> <args…>` spawns the developer's REAL MCP\r\n * server as a child and sits transparently between the host and that server,\r\n * speaking the MCP stdio wire format: NEWLINE-DELIMITED JSON-RPC (one single-line\r\n * JSON object per message, terminated by `\\n`; stdio has NO Content-Length\r\n * framing). Bytes are forwarded VERBATIM in both directions so the proxy can\r\n * never corrupt the stream; a side line-buffer tees a COPY of each direction,\r\n * parses complete messages, and measures per-tool token usage out of band.\r\n *\r\n * Honesty / safety rules baked in here:\r\n * • Forwarding always happens FIRST. Measurement runs after the bytes are out\r\n * the door and is wrapped so a measurement bug can never break a tool call.\r\n * • Telemetry stores AGGREGATE COUNTS ONLY — the parsed `tools/call` arguments\r\n * and results are tokenized and discarded; raw content is never persisted.\r\n * • AGENT_CONNECTOR_TELEMETRY=0 → still proxy transparently, skip all measure.\r\n *\r\n * The proxy resolves with the child's exit code and propagates SIGINT/SIGTERM.\r\n */\r\n\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport { spawnChild } from \"../core/spawn-child.js\";\r\nimport { type JsonRpcMessage, LineBuffer, idKey, isObject } from \"./jsonrpc.js\";\r\nimport {\r\n applyCalibration,\r\n countTokensAnthropic,\r\n isCalibrationEnabled,\r\n modelForFamily,\r\n recordSample,\r\n shouldSample,\r\n} from \"./calibration.js\";\r\nimport { measureToolCall, measureToolDefs } from \"./measure.js\";\r\nimport { newRecordId } from \"./store.js\";\r\nimport { inferModelFamily } from \"./tokenizer.js\";\r\nimport type {\r\n ConfidenceSource,\r\n LaunchMethod,\r\n ModelFamily,\r\n TelemetryInstallScope,\r\n TelemetryStore,\r\n Tokenizer,\r\n ToolEventRecord,\r\n} from \"./types.js\";\r\n\r\n/** Options for {@link runServeProxy}. */\r\nexport interface RunServeProxyOptions {\r\n /** Connector id this server belongs to (stamped on every record). */\r\n connectorId: string;\r\n /** The real MCP server executable to spawn. */\r\n command: string;\r\n /** Arguments passed to that executable. */\r\n args: string[];\r\n /** Where measured records are appended. */\r\n store: TelemetryStore;\r\n /** Tokenizer used for measurement (defaults baked into measure helpers). */\r\n tokenizer: Tokenizer;\r\n /**\r\n * Telemetry model-family hint. \"auto\" infers the family from the MCP client's\r\n * `initialize` clientInfo.name; any explicit family wins outright.\r\n */\r\n modelFamilyHint: \"auto\" | ModelFamily;\r\n /** Host platform executing this proxy (stamped on every record). */\r\n hostPlatform: PlatformId;\r\n /** Host session id (stamped on every record). */\r\n sessionId: string;\r\n /** Hashed stable project identity (stamped on every record). */\r\n projectKey: string;\r\n /** Human-readable project directory (stamped on every record). */\r\n projectDir: string;\r\n /** When true, tokenize the `tools/list` schema once → tool-defs overhead. */\r\n measureToolDefs: boolean;\r\n /**\r\n * Install scope (global user vs project-local) stamped on every record.\r\n * OPTIONAL: omitted for configs written before scope plumbing existed, leaving\r\n * the field off the row so it reads as \"unknown\".\r\n */\r\n installScope?: TelemetryInstallScope;\r\n /**\r\n * How the wrapped server was launched (npx/bunx/uvx/node/binary/http), stamped\r\n * on every record. OPTIONAL for the same backward-compat reason as above.\r\n */\r\n launchMethod?: LaunchMethod;\r\n}\r\n\r\n// ── JSON-RPC shapes we read (kept local + narrow; everything else opaque) ─────\r\n\r\n/** A client→server `tools/call` we remember until its result comes back. */\r\ninterface PendingCall {\r\n toolName: string;\r\n args: unknown;\r\n}\r\n\r\n/**\r\n * Canonical string form of a tool call's arguments for an off-box count_tokens\r\n * sample — mirrors the tokenizer's own serialization so the sampled text equals\r\n * the bytes we approximated locally. A string passes through; anything else is\r\n * JSON-stringified; a value that stringifies to undefined becomes \"\".\r\n */\r\nfunction canonicalArgs(value: unknown): string {\r\n if (typeof value === \"string\") return value;\r\n try {\r\n return JSON.stringify(value) ?? \"\";\r\n } catch {\r\n return \"\";\r\n }\r\n}\r\n\r\n/**\r\n * Run the telemetry-wrapping MCP stdio proxy.\r\n *\r\n * Spawns `command args` with piped stdin/stdout (stderr inherited), forwards\r\n * both directions byte-for-byte, and measures `tools/call` round-trips and the\r\n * one-time `tools/list` tool-defs overhead into `store`. Resolves with the\r\n * child's exit code (or a signal-derived code) once the child closes.\r\n */\r\nexport async function runServeProxy(\r\n opts: RunServeProxyOptions,\r\n): Promise<number> {\r\n const {\r\n connectorId,\r\n command,\r\n args,\r\n store,\r\n tokenizer,\r\n modelFamilyHint,\r\n hostPlatform,\r\n sessionId,\r\n projectKey,\r\n projectDir,\r\n measureToolDefs: shouldMeasureToolDefs,\r\n installScope,\r\n launchMethod,\r\n } = opts;\r\n\r\n // Global kill switch: still proxy transparently, but skip ALL measurement.\r\n const measuringEnabled = process.env.AGENT_CONNECTOR_TELEMETRY !== \"0\";\r\n // OPT-IN Anthropic count_tokens calibration. Resolved once per session — the\r\n // gate (AGENT_CONNECTOR_CALIBRATE + ANTHROPIC_API_KEY) is process-level and\r\n // does not change mid-session. Off by default; privacy-safe.\r\n const calibrationEnabled = isCalibrationEnabled();\r\n\r\n // spawnChild fixes native-Windows package-runner launches: a bare npx/uvx\r\n // resolves to a .cmd shim that raw CreateProcess can't find/launch, so it is\r\n // run via a quoted single command line with a shell. No-op on macOS/Linux.\r\n const child = spawnChild(command, args, {\r\n stdio: [\"pipe\", \"pipe\", \"inherit\"],\r\n env: process.env,\r\n });\r\n\r\n // ── Per-session measurement state ───────────────────────────────────────\r\n // Family resolved from the client's `initialize` (or the hint) and cached.\r\n let family: ModelFamily = inferModelFamily(\"\", modelFamilyHint);\r\n // Outstanding tools/call requests, keyed by JSON-RPC id.\r\n const pending = new Map<string, PendingCall>();\r\n // Monotonic sequence so two records in the same ms still get distinct ids.\r\n let seq = 0;\r\n // tools/list overhead is measured exactly once per session.\r\n let toolDefsMeasured = false;\r\n\r\n /**\r\n * Build a base record stamped with the per-session identity fields plus the\r\n * scope dimensions. `installScope` / `launchMethod` are spread in ONLY when\r\n * defined, so a session without them leaves those fields off the row entirely\r\n * (read as \"unknown\") rather than writing an explicit `undefined`.\r\n */\r\n function baseRecord(): Omit<\r\n ToolEventRecord,\r\n \"toolName\" | \"scope\" | \"inputTokens\" | \"outputTokens\" | \"confidenceSource\" | \"isError\"\r\n > {\r\n return {\r\n id: newRecordId(seq++),\r\n ts: Date.now(),\r\n connectorId,\r\n hostPlatform,\r\n sessionId,\r\n projectKey,\r\n projectDir,\r\n ...(installScope !== undefined ? { installScope } : {}),\r\n ...(launchMethod !== undefined ? { launchMethod } : {}),\r\n };\r\n }\r\n\r\n /**\r\n * Inspect one parsed CLIENT→SERVER message. Caches the model family on\r\n * `initialize` and remembers `tools/call` requests so their results can be\r\n * measured when they come back. Never throws into the data path.\r\n */\r\n function onClientMessage(msg: JsonRpcMessage): void {\r\n const method = msg.method;\r\n if (method === \"initialize\") {\r\n const params = isObject(msg.params) ? msg.params : undefined;\r\n const clientInfo =\r\n params && isObject(params[\"clientInfo\"])\r\n ? (params[\"clientInfo\"] as Record<string, unknown>)\r\n : undefined;\r\n const clientName =\r\n clientInfo && typeof clientInfo[\"name\"] === \"string\"\r\n ? (clientInfo[\"name\"] as string)\r\n : \"\";\r\n family = inferModelFamily(clientName, modelFamilyHint);\r\n return;\r\n }\r\n\r\n if (method === \"tools/call\") {\r\n const key = idKey(msg.id);\r\n if (key === null) return; // a call with no id has no result to match\r\n const params = isObject(msg.params) ? msg.params : undefined;\r\n const toolName =\r\n params && typeof params[\"name\"] === \"string\"\r\n ? (params[\"name\"] as string)\r\n : \"unknown\";\r\n const callArgs = params ? params[\"arguments\"] : undefined;\r\n pending.set(key, { toolName, args: callArgs });\r\n }\r\n }\r\n\r\n /**\r\n * Inspect one parsed SERVER→CLIENT message. Matches `tools/call` results to\r\n * remembered requests and measures them; measures `tools/list` results once.\r\n * Never throws into the data path.\r\n */\r\n function onServerMessage(msg: JsonRpcMessage): void {\r\n const key = idKey(msg.id);\r\n if (key === null) return; // notifications carry no id we can correlate\r\n\r\n const pendingCall = pending.get(key);\r\n if (pendingCall !== undefined) {\r\n pending.delete(key);\r\n recordToolCall(pendingCall, msg);\r\n return;\r\n }\r\n\r\n // Not a tracked tools/call — it may be the tools/list result we measure\r\n // once for the fixed tool-definition overhead.\r\n if (shouldMeasureToolDefs && !toolDefsMeasured) {\r\n const result = isObject(msg.result) ? msg.result : undefined;\r\n const tools = result ? result[\"tools\"] : undefined;\r\n if (Array.isArray(tools)) {\r\n recordToolDefs(tools);\r\n }\r\n }\r\n }\r\n\r\n /** Measure a matched tools/call round-trip and append the record. */\r\n function recordToolCall(call: PendingCall, response: JsonRpcMessage): void {\r\n const hasError = response.error !== undefined;\r\n const result = response.result;\r\n // A JSON-RPC error has no `result`; measure against an empty object so the\r\n // input (args) tokens are still recorded honestly.\r\n const resultForMeasure = hasError ? {} : result;\r\n const measurement = measureToolCall(\r\n call.args,\r\n resultForMeasure,\r\n family,\r\n tokenizer,\r\n );\r\n\r\n // OPT-IN calibration: for an anthropic session, nudge the locally-tokenized\r\n // approx counts toward Anthropic's real numbers via the learned factor, and\r\n // fire a sampled off-box count_tokens to keep that factor fresh. Both steps\r\n // are best-effort and wrapped so they can never break the record path.\r\n let inputTokens = measurement.inputTokens;\r\n let outputTokens = measurement.outputTokens;\r\n let confidenceSource: ConfidenceSource = measurement.source;\r\n if (calibrationEnabled && family === \"anthropic\") {\r\n try {\r\n ({ inputTokens, outputTokens, confidenceSource } = calibrateCounts(\r\n measurement.inputTokens,\r\n measurement.outputTokens,\r\n measurement.source,\r\n ));\r\n maybeSampleCalibration(call.args, measurement.inputTokens);\r\n } catch {\r\n // Calibration is a non-essential enrichment — fall back to the raw\r\n // measurement values already captured above.\r\n }\r\n }\r\n\r\n // MCP signals a tool-level failure via result.isError===true; a JSON-RPC\r\n // protocol error (response.error) is also a failure.\r\n const resultIsError =\r\n isObject(result) && result[\"isError\"] === true;\r\n store.append({\r\n ...baseRecord(),\r\n toolName: call.toolName,\r\n scope: \"call\",\r\n inputTokens,\r\n outputTokens,\r\n confidenceSource,\r\n isError: hasError || resultIsError,\r\n });\r\n }\r\n\r\n /**\r\n * Apply the learned anthropic factor to the approx input/output counts. Only\r\n * a `tokenizer-approx` measurement is calibratable (a heuristic fallback or\r\n * host-native count is left exactly as-is); the combined source becomes\r\n * `tokenizer-calibrated` only when at least one side was actually scaled.\r\n */\r\n function calibrateCounts(\r\n rawIn: number,\r\n rawOut: number,\r\n rawSource: ConfidenceSource,\r\n ): { inputTokens: number; outputTokens: number; confidenceSource: ConfidenceSource } {\r\n if (rawSource !== \"tokenizer-approx\") {\r\n return { inputTokens: rawIn, outputTokens: rawOut, confidenceSource: rawSource };\r\n }\r\n const cin = applyCalibration(rawIn, family);\r\n const cout = applyCalibration(rawOut, family);\r\n const calibrated =\r\n cin.source === \"tokenizer-calibrated\" || cout.source === \"tokenizer-calibrated\";\r\n return {\r\n inputTokens: cin.tokens,\r\n outputTokens: cout.tokens,\r\n confidenceSource: calibrated ? \"tokenizer-calibrated\" : \"tokenizer-approx\",\r\n };\r\n }\r\n\r\n /**\r\n * On a SAMPLED call, fire-and-forget an off-box count_tokens for the call's\r\n * input args and fold the resulting exact/approx ratio into the learned\r\n * factor. Returns immediately — the network round-trip never blocks\r\n * forwarding or measurement. Fail-open: any error is swallowed.\r\n */\r\n function maybeSampleCalibration(args: unknown, approxInputTokens: number): void {\r\n if (approxInputTokens <= 0) return; // nothing to ratio against\r\n if (!shouldSample()) return;\r\n const text = canonicalArgs(args);\r\n if (text === \"\") return;\r\n // Detached promise — intentionally NOT awaited so the proxy never blocks on\r\n // the network. Errors are handled inside countTokensAnthropic (returns null)\r\n // and the .catch is a belt-and-braces guard against an unhandled rejection.\r\n void countTokensAnthropic(text, modelForFamily(family))\r\n .then((exact) => {\r\n if (exact !== null && exact > 0) {\r\n recordSample(family, exact / approxInputTokens);\r\n }\r\n })\r\n .catch(() => {\r\n /* fail-open: a calibration sample must never surface an error */\r\n });\r\n }\r\n\r\n /** Measure the one-time tools/list overhead and append the record. */\r\n function recordToolDefs(tools: readonly unknown[]): void {\r\n toolDefsMeasured = true;\r\n const count = measureToolDefs(tools, family, tokenizer);\r\n store.append({\r\n ...baseRecord(),\r\n toolName: \"*\",\r\n scope: \"tool_defs\",\r\n inputTokens: count.tokens,\r\n outputTokens: 0,\r\n confidenceSource: count.source,\r\n isError: false,\r\n });\r\n }\r\n\r\n /** Parse one NDJSON line and route it, swallowing any measurement error. */\r\n function handleLine(line: string, direction: \"client\" | \"server\"): void {\r\n if (!measuringEnabled) return;\r\n let msg: JsonRpcMessage;\r\n try {\r\n const parsed = JSON.parse(line) as unknown;\r\n if (!isObject(parsed)) return;\r\n msg = parsed as JsonRpcMessage;\r\n } catch {\r\n return; // not a complete/valid JSON message — ignore for telemetry\r\n }\r\n try {\r\n if (direction === \"client\") onClientMessage(msg);\r\n else onServerMessage(msg);\r\n } catch {\r\n // Measurement must NEVER break the proxy. Drop the record silently.\r\n }\r\n }\r\n\r\n const clientLines = new LineBuffer((line) => handleLine(line, \"client\"));\r\n const serverLines = new LineBuffer((line) => handleLine(line, \"server\"));\r\n\r\n // ── Transparent byte forwarding (host stdin → child stdin) ───────────────\r\n // Forward VERBATIM first, then tee a copy into the line buffer to measure.\r\n // Honor backpressure: if the child's stdin buffer is full, pause the host\r\n // stdin and resume on drain so a slow server can't make us buffer unboundedly.\r\n process.stdin.on(\"data\", (chunk: Buffer) => {\r\n if (child.stdin && child.stdin.writable) {\r\n const ok = child.stdin.write(chunk);\r\n if (!ok) {\r\n process.stdin.pause();\r\n child.stdin.once(\"drain\", () => process.stdin.resume());\r\n }\r\n }\r\n if (measuringEnabled) {\r\n try {\r\n clientLines.push(chunk);\r\n } catch {\r\n /* tee side-channel must never affect forwarding */\r\n }\r\n }\r\n });\r\n process.stdin.on(\"end\", () => {\r\n if (child.stdin && !child.stdin.destroyed) child.stdin.end();\r\n });\r\n\r\n // ── Transparent byte forwarding (child stdout → host stdout) ─────────────\r\n if (child.stdout) {\r\n const childStdout = child.stdout;\r\n childStdout.on(\"data\", (chunk: Buffer) => {\r\n const ok = process.stdout.write(chunk);\r\n if (!ok) {\r\n childStdout.pause();\r\n process.stdout.once(\"drain\", () => childStdout.resume());\r\n }\r\n if (measuringEnabled) {\r\n try {\r\n serverLines.push(chunk);\r\n } catch {\r\n /* tee side-channel must never affect forwarding */\r\n }\r\n }\r\n });\r\n }\r\n\r\n // ── Signal propagation: forward SIGINT/SIGTERM to the child ──────────────\r\n const forwardSignal = (signal: NodeJS.Signals): void => {\r\n try {\r\n if (!child.killed) child.kill(signal);\r\n } catch {\r\n /* child may already be gone */\r\n }\r\n };\r\n const onSigint = (): void => forwardSignal(\"SIGINT\");\r\n const onSigterm = (): void => forwardSignal(\"SIGTERM\");\r\n process.on(\"SIGINT\", onSigint);\r\n process.on(\"SIGTERM\", onSigterm);\r\n\r\n // ── Resolve with the child's exit code (or signal-derived code) ──────────\r\n return await new Promise<number>((resolve) => {\r\n const finish = (code: number): void => {\r\n process.removeListener(\"SIGINT\", onSigint);\r\n process.removeListener(\"SIGTERM\", onSigterm);\r\n try {\r\n store.close();\r\n } catch {\r\n /* best-effort flush */\r\n }\r\n resolve(code);\r\n };\r\n\r\n child.on(\"error\", () => {\r\n // The server could not be spawned/launched. Surface a conventional\r\n // \"command not executable\" style failure code.\r\n finish(126);\r\n });\r\n\r\n child.on(\"close\", (code, signal) => {\r\n if (typeof code === \"number\") {\r\n finish(code);\r\n return;\r\n }\r\n // Killed by a signal → conventional 128 + signal number where known.\r\n if (signal) {\r\n const num = signalToNumber(signal);\r\n finish(num === undefined ? 1 : 128 + num);\r\n return;\r\n }\r\n finish(1);\r\n });\r\n });\r\n}\r\n\r\n/** Map the common termination signals to their numbers for exit-code math. */\r\nfunction signalToNumber(signal: NodeJS.Signals): number | undefined {\r\n const table: Partial<Record<NodeJS.Signals, number>> = {\r\n SIGINT: 2,\r\n SIGQUIT: 3,\r\n SIGKILL: 9,\r\n SIGTERM: 15,\r\n };\r\n return table[signal];\r\n}\r\n\r\nexport default runServeProxy;\r\n","/**\r\n * runtime/usage-event — the OPT-IN host-native turn-usage enricher (enricher 4a).\r\n *\r\n * Gemini CLI / Antigravity fire an `AfterModel` (Antigravity CLI: `PostInvocation`)\r\n * hook AFTER each model turn, whose stdin payload carries a `usageMetadata` block\r\n * with the host's OWN exact token accounting for that whole conversation turn:\r\n * { promptTokenCount, candidatesTokenCount, cachedContentTokenCount,\r\n * thoughtsTokenCount, totalTokenCount }\r\n * This is the single point in the whole matrix where a host reports REAL per-turn\r\n * usage to a command we control — so we record it as `confidenceSource:\"host-native\"`\r\n * (the top of CONFIDENCE_RANK), upgrading those rows from the serve-proxy estimate.\r\n *\r\n * DISTINCT, NEVER-SUMMED ORIGIN. Host-native turn usage is WHOLE-CONVERSATION, not\r\n * per-MCP. To avoid double-counting against (a) the per-MCP serve-proxy `call`\r\n * rows and (b) the usage-reader host-scan numbers, every record written here uses\r\n * the DISTINCT scope `\"model_turn\"`. The MCP leaderboard excludes that scope (just\r\n * as it special-cases `tool_defs`); the unified leaderboard surfaces it as its own\r\n * clearly-labeled section that is explicitly never added to the other two origins.\r\n *\r\n * PRIVACY + FAIL-OPEN. This enricher is OPT-IN at install time (see the adapters);\r\n * it stores AGGREGATE COUNTS ONLY (no raw content — the payload's prompt/response\r\n * text is never read). ANY parse / store / IO error degrades to exit 0 and records\r\n * NOTHING: a host-native usage hook must never break a model turn. This function\r\n * never throws.\r\n */\r\n\r\nimport { projectIdentity } from \"../core/paths.js\";\r\nimport { newRecordId, openStore } from \"../telemetry/store.js\";\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport type { ToolEventRecord } from \"../telemetry/types.js\";\r\n\r\n/** Flags + stdin the CLI hands to {@link runUsageEvent}. */\r\nexport interface RunUsageEventOptions {\r\n /** Host platform id from the command (`usage-event <platformId> …`). */\r\n platformId: string;\r\n /** Connector id from `--connector <id>` (stamped on the record). */\r\n connectorId: string;\r\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → no record. */\r\n stdin: string;\r\n}\r\n\r\n/** Process-level result the CLI translates into an exit code. */\r\nexport interface RunUsageEventResult {\r\n /** Always 0 — a host-native usage hook must never block a model turn. */\r\n exitCode: number;\r\n /** True when a `model_turn` record was appended (false on any fail-open path). */\r\n recorded: boolean;\r\n}\r\n\r\n/** The never-records, never-throws result. */\r\nconst NOOP: RunUsageEventResult = { exitCode: 0, recorded: false };\r\n\r\nfunction isObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\r\n}\r\n\r\n/** Coerce a value to a non-negative safe integer (NaN/negatives/floats → 0). */\r\nfunction toNonNegInt(v: unknown): number {\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n if (!Number.isFinite(n) || n < 0) return 0;\r\n return Math.floor(n);\r\n}\r\n\r\n/** First non-empty string among the given keys of an object. */\r\nfunction firstStr(obj: Record<string, unknown>, keys: readonly string[]): string | undefined {\r\n for (const k of keys) {\r\n const v = obj[k];\r\n if (typeof v === \"string\" && v.trim() !== \"\") return v;\r\n }\r\n return undefined;\r\n}\r\n\r\n/** Read a token field tolerant of both camelCase and snake_case spellings. */\r\nfunction tokenField(\r\n meta: Record<string, unknown>,\r\n keys: readonly string[],\r\n): number {\r\n for (const k of keys) {\r\n if (k in meta) return toNonNegInt(meta[k]);\r\n }\r\n return 0;\r\n}\r\n\r\n/** Locate the `usageMetadata` block under either spelling, top-level or nested. */\r\nfunction findUsageMetadata(root: Record<string, unknown>): Record<string, unknown> | undefined {\r\n for (const k of [\"usageMetadata\", \"usage_metadata\"]) {\r\n if (isObject(root[k])) return root[k] as Record<string, unknown>;\r\n }\r\n // Some hosts nest the model response (with its usage) under a `response` /\r\n // `modelResponse` envelope — probe one level down, best-effort.\r\n for (const k of [\"response\", \"modelResponse\", \"model_response\"]) {\r\n const nested = root[k];\r\n if (isObject(nested)) {\r\n for (const mk of [\"usageMetadata\", \"usage_metadata\"]) {\r\n if (isObject(nested[mk])) return nested[mk] as Record<string, unknown>;\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** A registered PlatformId, or \"unknown\" when the id is not one we know. */\r\nconst KNOWN_PLATFORMS: ReadonlySet<string> = new Set<PlatformId>([\r\n \"claude-code\", \"codex\", \"cursor\", \"vscode-copilot\", \"jetbrains-copilot\",\r\n \"copilot-cli\", \"gemini-cli\", \"opencode\", \"kilo\", \"kilo-cli\", \"warp\", \"hermes\",\r\n \"openclaw\", \"zed\", \"antigravity\", \"antigravity-cli\", \"kiro\", \"qwen-code\",\r\n \"kimi\", \"pi\", \"omp\", \"droid\", \"roo-code\", \"trae\", \"amp\", \"codebuff\", \"mux\",\r\n \"crush\", \"goose\", \"synthetic\", \"unknown\",\r\n]);\r\n\r\nfunction asPlatformId(id: string): PlatformId {\r\n return (KNOWN_PLATFORMS.has(id) ? id : \"unknown\") as PlatformId;\r\n}\r\n\r\n/**\r\n * Record one host-native model turn. Parses the host's `usageMetadata`, maps it to\r\n * a DISTINCT `model_turn` `ToolEventRecord` (confidence `host-native`, toolName\r\n * `\"*\"`), and appends it to the shared telemetry store. NEVER throws and never\r\n * blocks the turn: every failure path resolves to {@link NOOP} (exit 0, nothing\r\n * recorded).\r\n */\r\nexport async function runUsageEvent(\r\n opts: RunUsageEventOptions,\r\n): Promise<RunUsageEventResult> {\r\n try {\r\n // Global telemetry kill switch is honored by the store's append (no-op when\r\n // AGENT_CONNECTOR_TELEMETRY=0); we still parse cheaply and let append drop it.\r\n const trimmed = opts.stdin.trim();\r\n if (trimmed === \"\") return NOOP;\r\n\r\n let raw: unknown;\r\n try {\r\n raw = JSON.parse(trimmed);\r\n } catch {\r\n return NOOP; // malformed payload → fail-open, record nothing\r\n }\r\n if (!isObject(raw)) return NOOP;\r\n\r\n const meta = findUsageMetadata(raw);\r\n if (meta === undefined) return NOOP;\r\n\r\n const promptTokens = tokenField(meta, [\"promptTokenCount\", \"prompt_token_count\"]);\r\n const candidates = tokenField(meta, [\"candidatesTokenCount\", \"candidates_token_count\"]);\r\n const cached = tokenField(meta, [\"cachedContentTokenCount\", \"cached_content_token_count\"]);\r\n const thoughts = tokenField(meta, [\"thoughtsTokenCount\", \"thoughts_token_count\"]);\r\n\r\n // promptTokenCount is cache-INCLUSIVE (Gemini-family semantics, mirrored by\r\n // the antigravity-shared usage reader): net fresh input = prompt - cached.\r\n const inputTokens = Math.max(0, promptTokens - Math.min(cached, promptTokens));\r\n // Output = generated candidates + reasoning (\"thoughts\") tokens.\r\n const outputTokens = candidates + thoughts;\r\n\r\n // A turn with no measurable tokens is not worth a row.\r\n if (inputTokens === 0 && outputTokens === 0) return NOOP;\r\n\r\n // Identity: prefer payload session/cwd; fall back to process.cwd() for the\r\n // project key so a turn always lands in a stable project partition.\r\n const sessionId =\r\n firstStr(raw, [\"session_id\", \"sessionId\", \"conversationId\", \"conversation_id\"]) ?? \"\";\r\n const cwd = firstStr(raw, [\"cwd\", \"workspace\", \"projectDir\", \"project_dir\"]) ?? process.cwd();\r\n const id = projectIdentity(cwd);\r\n\r\n const record: ToolEventRecord = {\r\n id: newRecordId(0),\r\n ts: Date.now(),\r\n connectorId: opts.connectorId,\r\n toolName: \"*\",\r\n scope: \"model_turn\",\r\n hostPlatform: asPlatformId(opts.platformId),\r\n sessionId,\r\n projectKey: id.key,\r\n projectDir: id.dir,\r\n inputTokens,\r\n outputTokens,\r\n confidenceSource: \"host-native\",\r\n isError: false,\r\n };\r\n\r\n const store = openStore({});\r\n try {\r\n store.append(record);\r\n } finally {\r\n try {\r\n store.close();\r\n } catch {\r\n /* best-effort flush */\r\n }\r\n }\r\n return { exitCode: 0, recorded: true };\r\n } catch {\r\n // Fail-open: a host-native usage enricher must never surface an error.\r\n return NOOP;\r\n }\r\n}\r\n\r\nexport default runUsageEvent;\r\n","/**\r\n * runtime/statusline-entrypoint — the universal statusline (HUD) renderer.\r\n *\r\n * A statusline-supporting host points its status line config at the single stable\r\n * home binary (`<homeBin> statusline <platformId> --connector <id>`, built by\r\n * core/spawn.buildHomeBinStatuslineCommand). The CLI reads stdin and calls\r\n * {@link runStatusline} with the parsed flags + the raw stdin string. This module:\r\n *\r\n * 1. Loads the registered connector (live render handler) and the host adapter.\r\n * 2. Parses the host's raw status payload into a normalized StatuslineContext.\r\n * 3. Calls the connector's render(ctx) and coerces the result to a string.\r\n * 4. Formats the line into the host's native status reply (exit code + stdout).\r\n *\r\n * FAIL-SAFE is the contract (NOT fail-open with carve-outs like hooks): a status\r\n * line is decorative — it must NEVER wedge the host or spew an error/stack trace\r\n * into the status bar. So ANY error path — bad payload, missing connector,\r\n * throwing render, adapter without statusline support — returns exit 0 with NO\r\n * stdout (an empty status line), never a non-zero exit and never partial output.\r\n *\r\n * NOTE (follow-up): no telemetry is recorded for statusline renders in v1 (out of\r\n * scope). When added, it MUST stay inside the same fail-safe envelope.\r\n */\r\n\r\nimport type { PlatformId } from \"../core/types.js\";\r\nimport { log } from \"../core/logger.js\";\r\nimport {\r\n loadRegisteredConnector,\r\n readRegisteredMeta,\r\n} from \"../core/load-connector.js\";\r\nimport { loadAdapter } from \"../adapters/registry.js\";\r\nimport {\r\n attachLazyTelemetryUsage,\r\n buildTelemetryAccessor,\r\n} from \"./telemetry-accessor.js\";\r\n\r\n/** Flags + stdin the CLI hands to {@link runStatusline}. */\r\nexport interface RunStatuslineOptions {\r\n /** Host platform id from the command (`statusline <platformId> …`). */\r\n platformId: string;\r\n /** Connector id from `--connector <id>`. */\r\n connectorId: string;\r\n /** Raw stdin payload (host-native status JSON). Empty string is tolerated → `{}`. */\r\n stdin: string;\r\n}\r\n\r\n/** Process-level result the CLI translates into exit code + stdout/stderr. */\r\nexport interface RunStatuslineResult {\r\n exitCode: number;\r\n stdout?: string;\r\n stderr?: string;\r\n}\r\n\r\n/** The fail-safe result: exit 0, no output (an empty status line). */\r\nconst EMPTY: RunStatuslineResult = { exitCode: 0 };\r\n\r\n/**\r\n * Tolerantly parse the host's stdin payload. Empty / whitespace-only input is a\r\n * legitimate \"no payload\" signal and resolves to `{}`. Malformed JSON also\r\n * degrades to `{}` rather than throwing — fail-safe is the contract.\r\n */\r\nfunction parseStdin(stdin: string): unknown {\r\n const trimmed = stdin.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\r\n/**\r\n * Render one host status-line invocation. Always resolves (never rejects): every\r\n * failure path returns the empty {@link EMPTY} result so the CLI exits cleanly\r\n * with an empty status line. See the module header for the fail-safe contract.\r\n */\r\nexport async function runStatusline(\r\n opts: RunStatuslineOptions,\r\n): Promise<RunStatuslineResult> {\r\n const { platformId, connectorId, stdin } = opts;\r\n\r\n try {\r\n const connector = await loadRegisteredConnector(connectorId);\r\n const adapter = await loadAdapter(platformId);\r\n\r\n // Nothing to render: no statusline declared, or the host adapter has no\r\n // statusline runtime support (parse/format pair). Empty line, exit 0.\r\n if (\r\n !connector.statusline ||\r\n typeof connector.statusline.render !== \"function\" ||\r\n !adapter ||\r\n !adapter.parseStatusInput ||\r\n !adapter.formatStatusOutput\r\n ) {\r\n return EMPTY;\r\n }\r\n\r\n const raw = parseStdin(stdin);\r\n const ctx = adapter.parseStatusInput(raw);\r\n // The command carries the authoritative connector id; stamp it so render()\r\n // sees the connector it was dispatched for.\r\n ctx.connectorId = connectorId;\r\n // capabilities is REQUIRED on StatuslineContext. claude-code's parseStatusInput\r\n // sets it, but a future/third-party adapter could omit it — backfill so the\r\n // runtime guarantee matches the type (mirrors the simulate harness). Never\r\n // let render() see undefined capabilities while the type claims it is present.\r\n if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;\r\n // Recover the install scope from the registered metadata (sync) and stamp it\r\n // onto the ctx — scope is an install-time property the entrypoint has no\r\n // other access to. Undefined when the meta carries none. Also expose the\r\n // per-connector telemetry usage accessor (lazy, fail-safe to zeros).\r\n ctx.scope = readRegisteredMeta(connectorId)?.scope;\r\n ctx.telemetry = buildTelemetryAccessor(connectorId);\r\n // Keep `ctx.usage` synchronous for render functions, but compute it only if\r\n // the render actually reads the property. Status lines refresh frequently,\r\n // and the NDJSON backend has to scan the store for an all-time summary.\r\n attachLazyTelemetryUsage(ctx, connectorId);\r\n\r\n // Per-host render override: when rendering for host X, `hosts[X].render`\r\n // wins over the top-level render; a host not listed (or a per-host entry\r\n // that is somehow not a function) falls back to the top-level render.\r\n // Selection NEVER throws — fail-safe is preserved.\r\n const perHost = connector.statusline.hosts?.[platformId as PlatformId]?.render;\r\n const render =\r\n typeof perHost === \"function\" ? perHost : connector.statusline.render;\r\n\r\n const rendered = await render(ctx);\r\n // Coerce: a non-string return (number, accidental object) is stringified so\r\n // a misbehaving render never crashes the format step. null/undefined → \"\".\r\n const line = rendered == null ? \"\" : String(rendered);\r\n\r\n const reply = adapter.formatStatusOutput(line);\r\n return {\r\n exitCode: reply.exitCode,\r\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\r\n ...(reply.stderr !== undefined ? { stderr: reply.stderr } : {}),\r\n };\r\n } catch (err) {\r\n // FAIL-SAFE: a status line must never wedge the host or surface an error.\r\n // Log for diagnostics (stderr of the home binary — not the status bar) and\r\n // return the empty line.\r\n const message = err instanceof Error ? err.message : String(err);\r\n log.error(`statusline ${platformId} (${connectorId}) failed:`, message);\r\n return EMPTY;\r\n }\r\n}\r\n\r\nexport default runStatusline;\r\n","/**\r\n * runtime/action-entrypoint — the universal user-invokable action dispatcher.\r\n *\r\n * A future host affordance (slash command / keybinding) points at the single\r\n * stable home binary (`<homeBin> action <platformId> <actionId> --connector\r\n * <id>`, built by core/spawn.buildHomeBinActionCommand). The CLI parses the\r\n * flags and calls {@link runAction}. This module:\r\n *\r\n * 1. Loads the registered connector (live run handler) and the host adapter.\r\n * 2. Builds the shared {@link HostCtx} (host + the adapter's capabilities; NO\r\n * stdin — an action takes no host payload, unlike a hook or status line).\r\n * 3. Finds the action by id, resolves the per-host run override, runs it, and\r\n * prints the optional result.message.\r\n *\r\n * USER-TRIGGERED ERROR SEMANTICS (the key difference from hooks/statusline):\r\n * actions are invoked DELIBERATELY by a user, so errors are SURFACED, never\r\n * failed silently. An unknown action id or a throwing run → exit 1 + a stderr\r\n * message. (Hooks fail-open to exit 0; the statusline fail-safes to an empty\r\n * line — both are passive surfaces a host fires automatically. An action is not:\r\n * swallowing its failure would leave the user staring at nothing, unable to\r\n * tell the action ran.)\r\n *\r\n * NOTE (follow-up): no telemetry is recorded for action runs in v1 (out of\r\n * scope), and no adapter EMITS the affordance yet — v1 ships the dispatch\r\n * backbone only.\r\n */\r\n\r\nimport type {\r\n HostCtx,\r\n PlatformCapabilities,\r\n PlatformId,\r\n} from \"../core/types.js\";\r\nimport {\r\n loadRegisteredConnector,\r\n readRegisteredMeta,\r\n} from \"../core/load-connector.js\";\r\nimport { loadAdapter, REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\r\nimport { buildTelemetryAccessor } from \"./telemetry-accessor.js\";\r\n\r\n/** Flags the CLI hands to {@link runAction} (no stdin — actions take no payload). */\r\nexport interface RunActionOptions {\r\n /** Host platform id from the command (`action <platformId> <actionId> …`). */\r\n platformId: string;\r\n /** Connector id from `--connector <id>`. */\r\n connectorId: string;\r\n /** The action id positional (`action <platformId> <actionId> …`). */\r\n actionId: string;\r\n}\r\n\r\n/** Process-level result the CLI translates into exit code + stdout/stderr. */\r\nexport interface RunActionResult {\r\n exitCode: number;\r\n stdout?: string;\r\n stderr?: string;\r\n}\r\n\r\n/**\r\n * Minimal all-false capabilities for the fallback ctx when the host adapter is\r\n * unknown/unregistered. The action still runs (it takes no host payload), but\r\n * branching on `ctx.capabilities` reads every flag as off — no affordance is\r\n * advertised for a host we cannot describe.\r\n */\r\nconst EMPTY_CAPABILITIES: PlatformCapabilities = {\r\n preToolUse: false,\r\n postToolUse: false,\r\n preCompact: false,\r\n sessionStart: false,\r\n sessionEnd: false,\r\n userPromptSubmit: false,\r\n stop: false,\r\n notification: false,\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n transports: [],\r\n};\r\n\r\n/**\r\n * Dispatch one user-invokable action. Always resolves (never rejects): every\r\n * path returns a concrete {@link RunActionResult} so the CLI can exit cleanly,\r\n * but UNLIKE the hook/statusline runtimes an error resolves to exit 1 + a stderr\r\n * message (actions are user-triggered — surface the failure). See the module\r\n * header for the user-triggered error contract.\r\n */\r\nexport async function runAction(\r\n opts: RunActionOptions,\r\n): Promise<RunActionResult> {\r\n const { platformId, connectorId, actionId } = opts;\r\n\r\n let connector;\r\n try {\r\n connector = await loadRegisteredConnector(connectorId);\r\n } catch (err) {\r\n // A user asked to run an action on a connector we cannot load — surface it\r\n // (do NOT fail-open: the user would otherwise see nothing and assume it ran).\r\n const message = err instanceof Error ? err.message : String(err);\r\n return { exitCode: 1, stderr: message };\r\n }\r\n\r\n const action = connector.actions.find((a) => a.id === actionId);\r\n if (!action) {\r\n return {\r\n exitCode: 1,\r\n stderr: `no action \"${actionId}\" on connector ${connectorId}`,\r\n };\r\n }\r\n\r\n // Build the shared HostCtx. capabilities come from the adapter; an unknown\r\n // adapter still runs with the minimal ctx (an action takes no host payload).\r\n const adapter = await loadAdapter(platformId).catch(() => undefined);\r\n const host: PlatformId = REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)\r\n ? (platformId as PlatformId)\r\n : \"unknown\";\r\n const ctx: HostCtx = {\r\n host,\r\n capabilities: adapter?.capabilities ?? EMPTY_CAPABILITIES,\r\n // Recover the install scope from the registered metadata (sync) — scope is\r\n // an install-time property the entrypoint otherwise has no access to —\r\n // and expose the per-connector telemetry usage accessor (lazy, fail-safe).\r\n scope: readRegisteredMeta(connectorId)?.scope,\r\n telemetry: buildTelemetryAccessor(connectorId),\r\n };\r\n\r\n // Per-host run override: when running for host X, `hosts[X].run` wins over the\r\n // top-level run; a host not listed (or a per-host entry that is somehow not a\r\n // function) falls back to the top-level run.\r\n const perHost = action.hosts?.[platformId as PlatformId]?.run;\r\n const run = typeof perHost === \"function\" ? perHost : action.run;\r\n\r\n try {\r\n const result = await run(ctx);\r\n const message = result?.message;\r\n if (typeof message === \"string\" && message !== \"\") {\r\n return { exitCode: 0, stdout: message };\r\n }\r\n // void / no message → success with no output.\r\n return { exitCode: 0 };\r\n } catch (err) {\r\n // USER-TRIGGERED: a throwing action surfaces its failure (exit 1 + stderr),\r\n // never a silent fail-open. This is the documented difference from\r\n // hooks/statusline.\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n exitCode: 1,\r\n stderr: `action \"${actionId}\" failed: ${message}`,\r\n };\r\n }\r\n}\r\n\r\nexport default runAction;\r\n"],"mappings":"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","names":["args","isObject"]}
@@ -1,25 +1,25 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- log
4
- } from "./chunk-KHO5WNTP.js";
5
2
  import {
6
3
  getTokenizer,
7
4
  inferModelFamily,
8
5
  newRecordId,
9
6
  openStore,
10
7
  worstConfidence
11
- } from "./chunk-IVAWP6HH.js";
8
+ } from "./chunk-IOJDZJ72.js";
9
+ import {
10
+ log
11
+ } from "./chunk-4GH2KA7V.js";
12
12
  import {
13
13
  loadRegisteredConnector,
14
14
  readRegisteredMeta
15
- } from "./chunk-PW42ZXCB.js";
15
+ } from "./chunk-3EP5V2F4.js";
16
16
  import {
17
17
  REGISTERED_PLATFORM_IDS,
18
18
  loadAdapter
19
- } from "./chunk-D4ELPBB5.js";
19
+ } from "./chunk-NDMNMAKP.js";
20
20
  import {
21
21
  projectIdentity
22
- } from "./chunk-GZGLCCEE.js";
22
+ } from "./chunk-QYOUQCC6.js";
23
23
 
24
24
  // src/runtime/telemetry-accessor.ts
25
25
  var ZERO = {
@@ -371,4 +371,4 @@ export {
371
371
  isNativeHookDeclared,
372
372
  runNativeHook
373
373
  };
374
- //# sourceMappingURL=chunk-I5UXHIDT.js.map
374
+ //# sourceMappingURL=chunk-GCPEPUD7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/telemetry-accessor.ts","../src/telemetry/measure.ts","../src/runtime/hook-entrypoint.ts"],"sourcesContent":["/**\r\n * runtime/telemetry-accessor — build the handler-facing {@link TelemetryAccessor}.\r\n *\r\n * Every runtime entrypoint (hook / statusline / action) stamps `ctx.telemetry`\r\n * (or `evt.telemetry`) with the accessor built here so a connector handler can\r\n * read ITS OWN recorded usage on demand (`await ctx.telemetry?.()`), without the\r\n * handler reaching into the telemetry store directly.\r\n *\r\n * The accessor is LAZY (it opens the store only when called) and DEFENSIVE:\r\n * - AGENT_CONNECTOR_TELEMETRY=0 → resolves zeros without touching disk;\r\n * - any read error → resolves zeros (NEVER throws into a handler — the hook\r\n * runtime is fail-open, the statusline fail-safe, and even the action\r\n * surface must not have a usage read crash an otherwise-fine run).\r\n *\r\n * COST: each accessor call, or the first read of a lazy `ctx.usage`, reads the\r\n * connector's telemetry file in full (the NDJSON store has no since/limit bound)\r\n * and linear-scans it. Cheap for normal stores, but a handler on a HOT path\r\n * should call it only when it needs usage.\r\n */\r\n\r\nimport type { TelemetryAccessor, TelemetryUsageSummary } from \"../core/types.js\";\r\nimport { openStore } from \"../telemetry/store.js\";\r\n\r\n/** The empty summary returned when telemetry is off or a read fails. */\r\nconst ZERO: TelemetryUsageSummary = {\r\n inputTokens: 0,\r\n outputTokens: 0,\r\n totalTokens: 0,\r\n calls: 0,\r\n};\r\n\r\n/**\r\n * Synchronously sum every stored row's inputTokens/outputTokens for the\r\n * connector and count rows (`calls`). Honors the AGENT_CONNECTOR_TELEMETRY=0\r\n * kill switch (zeros, no disk touch) and swallows any error (zeros).\r\n */\r\nexport function readTelemetryUsageSummary(connectorId: string): TelemetryUsageSummary {\r\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return { ...ZERO };\r\n try {\r\n const store = openStore({});\r\n try {\r\n let inputTokens = 0;\r\n let outputTokens = 0;\r\n let calls = 0;\r\n // COERCE each addend to a finite number: query() tolerates malformed\r\n // LINES but does not validate field shapes, so a legacy/hand-edited row\r\n // with a missing (→ NaN) or string (→ concat) token field must not\r\n // poison the summary's numeric contract. A non-finite token reads as 0.\r\n for (const row of store.query({ connectorId })) {\r\n inputTokens += Number.isFinite(row.inputTokens) ? row.inputTokens : 0;\r\n outputTokens += Number.isFinite(row.outputTokens) ? row.outputTokens : 0;\r\n calls += 1;\r\n }\r\n return {\r\n inputTokens,\r\n outputTokens,\r\n totalTokens: inputTokens + outputTokens,\r\n calls,\r\n };\r\n } finally {\r\n store.close();\r\n }\r\n } catch {\r\n // Never throw into a handler — a usage read failure resolves to zeros.\r\n return { ...ZERO };\r\n }\r\n}\r\n\r\n/**\r\n * Build a {@link TelemetryAccessor} bound to `connectorId`. The returned async\r\n * function uses {@link readTelemetryUsageSummary}; it never throws.\r\n */\r\nexport function buildTelemetryAccessor(connectorId: string): TelemetryAccessor {\r\n return async (): Promise<TelemetryUsageSummary> => {\r\n return readTelemetryUsageSummary(connectorId);\r\n };\r\n}\r\n\r\n/**\r\n * Install a cached lazy `usage` property on a statusline context. This preserves\r\n * synchronous `ctx.usage` reads for render functions without forcing every\r\n * status refresh to scan the telemetry store when the render never uses usage.\r\n */\r\nexport function attachLazyTelemetryUsage(\r\n ctx: { usage?: TelemetryUsageSummary },\r\n connectorId: string,\r\n read: (connectorId: string) => TelemetryUsageSummary = readTelemetryUsageSummary,\r\n): void {\r\n let cached: TelemetryUsageSummary | undefined;\r\n let hasCached = false;\r\n\r\n Object.defineProperty(ctx, \"usage\", {\r\n configurable: true,\r\n enumerable: true,\r\n get() {\r\n if (!hasCached) {\r\n cached = read(connectorId);\r\n hasCached = true;\r\n }\r\n return cached;\r\n },\r\n set(value: TelemetryUsageSummary | undefined) {\r\n cached = value;\r\n hasCached = true;\r\n },\r\n });\r\n}\r\n","/**\r\n * telemetry/measure — pure measurement helpers over a single MCP tool call.\r\n *\r\n * The framework tokenizes its OWN bytes: the tool-call arguments going in, the\r\n * tool result coming out, and the fixed tool-definition schemas. These helpers\r\n * turn those into {@link TokenCount}s using the default {@link Tokenizer}.\r\n *\r\n * Honesty rules:\r\n * • Binary modalities (image/audio/resource blobs) are NOT tokenized — base64\r\n * would massively over-count. Each gets a flat per-modality estimate, and\r\n * because that estimate is itself an approximation the combined confidence\r\n * is pulled down to at least \"tokenizer-approx\".\r\n * • When combining several counts, the reported confidence is the WORST\r\n * (least-confident) of the inputs — never report \"exact\" for a mixed bag.\r\n *\r\n * Everything here is pure: no IO, no mutation of inputs.\r\n */\r\n\r\nimport { getTokenizer } from \"./tokenizer.js\";\r\nimport { worstConfidence } from \"./types.js\";\r\nimport type {\r\n ConfidenceSource,\r\n ModelFamily,\r\n TokenCount,\r\n Tokenizer,\r\n} from \"./types.js\";\r\n\r\n/** Per-call measurement: tokens in, tokens out, and combined confidence. */\r\nexport interface ToolCallMeasurement {\r\n inputTokens: number;\r\n outputTokens: number;\r\n source: ConfidenceSource;\r\n}\r\n\r\n/**\r\n * Flat token estimates for non-text content blocks. These mirror common host\r\n * accounting (an image tile is roughly ~85 tokens at low detail); they are\r\n * deliberately coarse — we never tokenize the underlying base64 payload.\r\n */\r\nconst MODALITY_ESTIMATE: Record<string, number> = {\r\n image: 85,\r\n audio: 85,\r\n resource: 85,\r\n};\r\n/** Fallback estimate for an unknown non-text block type. */\r\nconst UNKNOWN_BLOCK_ESTIMATE = 85;\r\n\r\n// Confidence ordering + the worst-of comparison live in ./types (the single\r\n// source of truth, imported as `worstConfidence` above). Re-exported here so\r\n// callers that already import `worstConfidence` from this module keep working\r\n// as a new ConfidenceSource value (e.g. tokenizer-calibrated) slots into the\r\n// shared ordering.\r\nexport { worstConfidence };\r\n\r\n/** Reduce a list of sources to the single worst (least-confident) one. */\r\nfunction combineSources(sources: readonly ConfidenceSource[]): ConfidenceSource {\r\n // Default to the most-confident floor so an empty list combines to the only\r\n // other source it is paired with; in practice callers always pass ≥1 source.\r\n let worst: ConfidenceSource = \"host-native\";\r\n for (const s of sources) worst = worstConfidence(worst, s);\r\n return worst;\r\n}\r\n\r\n// ── Structural shapes we read from a tool result (kept local + narrow) ───────\r\n\r\n/** A text content block: `{ type: \"text\", text: \"...\" }`. */\r\ninterface TextBlock {\r\n type: \"text\";\r\n text?: unknown;\r\n}\r\n/** Any content block; only text blocks expose a `text` field we tokenize. */\r\ninterface ContentBlock {\r\n type?: unknown;\r\n text?: unknown;\r\n}\r\n/** The portion of an MCP tool result we measure. */\r\ninterface ToolResultLike {\r\n content?: unknown;\r\n structuredContent?: unknown;\r\n}\r\n\r\nfunction isObject(v: unknown): v is Record<string, unknown> {\r\n return typeof v === \"object\" && v !== null;\r\n}\r\n\r\nfunction isTextBlock(block: ContentBlock): block is TextBlock {\r\n return block.type === \"text\";\r\n}\r\n\r\n/**\r\n * Measure one tool call.\r\n *\r\n * Input = canonical JSON of `args`.\r\n * Output = tokens over the TEXTUAL parts of `result`:\r\n * • If `result.content` is an array of blocks, text blocks are tokenized via\r\n * their `text` field and non-text blocks add a flat per-modality estimate\r\n * (no base64 tokenization).\r\n * • `result.structuredContent`, when present, is tokenized as canonical JSON.\r\n * • Anything else (string / arbitrary object) is tokenized as canonical JSON.\r\n *\r\n * The reported `source` is the worst of the input and output sources.\r\n */\r\nexport function measureToolCall(\r\n args: unknown,\r\n result: unknown,\r\n family: ModelFamily,\r\n tok: Tokenizer = getTokenizer(),\r\n): ToolCallMeasurement {\r\n const input = tok.countValue(args, family);\r\n const output = measureResult(result, family, tok);\r\n return {\r\n inputTokens: input.tokens,\r\n outputTokens: output.tokens,\r\n source: worstConfidence(input.source, output.source),\r\n };\r\n}\r\n\r\n/** Tokenize the textual + structured parts of a tool result. */\r\nfunction measureResult(\r\n result: unknown,\r\n family: ModelFamily,\r\n tok: Tokenizer,\r\n): TokenCount {\r\n if (!isObject(result)) {\r\n // Plain string or scalar result — count its canonical form directly.\r\n return tok.countValue(result, family);\r\n }\r\n\r\n const res = result as ToolResultLike;\r\n const sources: ConfidenceSource[] = [];\r\n let tokens = 0;\r\n\r\n if (Array.isArray(res.content)) {\r\n for (const raw of res.content) {\r\n if (!isObject(raw)) continue;\r\n const block = raw as ContentBlock;\r\n if (isTextBlock(block)) {\r\n const text = typeof block.text === \"string\" ? block.text : \"\";\r\n const c = tok.count(text, family);\r\n tokens += c.tokens;\r\n sources.push(c.source);\r\n } else {\r\n // image / audio / resource / unknown → flat estimate, no base64 BPE.\r\n const key = typeof block.type === \"string\" ? block.type : \"\";\r\n tokens += MODALITY_ESTIMATE[key] ?? UNKNOWN_BLOCK_ESTIMATE;\r\n // A flat estimate is an approximation, never \"exact\".\r\n sources.push(\"tokenizer-approx\");\r\n }\r\n }\r\n } else if (res.content !== undefined) {\r\n // content present but not an array (e.g. a bare string) → count it whole.\r\n const c = tok.countValue(res.content, family);\r\n tokens += c.tokens;\r\n sources.push(c.source);\r\n }\r\n\r\n if (res.structuredContent !== undefined) {\r\n const c = tok.countValue(res.structuredContent, family);\r\n tokens += c.tokens;\r\n sources.push(c.source);\r\n }\r\n\r\n // Nothing measurable found → count the whole object as a fallback so we never\r\n // silently report zero for a non-empty result.\r\n if (sources.length === 0) {\r\n return tok.countValue(result, family);\r\n }\r\n\r\n return { tokens, source: combineSources(sources) };\r\n}\r\n\r\n/**\r\n * Measure the fixed tool-definition overhead: tokenize the JSON of the entire\r\n * `tools/list` array once. `tools` is the raw array of tool descriptors as the\r\n * server advertises them (shape is host/SDK-specific, so it is treated as\r\n * opaque JSON here).\r\n */\r\nexport function measureToolDefs(\r\n tools: readonly unknown[],\r\n family: ModelFamily,\r\n tok: Tokenizer = getTokenizer(),\r\n): TokenCount {\r\n return tok.countValue(tools, family);\r\n}\r\n\r\n/**\r\n * Measure one RUNTIME hook dispatch (the new `hook` developer-axis surface).\r\n *\r\n * Input = canonical JSON of the inbound normalized event payload the handler\r\n * reads (the host-provided fields: toolInput / prompt / raw / etc.).\r\n * Output = canonical JSON of what the handler RETURNS that becomes context or a\r\n * decision the host consumes (additionalContext / updatedInput / reason\r\n * and the rest of the normalized {@link HookResponse}).\r\n *\r\n * Uses the SAME tokenizer + worst-of confidence combination as the proxy. Pure:\r\n * no IO, no mutation. The two values are tokenized whole (a hook payload has no\r\n * binary content blocks to special-case, unlike a tool result).\r\n */\r\nexport function measureHook(\r\n eventPayload: unknown,\r\n handlerReturn: unknown,\r\n family: ModelFamily,\r\n tok: Tokenizer = getTokenizer(),\r\n): ToolCallMeasurement {\r\n const input = tok.countValue(eventPayload, family);\r\n const output = tok.countValue(handlerReturn, family);\r\n return {\r\n inputTokens: input.tokens,\r\n outputTokens: output.tokens,\r\n source: worstConfidence(input.source, output.source),\r\n };\r\n}\r\n","/**\r\n * runtime/hook-entrypoint — the universal json-stdio hook dispatcher.\r\n *\r\n * Every json-stdio host points its hook config at the single stable home binary\r\n * (`<homeBin> hook <platformId> <event> --connector <id>`, built by\r\n * core/spawn.buildHomeBinHookCommand). The CLI reads stdin and calls\r\n * {@link runHook} with the parsed flags + the raw stdin string. This module:\r\n *\r\n * 1. Loads the registered connector (live handlers) and the host adapter.\r\n * 2. Parses the host's stdin payload into a normalized event via the adapter.\r\n * 3. Finds the connector's handler for the event, honoring the tool matcher.\r\n * 4. Runs the handler, normalizes its return, and formats the adapter's native\r\n * reply.\r\n *\r\n * Fail-open is the safety contract: ANY error degrades to exit 0 (\"allow\") so a\r\n * framework or handler bug can never wedge a host's tool call — EXCEPT when the\r\n * event is PreToolUse or PermissionRequest and the handler explicitly denied,\r\n * in which case the deny (a deliberate security decision) is preserved rather\r\n * than swallowed.\r\n */\r\n\r\nimport type {\r\n HookEventName,\r\n HookResponse,\r\n NativeHookDef,\r\n NativeHookEvent,\r\n PlatformId,\r\n ResolvedConnector,\r\n} from \"../core/types.js\";\r\nimport { log } from \"../core/logger.js\";\r\nimport {\r\n loadRegisteredConnector,\r\n readRegisteredMeta,\r\n} from \"../core/load-connector.js\";\r\nimport { loadAdapter, REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\r\nimport { buildTelemetryAccessor } from \"./telemetry-accessor.js\";\r\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\r\nimport { projectIdentity } from \"../core/paths.js\";\r\nimport { measureHook } from \"../telemetry/measure.js\";\r\nimport { openStore, newRecordId } from \"../telemetry/store.js\";\r\nimport { getTokenizer, inferModelFamily } from \"../telemetry/tokenizer.js\";\r\n\r\n/** Flags + stdin the CLI hands to {@link runHook}. */\r\nexport interface RunHookOptions {\r\n /** Host platform id from the hook command (`hook <platformId> ...`). */\r\n platformId: string;\r\n /** Canonical lifecycle event name from the hook command. */\r\n event: HookEventName;\r\n /** Connector id from `--connector <id>`. */\r\n connectorId: string;\r\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\r\n stdin: string;\r\n}\r\n\r\n/** Process-level result the CLI translates into exit code + stdout/stderr. */\r\nexport interface RunHookResult {\r\n exitCode: number;\r\n stdout?: string;\r\n stderr?: string;\r\n}\r\n\r\n/** A pass-through \"allow\" reply with exit 0 and no payload. */\r\nconst ALLOW: RunHookResult = { exitCode: 0 };\r\n\r\n/**\r\n * The string a connector matcher filters on, when the event carries one:\r\n * tool events (PreToolUse / PostToolUse / PermissionRequest /\r\n * PostToolUseFailure) expose `toolName`; subagent events expose `agentType`.\r\n * Returns undefined when the event has neither (no filtering) — including a\r\n * SubagentStop arriving WITHOUT agent_type, which some hosts fail to populate;\r\n * filtering out such an event would silently drop a real stop, so we run the\r\n * handler instead (fail-open).\r\n */\r\nexport function eventMatcherSubject(evt: NormalizedEvent): string | undefined {\r\n const name = (evt as { toolName?: unknown }).toolName;\r\n if (typeof name === \"string\") return name;\r\n const agentType = (evt as { agentType?: unknown }).agentType;\r\n if (typeof agentType === \"string\") return agentType;\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Tolerantly parse the host's stdin payload. Empty / whitespace-only input is a\r\n * legitimate \"no payload\" signal from some hosts and resolves to `{}`. Malformed\r\n * JSON also degrades to `{}` rather than throwing — fail-open is the contract.\r\n */\r\nfunction parseStdin(stdin: string): unknown {\r\n const trimmed = stdin.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\r\n/**\r\n * Compile the connector's matcher (a regex string) for a tool event. A missing\r\n * or empty matcher matches everything (returns null → no filtering). An invalid\r\n * regex degrades to \"match everything\" so a typo never blocks tool calls.\r\n */\r\nexport function compileMatcher(matcher: string | undefined): RegExp | null {\r\n if (!matcher || matcher === \"\") return null;\r\n try {\r\n return new RegExp(matcher);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Normalize a handler return into a concrete HookResponse. A void/non-object\r\n * return becomes `{}` — NO decision — which every adapter formats as\r\n * pass-through allow (`response.decision ?? \"allow\"`). Deliberately NOT\r\n * `{decision:\"allow\"}`: on PermissionRequest an explicit \"allow\" is an ACTIVE\r\n * grant that suppresses the host's permission dialog, and a handler that\r\n * returns nothing must fall through to the dialog, never silently grant.\r\n */\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 * Resolve the host platform to stamp on a hook telemetry row. Mirrors the serve\r\n * `--host` plumbing: the install-target platform id is baked into the hook\r\n * command (`hook <platformId> <event> …`) so `opts.platformId` is authoritative,\r\n * but an explicit env override (AGENT_CONNECTOR_HOST) wins when it names a known\r\n * platform. Falls back to the event's adapter-stamped hostPlatform, then\r\n * \"unknown\" — never throws.\r\n */\r\nfunction resolveHookHostPlatform(\r\n platformId: string,\r\n evt: NormalizedEvent | NativeHookEvent,\r\n): PlatformId {\r\n const override = process.env.AGENT_CONNECTOR_HOST;\r\n if (\r\n override !== undefined &&\r\n REGISTERED_PLATFORM_IDS.has(override as PlatformId)\r\n ) {\r\n return override as PlatformId;\r\n }\r\n if (REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)) {\r\n return platformId as PlatformId;\r\n }\r\n const fromEvent = (evt as { hostPlatform?: unknown }).hostPlatform;\r\n if (\r\n typeof fromEvent === \"string\" &&\r\n REGISTERED_PLATFORM_IDS.has(fromEvent as PlatformId)\r\n ) {\r\n return fromEvent as PlatformId;\r\n }\r\n return \"unknown\";\r\n}\r\n\r\n/**\r\n * Record one RUNTIME hook dispatch as a `hook` developer-axis telemetry row.\r\n *\r\n * input = the inbound event payload the handler reads (normalized event, or\r\n * the {@link NativeHookEvent} envelope for native passthrough hooks);\r\n * output = what the handler returned (a normalized {@link HookResponse}, or the\r\n * verbatim native reply object). Tokenized with the SAME tokenizer the\r\n * proxy uses. For a native hook `opts.event` is the host-native name.\r\n *\r\n * MUST be fail-open (the hook runtime is fail-open by contract): any error here\r\n * is swallowed so a measurement bug can NEVER break a host's hook. Honors the\r\n * AGENT_CONNECTOR_TELEMETRY=0 kill switch (skips entirely; the store's append is\r\n * already a no-op under it, but we also skip the tokenize work).\r\n */\r\nfunction recordHookTelemetry(\r\n opts: { platformId: string; event: string; connectorId: string },\r\n connector: ResolvedConnector,\r\n evt: NormalizedEvent | NativeHookEvent,\r\n response: unknown,\r\n): void {\r\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return;\r\n try {\r\n const family = inferModelFamily(\"\", connector.telemetry.modelFamilyHint);\r\n const measurement = measureHook(evt, response, family, getTokenizer());\r\n\r\n const sessionId =\r\n typeof (evt as { sessionId?: unknown }).sessionId === \"string\"\r\n ? ((evt as { sessionId: string }).sessionId)\r\n : \"\";\r\n const evtProjectDir = (evt as { projectDir?: unknown }).projectDir;\r\n const projectDir =\r\n typeof evtProjectDir === \"string\" && evtProjectDir !== \"\"\r\n ? evtProjectDir\r\n : process.cwd();\r\n const id = projectIdentity(projectDir);\r\n\r\n const store = openStore({});\r\n try {\r\n store.append({\r\n id: newRecordId(0),\r\n ts: Date.now(),\r\n connectorId: opts.connectorId,\r\n toolName: opts.event, // for a hook row the per-item name IS the event\r\n scope: \"hook\",\r\n surfaceKind: \"hook\",\r\n hostPlatform: resolveHookHostPlatform(opts.platformId, evt),\r\n sessionId,\r\n projectKey: id.key,\r\n projectDir: id.dir,\r\n inputTokens: measurement.inputTokens,\r\n outputTokens: measurement.outputTokens,\r\n confidenceSource: measurement.source,\r\n isError: false,\r\n });\r\n } finally {\r\n store.close();\r\n }\r\n } catch {\r\n // Fail-open: a telemetry error must NEVER break the hook runtime.\r\n }\r\n}\r\n\r\n/**\r\n * Dispatch one host hook invocation. Always resolves (never rejects): every\r\n * failure path returns a concrete {@link RunHookResult} so the CLI can exit\r\n * cleanly. See the module header for the fail-open contract.\r\n */\r\nexport async function runHook(opts: RunHookOptions): Promise<RunHookResult> {\r\n const { platformId, event, connectorId, stdin } = opts;\r\n\r\n try {\r\n const connector = await loadRegisteredConnector(connectorId);\r\n const adapter = await loadAdapter(platformId);\r\n\r\n // mcp-only hosts (and any adapter without runtime dispatch) cannot parse a\r\n // hook payload — nothing to do, allow.\r\n if (!adapter || !adapter.parseEvent || !adapter.formatReply) {\r\n return ALLOW;\r\n }\r\n\r\n const raw = parseStdin(stdin);\r\n const evt = adapter.parseEvent(event, raw);\r\n // The hook command carries the authoritative connector id; stamp it on the\r\n // event so handlers see the connector they were dispatched for.\r\n evt.connectorId = connectorId;\r\n // Unify the hook event with HostCtx: the adapter's parseEvent has no\r\n // capabilities/scope/telemetry at parse time (they are runtime-only), so the\r\n // runtime ALWAYS backfills them before the handler runs. capabilities comes\r\n // from the adapter; scope is recovered from the registered metadata (sync);\r\n // telemetry is the lazy per-connector usage accessor. The SAME three\r\n // assignments run on the error path (failOpenOrPreserveDeny) for symmetry.\r\n evt.capabilities = adapter.capabilities;\r\n evt.scope = readRegisteredMeta(connectorId)?.scope;\r\n evt.telemetry = buildTelemetryAccessor(connectorId);\r\n\r\n const definition = connector.hooks[event];\r\n if (!definition || typeof definition.handler !== \"function\") {\r\n // The connector declares no handler for this event — allow.\r\n return ALLOW;\r\n }\r\n\r\n // Per-host handler override: when dispatching for host X, `hosts[X].handler`\r\n // wins over the top-level handler; a host not listed (or a per-host entry\r\n // that is somehow not a function) falls back to the top-level handler.\r\n // Selection NEVER throws — fail-open is preserved.\r\n // NOTE: keyed on the RAW install-baked platformId (the host this entrypoint\r\n // was registered for), NOT resolveHookHostPlatform's AGENT_CONNECTOR_HOST\r\n // override — that override only re-labels the telemetry row. The error path\r\n // (failOpenOrPreserveDeny) resolves the IDENTICAL handler the same way, so a\r\n // per-host deny is preserved consistently.\r\n const perHost = definition.hosts?.[platformId as PlatformId]?.handler;\r\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\r\n\r\n // Matcher: if set and the event's subject (tool name, or agent type for\r\n // Subagent* events) does not match, allow (the hook is simply not\r\n // interested in this tool/agent). The matcher is the definition's — a\r\n // per-host handler shares the top-level matcher.\r\n const subject = eventMatcherSubject(evt);\r\n if (subject !== undefined) {\r\n const re = compileMatcher(definition.matcher);\r\n if (re && !re.test(subject)) return ALLOW;\r\n }\r\n\r\n // Run the resolved handler. Its return is normalized (void → allow) and\r\n // handed to the adapter to render the host-native reply.\r\n const handlerResult = await handler(\r\n // The adapter parsed `evt` as exactly this event's payload type; the\r\n // HooksConfig handler for the same event expects that payload.\r\n evt as never,\r\n );\r\n const response = normalizeResponse(handlerResult);\r\n\r\n // Developer-axis telemetry for the RUNTIME `hook` surface. Fail-open: never\r\n // lets a measurement error break the hook (handled inside the helper).\r\n recordHookTelemetry(opts, connector, evt, response);\r\n\r\n const reply = adapter.formatReply(event, response);\r\n return {\r\n exitCode: reply.exitCode,\r\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\r\n ...(reply.stderr !== undefined ? { stderr: reply.stderr } : {}),\r\n };\r\n } catch (err) {\r\n // Fail-open: a framework/handler error must not wedge the host's tool call.\r\n // The ONE exception is a PreToolUse / PermissionRequest deny — a deliberate\r\n // block we honor even if a later step threw. We re-derive that decision\r\n // defensively from the adapter so a throw AFTER the deny still surfaces it.\r\n const message = err instanceof Error ? err.message : String(err);\r\n log.error(`hook ${platformId}/${event} (${connectorId}) failed:`, message);\r\n return failOpenOrPreserveDeny(opts, message);\r\n }\r\n}\r\n\r\n/**\r\n * Events whose explicit deny survives the fail-open error path. Both are\r\n * permission gates — a deny there is a deliberate security decision that must\r\n * never be silently downgraded to allow. (Stop-class denies are persistence\r\n * conveniences, not security boundaries, so they stay fail-open.)\r\n */\r\nconst DENY_PRESERVE_EVENTS: ReadonlySet<HookEventName> = new Set([\r\n \"PreToolUse\",\r\n \"PermissionRequest\",\r\n]);\r\n\r\n/**\r\n * Error path. Returns fail-open allow, UNLESS this is a deny-preserving event\r\n * (PreToolUse / PermissionRequest) whose handler explicitly denied — in which\r\n * case the deny is reconstructed and preserved (a security decision is never\r\n * silently downgraded to allow).\r\n *\r\n * Best-effort: if anything in the deny reconstruction itself throws, we fall\r\n * back to allow (we never escalate an error into a spurious block).\r\n */\r\nasync function failOpenOrPreserveDeny(\r\n opts: RunHookOptions,\r\n errorMessage: string,\r\n): Promise<RunHookResult> {\r\n if (!DENY_PRESERVE_EVENTS.has(opts.event)) return ALLOW;\r\n try {\r\n const connector = await loadRegisteredConnector(opts.connectorId);\r\n const adapter = await loadAdapter(opts.platformId);\r\n if (!adapter?.parseEvent || !adapter.formatReply) return ALLOW;\r\n\r\n const definition = connector.hooks[opts.event];\r\n if (!definition || typeof definition.handler !== \"function\") return ALLOW;\r\n\r\n // Reconstruct the deny from the SAME handler the main path would have run —\r\n // the per-host override if one is declared for this host, else the top-level\r\n // handler. Otherwise a per-host deny could be silently downgraded on the\r\n // error path.\r\n const perHost = definition.hosts?.[opts.platformId as PlatformId]?.handler;\r\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\r\n\r\n const raw = parseStdin(opts.stdin);\r\n const evt = adapter.parseEvent(opts.event, raw);\r\n evt.connectorId = opts.connectorId;\r\n // Backfill the HostCtx fields here too (symmetry with the main path), so a\r\n // per-host deny reconstructed on the error path still carries capabilities/\r\n // scope/telemetry — a handler that branches on them denies identically.\r\n evt.capabilities = adapter.capabilities;\r\n evt.scope = readRegisteredMeta(opts.connectorId)?.scope;\r\n evt.telemetry = buildTelemetryAccessor(opts.connectorId);\r\n\r\n const subject = eventMatcherSubject(evt);\r\n if (subject !== undefined) {\r\n const re = compileMatcher(definition.matcher);\r\n if (re && !re.test(subject)) return ALLOW;\r\n }\r\n\r\n const response = normalizeResponse(await handler(evt as never));\r\n if (response.decision !== \"deny\") return ALLOW;\r\n\r\n const reply = adapter.formatReply(opts.event, response);\r\n return {\r\n exitCode: reply.exitCode,\r\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\r\n ...(reply.stderr !== undefined\r\n ? { stderr: reply.stderr }\r\n : { stderr: errorMessage }),\r\n };\r\n } catch {\r\n // Reconstruction failed — never escalate an internal error into a block.\r\n return ALLOW;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Native (passthrough) hooks — host events OUTSIDE the normalized union\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Flags + stdin for {@link runNativeHook}. `event` is HOST-NATIVE (non-union). */\r\nexport interface RunNativeHookOptions {\r\n /** Host platform id from the hook command (`hook <platformId> ...`). */\r\n platformId: string;\r\n /** Host-native event name, verbatim (e.g. \"TaskCreated\"). */\r\n event: string;\r\n /** Connector id from `--connector <id>`. */\r\n connectorId: string;\r\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\r\n stdin: string;\r\n}\r\n\r\n/** The connector's native hook definition for `platformId`/`event`, if any. */\r\nfunction nativeHookDef(\r\n connector: ResolvedConnector,\r\n platformId: string,\r\n event: string,\r\n): NativeHookDef | undefined {\r\n return connector.platforms[platformId as PlatformId]?.nativeHooks?.[event];\r\n}\r\n\r\n/**\r\n * True when the registered connector declares `event` under\r\n * `platforms[platformId].nativeHooks` with a live handler. Used by the hook CLI\r\n * to decide whether a non-union event name is accepted (declared → dispatch via\r\n * {@link runNativeHook}) or rejected with the strict unknown-event error.\r\n * Fail-safe: any load error reads as \"not declared\".\r\n */\r\nexport async function isNativeHookDeclared(\r\n platformId: string,\r\n event: string,\r\n connectorId: string,\r\n): Promise<boolean> {\r\n try {\r\n const connector = await loadRegisteredConnector(connectorId);\r\n return typeof nativeHookDef(connector, platformId, event)?.handler === \"function\";\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Dispatch one NATIVE (passthrough) hook invocation. No normalized parse, no\r\n * HookResponse mapping, no runtime matcher evaluation (the host's own matcher\r\n * already filtered — the def's matcher string was written verbatim into the\r\n * host config at install):\r\n *\r\n * stdin JSON → NativeHookEvent{raw} → handler → VERBATIM JSON stdout (exit 0).\r\n *\r\n * void/undefined return → exit 0 with no output. Fail-open: ANY throw degrades\r\n * to exit 0 with no output — exit-2 blocking semantics are not modeled in v1.\r\n * Telemetry: records the same scope:\"hook\" developer-axis row as normalized\r\n * hooks, with the NATIVE event name as the per-item name.\r\n */\r\nexport async function runNativeHook(\r\n opts: RunNativeHookOptions,\r\n): Promise<RunHookResult> {\r\n const { platformId, event, connectorId, stdin } = opts;\r\n try {\r\n const connector = await loadRegisteredConnector(connectorId);\r\n const def = nativeHookDef(connector, platformId, event);\r\n if (!def || typeof def.handler !== \"function\") {\r\n // Not declared for this platform — nothing to do, allow.\r\n return ALLOW;\r\n }\r\n\r\n const raw = parseStdin(stdin);\r\n // Best-effort session/project extraction from the json-stdio common shape\r\n // (Claude Code: session_id / cwd). Everything else stays in `raw` untouched.\r\n const sessionId =\r\n typeof (raw as { session_id?: unknown })?.session_id === \"string\"\r\n ? (raw as { session_id: string }).session_id\r\n : \"\";\r\n const cwd = (raw as { cwd?: unknown })?.cwd;\r\n const evt: NativeHookEvent = {\r\n event,\r\n hostPlatform: REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)\r\n ? (platformId as PlatformId)\r\n : \"unknown\",\r\n sessionId,\r\n ...(typeof cwd === \"string\" && cwd !== \"\" ? { projectDir: cwd } : {}),\r\n raw,\r\n };\r\n\r\n const result = await def.handler(evt);\r\n\r\n // Same scope:\"hook\" telemetry row as normalized hooks (fail-open inside).\r\n recordHookTelemetry(opts, connector, evt, result === undefined ? {} : result);\r\n\r\n if (result === undefined) return ALLOW; // void → exit 0, no output\r\n const json = JSON.stringify(result);\r\n // Non-JSON-serializable returns (functions/symbols) degrade to silence.\r\n if (json === undefined) return ALLOW;\r\n return { exitCode: 0, stdout: json };\r\n } catch (err) {\r\n // Fail-open, unconditionally: native hooks have no deny-preserve carve-out\r\n // (there is no normalized decision to reconstruct).\r\n const message = err instanceof Error ? err.message : String(err);\r\n log.error(\r\n `native hook ${platformId}/${event} (${connectorId}) failed:`,\r\n message,\r\n );\r\n return ALLOW;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ADAPTER_REGISTRY
4
- } from "./chunk-D4ELPBB5.js";
4
+ } from "./chunk-NDMNMAKP.js";
5
5
 
6
6
  // src/adapters/detect.ts
7
7
  async function detectInstalledPlatforms(projectDir) {
@@ -85,4 +85,4 @@ export {
85
85
  detectInstalledPlatforms,
86
86
  detectRuntimeHost
87
87
  };
88
- //# sourceMappingURL=chunk-ISEJWBFF.js.map
88
+ //# sourceMappingURL=chunk-GZZCHJDP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/detect.ts"],"sourcesContent":["/**\r\n * adapters/detect — two complementary detection answers.\r\n *\r\n * 1. detectInstalledPlatforms(projectDir): which hosts are INSTALLED on this\r\n * machine (probe each adapter's config dirs / marker files). Drives the\r\n * install planner (\"auto\" target expansion, `doctor`).\r\n *\r\n * 2. detectRuntimeHost(env): which host is EXECUTING right now (read env\r\n * markers the host runtime injects into its child processes). Drives the\r\n * universal hook entrypoint — it must know which adapter's wire format the\r\n * stdin payload is in before it can parse it.\r\n *\r\n * Both derive their platform set from `ADAPTER_REGISTRY` (the single source of\r\n * truth in registry.ts), generalizing context-mode's detection cascade\r\n * (env-var tier → config-dir tier → low-confidence fallback) to our\r\n * Adapter/AdapterFactory contract. The config-dir tier is delegated to each\r\n * adapter's `detectInstalled()` rather than duplicated here, so adding a\r\n * platform never requires editing this file.\r\n */\r\n\r\nimport type { DetectedPlatform, DetectionSignal, PlatformId } from \"../core/types.js\";\r\nimport { ADAPTER_REGISTRY, loadAdapter } from \"./registry.js\";\r\n\r\n/**\r\n * Detect every platform installed on this machine.\r\n *\r\n * Loads each registered adapter and calls its `detectInstalled(projectDir)`\r\n * (which probes the platform's native config dirs + marker files), returning\r\n * only those reported `installed === true`, in registry order. Adapters never\r\n * throw from detection, but we guard each call so one misbehaving adapter\r\n * cannot abort the whole probe.\r\n *\r\n * @param projectDir Resolved project root — lets project-scoped adapters look\r\n * for `<projectDir>/.mcp.json`, `<projectDir>/.cursor`, etc.\r\n */\r\nexport async function detectInstalledPlatforms(\r\n projectDir: string,\r\n): Promise<DetectedPlatform[]> {\r\n const adapters = await Promise.all(\r\n ADAPTER_REGISTRY.map(async (factory) => {\r\n try {\r\n return await factory.load();\r\n } catch {\r\n return undefined;\r\n }\r\n }),\r\n );\r\n\r\n const detected: DetectedPlatform[] = [];\r\n for (const adapter of adapters) {\r\n if (!adapter) continue;\r\n try {\r\n const result = adapter.detectInstalled(projectDir);\r\n if (result.installed) detected.push(result);\r\n } catch {\r\n // A single adapter's detection failure must not sink the whole probe.\r\n }\r\n }\r\n return detected;\r\n}\r\n\r\n/**\r\n * One env-marker rule for runtime-host detection. `vars` is checked in order;\r\n * the FIRST rule with any non-empty matching env var wins, so the table order\r\n * is the detection precedence (forks before parents). Each var is documented at\r\n * the rule so the source of the marker is auditable.\r\n */\r\ninterface RuntimeSignalRule {\r\n readonly platform: PlatformId;\r\n /** Env var names that prove \"this host is executing me\". */\r\n readonly vars: readonly string[];\r\n /** Human-readable note (printed in the DetectionSignal.reason). */\r\n readonly note: string;\r\n}\r\n\r\n/**\r\n * Ordered env-marker table. Forks BEFORE parents (cursor — a VS Code fork —\r\n * precedes any future vscode-copilot entry so a Cursor session inside a\r\n * VS Code-derived shell is not misclassified). Markers are verified against\r\n * each host's source:\r\n *\r\n * - codebuddy: CODEBUDDY_SESSION_ID / CODEBUDDY_PROJECT_DIR / CODEBUDDY_PLUGIN_ROOT\r\n * are CodeBuddy-exclusive (verified in @tencent-ai/codebuddy-code@2.109.0).\r\n * CodeBuddy is a Claude Code fork that ALSO exports CLAUDE_SESSION_ID\r\n * (its bundle sets both), so it MUST precede claude-code or a CodeBuddy\r\n * session would be misclassified as Claude Code (fork-before-parent).\r\n * - claude-code: CLAUDE_CODE_ENTRYPOINT is set on every Claude Code session;\r\n * CLAUDE_PLUGIN_ROOT is set when a plugin is loaded. Both are CC-exclusive\r\n * and serve as the disambiguators when CC runs inside another IDE's shell.\r\n * - codex: CODEX_THREAD_ID is set per exec (codex-rs/core exec_env);\r\n * CODEX_CI is set in CI mode (unified_exec process_manager).\r\n * - cursor: CURSOR_TRACE_ID is the widely-observed Cursor marker (Cursor\r\n * CLI / agent); CURSOR_CLI marks the Cursor terminal; CURSOR_CWD is the\r\n * documented workspace var.\r\n */\r\nconst RUNTIME_SIGNALS: readonly RuntimeSignalRule[] = [\r\n {\r\n platform: \"codebuddy\",\r\n vars: [\"CODEBUDDY_SESSION_ID\", \"CODEBUDDY_PROJECT_DIR\", \"CODEBUDDY_PLUGIN_ROOT\"],\r\n note: \"CodeBuddy env marker set\",\r\n },\r\n {\r\n platform: \"claude-code\",\r\n vars: [\"CLAUDE_CODE_ENTRYPOINT\", \"CLAUDE_PLUGIN_ROOT\", \"CLAUDE_PROJECT_DIR\", \"CLAUDE_SESSION_ID\"],\r\n note: \"Claude Code env marker set\",\r\n },\r\n {\r\n platform: \"cursor\",\r\n vars: [\"CURSOR_TRACE_ID\", \"CURSOR_CLI\", \"CURSOR_CWD\"],\r\n note: \"Cursor env marker set\",\r\n },\r\n {\r\n platform: \"codex\",\r\n vars: [\"CODEX_THREAD_ID\", \"CODEX_CI\"],\r\n note: \"Codex env marker set\",\r\n },\r\n];\r\n\r\n/**\r\n * Identify which host runtime is executing the current process from environment\r\n * markers. High confidence on an env-marker hit; otherwise an explicit\r\n * `AGENT_CONNECTOR_PLATFORM` override (high confidence, validated against the\r\n * registry) is honored; failing both, returns `unknown` at low confidence so\r\n * the caller can decide on a safe default.\r\n *\r\n * Pure with respect to its `env` argument (defaults to `process.env`) so it is\r\n * trivially testable — pass a synthetic env to assert each rule.\r\n */\r\nexport function detectRuntimeHost(env: NodeJS.ProcessEnv = process.env): DetectionSignal {\r\n // ── Explicit override (highest priority, validated against the registry) ──\r\n const override = env.AGENT_CONNECTOR_PLATFORM;\r\n if (override && override.trim() !== \"\") {\r\n if (REGISTERED_RUNTIME_IDS.has(override as PlatformId)) {\r\n return {\r\n platform: override as PlatformId,\r\n confidence: \"high\",\r\n reason: `AGENT_CONNECTOR_PLATFORM=${override} override`,\r\n };\r\n }\r\n }\r\n\r\n // ── Env-marker tier (forks before parents; first hit wins) ──────────────\r\n for (const rule of RUNTIME_SIGNALS) {\r\n const hit = rule.vars.find((name) => {\r\n const value = env[name];\r\n return typeof value === \"string\" && value !== \"\";\r\n });\r\n if (hit) {\r\n return {\r\n platform: rule.platform,\r\n confidence: \"high\",\r\n reason: `${rule.note} (${hit})`,\r\n };\r\n }\r\n }\r\n\r\n // ── Fallback: no host marker present ────────────────────────────────────\r\n return {\r\n platform: \"unknown\",\r\n confidence: \"low\",\r\n reason: \"no host runtime env marker detected\",\r\n };\r\n}\r\n\r\n/** Platform ids the runtime-signal table knows how to detect. */\r\nconst REGISTERED_RUNTIME_IDS: ReadonlySet<PlatformId> = new Set(\r\n RUNTIME_SIGNALS.map((rule) => rule.platform),\r\n);\r\n\r\n// Re-export for callers that resolve an adapter straight from a detection\r\n// result without reaching into registry.ts.\r\nexport { loadAdapter };\r\n"],"mappings":";;;;;;AAmCA,eAAsB,yBACpB,YAC6B;AAC7B,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC7B,iBAAiB,IAAI,OAAO,YAAY;AACtC,UAAI;AACF,eAAO,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,WAA+B,CAAC;AACtC,aAAW,WAAW,UAAU;AAC9B,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,SAAS,QAAQ,gBAAgB,UAAU;AACjD,UAAI,OAAO,UAAW,UAAS,KAAK,MAAM;AAAA,IAC5C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAoCA,IAAM,kBAAgD;AAAA,EACpD;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,wBAAwB,yBAAyB,uBAAuB;AAAA,IAC/E,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,0BAA0B,sBAAsB,sBAAsB,mBAAmB;AAAA,IAChG,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,cAAc,YAAY;AAAA,IACpD,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,MAAM,CAAC,mBAAmB,UAAU;AAAA,IACpC,MAAM;AAAA,EACR;AACF;AAYO,SAAS,kBAAkB,MAAyB,QAAQ,KAAsB;AAEvF,QAAM,WAAW,IAAI;AACrB,MAAI,YAAY,SAAS,KAAK,MAAM,IAAI;AACtC,QAAI,uBAAuB,IAAI,QAAsB,GAAG;AACtD,aAAO;AAAA,QACL,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,QAAQ,4BAA4B,QAAQ;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAGA,aAAW,QAAQ,iBAAiB;AAClC,UAAM,MAAM,KAAK,KAAK,KAAK,CAAC,SAAS;AACnC,YAAM,QAAQ,IAAI,IAAI;AACtB,aAAO,OAAO,UAAU,YAAY,UAAU;AAAA,IAChD,CAAC;AACD,QAAI,KAAK;AACP,aAAO;AAAA,QACL,UAAU,KAAK;AAAA,QACf,YAAY;AAAA,QACZ,QAAQ,GAAG,KAAK,IAAI,KAAK,GAAG;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV;AACF;AAGA,IAAM,yBAAkD,IAAI;AAAA,EAC1D,gBAAgB,IAAI,CAAC,SAAS,KAAK,QAAQ;AAC7C;","names":[]}