@ken-jo/agent-connector 0.4.95 → 0.4.98

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 (385) 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-5JM23CFJ.js} +16 -16
  5. package/dist/action-5JM23CFJ.js.map +1 -0
  6. package/dist/{amazon-q-ETMAGY3D.js → amazon-q-LFZNGN27.js} +6 -6
  7. package/dist/amazon-q-LFZNGN27.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-S22XTPEQ.js} +7 -7
  11. package/dist/amp-S22XTPEQ.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-PZXYAO3X.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-PN474FXM.js} +9 -9
  18. package/dist/antigravity-cli-PN474FXM.js.map +1 -0
  19. package/dist/audit-2H7E3Z4N.js +311 -0
  20. package/dist/audit-2H7E3Z4N.js.map +1 -0
  21. package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
  22. package/dist/chunk-4GH2KA7V.js.map +1 -0
  23. package/dist/{chunk-7F5KHLEP.js → chunk-6XWORQM6.js} +4 -4
  24. package/dist/chunk-6XWORQM6.js.map +1 -0
  25. package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
  26. package/dist/chunk-7C2IPIIB.js.map +1 -0
  27. package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
  28. package/dist/chunk-7HHW23AC.js.map +1 -0
  29. package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
  30. package/dist/chunk-7PSNXUFK.js.map +1 -0
  31. package/dist/{chunk-YPAXJ7EW.js → chunk-7VSZXOXJ.js} +13 -13
  32. package/dist/chunk-7VSZXOXJ.js.map +1 -0
  33. package/dist/{chunk-ISEJWBFF.js → chunk-B6PKHS5I.js} +2 -2
  34. package/dist/chunk-B6PKHS5I.js.map +1 -0
  35. package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
  36. package/dist/chunk-C747DYNT.js.map +1 -0
  37. package/dist/{chunk-PW42ZXCB.js → chunk-DOPFBUCX.js} +3 -3
  38. package/dist/chunk-DOPFBUCX.js.map +1 -0
  39. package/dist/{chunk-I5UXHIDT.js → chunk-DWZ5IWF4.js} +8 -8
  40. package/dist/chunk-DWZ5IWF4.js.map +1 -0
  41. package/dist/{chunk-6WZODR3Z.js → chunk-FJ6F5U3W.js} +5 -5
  42. package/dist/chunk-FJ6F5U3W.js.map +1 -0
  43. package/dist/{chunk-H33M77DD.js → chunk-HATCOSQT.js} +22 -19
  44. package/dist/chunk-HATCOSQT.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-3NH2ARIS.js → chunk-KTBAB556.js} +6 -6
  54. package/dist/chunk-KTBAB556.js.map +1 -0
  55. package/dist/{chunk-5IOTP7LQ.js → chunk-KWC5DQ5Z.js} +7 -5
  56. package/dist/chunk-KWC5DQ5Z.js.map +1 -0
  57. package/dist/{chunk-JKKU2WKG.js → chunk-LD5HAN6O.js} +4 -4
  58. package/dist/chunk-LD5HAN6O.js.map +1 -0
  59. package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
  60. package/dist/chunk-LFWYYWLV.js.map +1 -0
  61. package/dist/{chunk-APZDTY76.js → chunk-LZL2YITN.js} +7 -7
  62. package/dist/chunk-LZL2YITN.js.map +1 -0
  63. package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
  64. package/dist/chunk-PSGTHJQU.js.map +1 -0
  65. package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
  66. package/dist/chunk-Q4ISQHUQ.js.map +1 -0
  67. package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
  68. package/dist/chunk-Q7PR2WGT.js.map +1 -0
  69. package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
  70. package/dist/chunk-QYOUQCC6.js.map +1 -0
  71. package/dist/{chunk-Z2MRSS2I.js → chunk-RNVIK5YJ.js} +2 -2
  72. package/dist/chunk-RNVIK5YJ.js.map +1 -0
  73. package/dist/{chunk-XC5SJ35F.js → chunk-RPWD5H6Y.js} +5 -5
  74. package/dist/chunk-RPWD5H6Y.js.map +1 -0
  75. package/dist/{chunk-PAPMUAR5.js → chunk-S25MVWBP.js} +12 -12
  76. package/dist/chunk-S25MVWBP.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-D4ELPBB5.js → chunk-TOSNS2BY.js} +43 -43
  80. package/dist/chunk-TOSNS2BY.js.map +1 -0
  81. package/dist/{chunk-MGTDQLJJ.js → chunk-UNU2KPYJ.js} +11 -9
  82. package/dist/chunk-UNU2KPYJ.js.map +1 -0
  83. package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
  84. package/dist/chunk-UZZTWWGG.js.map +1 -0
  85. package/dist/{chunk-4NHXLXSD.js → chunk-W7AGWAMH.js} +2 -2
  86. package/dist/chunk-W7AGWAMH.js.map +1 -0
  87. package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
  88. package/dist/chunk-XN7S44U4.js.map +1 -0
  89. package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
  90. package/dist/chunk-XXLCMSD3.js.map +1 -0
  91. package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
  92. package/dist/chunk-XZWMJLIT.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-TA53454O.js} +13 -13
  98. package/dist/claude-code-TA53454O.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-GZFN32BM.js} +6 -6
  104. package/dist/cline-GZFN32BM.js.map +1 -0
  105. package/dist/{codebuddy-ZNYQACMF.js → codebuddy-IYS3TQZ3.js} +7 -7
  106. package/dist/codebuddy-IYS3TQZ3.js.map +1 -0
  107. package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
  108. package/dist/codebuff-6KWBW6CT.js.map +1 -0
  109. package/dist/{codebuff-VR6JUDIY.js → codebuff-D3MIKJHC.js} +6 -6
  110. package/dist/codebuff-D3MIKJHC.js.map +1 -0
  111. package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
  112. package/dist/codex-DKBAAUCO.js.map +1 -0
  113. package/dist/{codex-CPNKTXG3.js → codex-ZKLP5IXZ.js} +9 -9
  114. package/dist/codex-ZKLP5IXZ.js.map +1 -0
  115. package/dist/{continue-WU2IB5KS.js → continue-I34FNSTJ.js} +8 -8
  116. package/dist/continue-I34FNSTJ.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-NWPM5GE6.js} +7 -7
  120. package/dist/copilot-cli-NWPM5GE6.js.map +1 -0
  121. package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
  122. package/dist/crush-A5C5USRL.js.map +1 -0
  123. package/dist/{crush-3AVQA4YU.js → crush-MDGPZSZU.js} +7 -7
  124. package/dist/crush-MDGPZSZU.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-JRVK56QA.js +22 -0
  128. package/dist/{define-connector-DTWb46UB.d.ts → define-connector-B7tLHVnx.d.ts} +1 -1
  129. package/dist/{detect-6V4LC3MA.js → detect-FKJC36P4.js} +4 -4
  130. package/dist/detect-FKJC36P4.js.map +1 -0
  131. package/dist/{devin-QMEM3OFA.js → devin-TZMRC57O.js} +7 -7
  132. package/dist/devin-TZMRC57O.js.map +1 -0
  133. package/dist/{doctor-2YDUVSAU.js → doctor-PGVYUQYG.js} +26 -26
  134. package/dist/doctor-PGVYUQYG.js.map +1 -0
  135. package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
  136. package/dist/droid-PFQ2PZSF.js.map +1 -0
  137. package/dist/{droid-MOKV53J4.js → droid-SCFWDJR4.js} +7 -7
  138. package/dist/droid-SCFWDJR4.js.map +1 -0
  139. package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-MLDGOR2L.js} +8 -8
  140. package/dist/gemini-cli-MLDGOR2L.js.map +1 -0
  141. package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
  142. package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
  143. package/dist/{goose-4EI5MYTI.js → goose-5DBHDSJW.js} +9 -9
  144. package/dist/goose-5DBHDSJW.js.map +1 -0
  145. package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
  146. package/dist/goose-K2HKUTBK.js.map +1 -0
  147. package/dist/{grok-cli-QYQPDSVB.js → grok-cli-LX6Q6RUY.js} +7 -7
  148. package/dist/grok-cli-LX6Q6RUY.js.map +1 -0
  149. package/dist/{hermes-F75OSK3Y.js → hermes-LJB3SJKB.js} +7 -7
  150. package/dist/hermes-LJB3SJKB.js.map +1 -0
  151. package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
  152. package/dist/hermes-MF3NX4ZY.js.map +1 -0
  153. package/dist/{hook-OEVRDMXD.js → hook-PVGBQUQI.js} +16 -16
  154. package/dist/hook-PVGBQUQI.js.map +1 -0
  155. package/dist/index.d.ts +3 -3
  156. package/dist/index.js +5 -5
  157. package/dist/{install-5NMCUDG5.js → install-HPDIK7F6.js} +192 -28
  158. package/dist/install-HPDIK7F6.js.map +1 -0
  159. package/dist/{introspect-dNekqPDY.d.ts → introspect-DWCu0Krk.d.ts} +1 -1
  160. package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-2DSVAJMJ.js} +6 -6
  161. package/dist/jetbrains-copilot-2DSVAJMJ.js.map +1 -0
  162. package/dist/{junie-DIFN2P22.js → junie-GUBDRADP.js} +6 -6
  163. package/dist/junie-GUBDRADP.js.map +1 -0
  164. package/dist/{kilo-EMZJBEBB.js → kilo-HRLWMENX.js} +8 -8
  165. package/dist/kilo-HRLWMENX.js.map +1 -0
  166. package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
  167. package/dist/kilo-SMHPOHJ5.js.map +1 -0
  168. package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-IQMFMYZF.js} +8 -8
  169. package/dist/kilo-cli-IQMFMYZF.js.map +1 -0
  170. package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
  171. package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
  172. package/dist/{kimi-IQJOIY6B.js → kimi-EPD46UJK.js} +7 -7
  173. package/dist/kimi-EPD46UJK.js.map +1 -0
  174. package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
  175. package/dist/kimi-MSIDEMCI.js.map +1 -0
  176. package/dist/{kiro-BYX4HEAJ.js → kiro-A7DL52FX.js} +7 -7
  177. package/dist/kiro-A7DL52FX.js.map +1 -0
  178. package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
  179. package/dist/kiro-NYRRBIKW.js.map +1 -0
  180. package/dist/{leaderboard-XEKS7B3H.js → leaderboard-KCT7XPCI.js} +7 -7
  181. package/dist/leaderboard-KCT7XPCI.js.map +1 -0
  182. package/dist/{mimo-code-7R53QDHB.js → mimo-code-NDGLBXNC.js} +7 -7
  183. package/dist/mimo-code-NDGLBXNC.js.map +1 -0
  184. package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LLWASF5X.js} +6 -6
  185. package/dist/mistral-vibe-LLWASF5X.js.map +1 -0
  186. package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
  187. package/dist/mux-7PIJ7HUQ.js.map +1 -0
  188. package/dist/{mux-NYCNGL43.js → mux-ON4QK3OC.js} +6 -6
  189. package/dist/mux-ON4QK3OC.js.map +1 -0
  190. package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-45R5MFBF.js} +7 -7
  191. package/dist/nemoclaw-45R5MFBF.js.map +1 -0
  192. package/dist/{omp-562NK5BJ.js → omp-ROCTFV3A.js} +7 -7
  193. package/dist/omp-ROCTFV3A.js.map +1 -0
  194. package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-V6CN542J.js} +6 -6
  195. package/dist/open-interpreter-V6CN542J.js.map +1 -0
  196. package/dist/openclaw-AOKFJGRQ.js +17 -0
  197. package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
  198. package/dist/openclaw-KAFTNLR2.js.map +1 -0
  199. package/dist/{opencode-YS4WX2E2.js → opencode-YYWQ45LF.js} +7 -7
  200. package/dist/opencode-YYWQ45LF.js.map +1 -0
  201. package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
  202. package/dist/opencode-Z65JYILL.js.map +1 -0
  203. package/dist/{openhands-QMYIKNOS.js → openhands-OFFMWCQW.js} +7 -7
  204. package/dist/openhands-OFFMWCQW.js.map +1 -0
  205. package/dist/{package-G25GVTH2.js → package-TALOSRQ4.js} +15 -15
  206. package/dist/package-TALOSRQ4.js.map +1 -0
  207. package/dist/{pi-644FIN3A.js → pi-4W74ETTI.js} +6 -6
  208. package/dist/pi-4W74ETTI.js.map +1 -0
  209. package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
  210. package/dist/pi-DLKCPOJE.js.map +1 -0
  211. package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
  212. package/dist/qwen-code-26CSFDX6.js.map +1 -0
  213. package/dist/{qwen-code-STDMX7EM.js → qwen-code-JOBFNEVT.js} +16 -16
  214. package/dist/qwen-code-JOBFNEVT.js.map +1 -0
  215. package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
  216. package/dist/roo-code-HSAE2CJU.js.map +1 -0
  217. package/dist/{roo-code-DLPIHHQ6.js → roo-code-RHIGJZNY.js} +6 -6
  218. package/dist/roo-code-RHIGJZNY.js.map +1 -0
  219. package/dist/runtime/index.d.ts +9 -8
  220. package/dist/runtime/index.js +14 -14
  221. package/dist/sdk/index.d.ts +4 -4
  222. package/dist/sdk/index.js +6 -6
  223. package/dist/sdk/index.js.map +1 -1
  224. package/dist/sdk/test.d.ts +2 -2
  225. package/dist/sdk/test.js +11 -11
  226. package/dist/{serve-XTMIUHKK.js → serve-4TEHK726.js} +16 -16
  227. package/dist/serve-4TEHK726.js.map +1 -0
  228. package/dist/{status-VJAGO4Z7.js → status-DVD4EHMX.js} +11 -11
  229. package/dist/status-DVD4EHMX.js.map +1 -0
  230. package/dist/{statusline-EY2E2UGF.js → statusline-ZVNH7XBK.js} +15 -15
  231. package/dist/statusline-ZVNH7XBK.js.map +1 -0
  232. package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
  233. package/dist/synthetic-LUT6V6SD.js.map +1 -0
  234. package/dist/{telemetry-OLZQBADT.js → telemetry-NWUJPIK3.js} +10 -10
  235. package/dist/telemetry-NWUJPIK3.js.map +1 -0
  236. package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
  237. package/dist/trae-SIQI3C66.js.map +1 -0
  238. package/dist/{trae-UOYD6JZZ.js → trae-TLAFSQRQ.js} +6 -6
  239. package/dist/trae-TLAFSQRQ.js.map +1 -0
  240. package/dist/{types-JyhpJHJa.d.ts → types-C6j18a6R.d.ts} +13 -14
  241. package/dist/{uninstall-AZEUPAND.js → uninstall-DZVFV7KE.js} +21 -21
  242. package/dist/uninstall-DZVFV7KE.js.map +1 -0
  243. package/dist/{upgrade-CAUUHSSC.js → upgrade-OUE4ED52.js} +21 -21
  244. package/dist/upgrade-OUE4ED52.js.map +1 -0
  245. package/dist/{usage-3KHZ4RYZ.js → usage-NT3ZXALZ.js} +4 -4
  246. package/dist/usage-NT3ZXALZ.js.map +1 -0
  247. package/dist/{usage-event-TZWOZPRJ.js → usage-event-IELZE6WO.js} +15 -15
  248. package/dist/usage-event-IELZE6WO.js.map +1 -0
  249. package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-VZ5XYSTK.js} +7 -7
  250. package/dist/vscode-copilot-VZ5XYSTK.js.map +1 -0
  251. package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
  252. package/dist/warp-5YBSP7FP.js.map +1 -0
  253. package/dist/{warp-QYJGKURM.js → warp-P2HWXIKT.js} +6 -6
  254. package/dist/warp-P2HWXIKT.js.map +1 -0
  255. package/dist/{windsurf-75LLZ37C.js → windsurf-3JC73GZK.js} +6 -6
  256. package/dist/windsurf-3JC73GZK.js.map +1 -0
  257. package/dist/{zed-HKAZXEJW.js → zed-6CMUKXLI.js} +7 -7
  258. package/dist/zed-6CMUKXLI.js.map +1 -0
  259. package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
  260. package/dist/zed-XXCNYOIN.js.map +1 -0
  261. package/package.json +92 -88
  262. package/dist/action-Q7GGO4PA.js.map +0 -1
  263. package/dist/amazon-q-ETMAGY3D.js.map +0 -1
  264. package/dist/amp-67BZONGF.js.map +0 -1
  265. package/dist/amp-BQZOL5GR.js.map +0 -1
  266. package/dist/antigravity-EA4L225L.js.map +0 -1
  267. package/dist/antigravity-VXLDLTO7.js +0 -18
  268. package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
  269. package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
  270. package/dist/chunk-2Y2MZRLP.js.map +0 -1
  271. package/dist/chunk-3NH2ARIS.js.map +0 -1
  272. package/dist/chunk-4NHXLXSD.js.map +0 -1
  273. package/dist/chunk-5IOTP7LQ.js.map +0 -1
  274. package/dist/chunk-6TF7MHG5.js.map +0 -1
  275. package/dist/chunk-6WZODR3Z.js.map +0 -1
  276. package/dist/chunk-7F5KHLEP.js.map +0 -1
  277. package/dist/chunk-APZDTY76.js.map +0 -1
  278. package/dist/chunk-BEFVJ2OE.js.map +0 -1
  279. package/dist/chunk-D4ELPBB5.js.map +0 -1
  280. package/dist/chunk-FBJQGRET.js.map +0 -1
  281. package/dist/chunk-FJCUKXGX.js.map +0 -1
  282. package/dist/chunk-GZGLCCEE.js.map +0 -1
  283. package/dist/chunk-H33M77DD.js.map +0 -1
  284. package/dist/chunk-H53QZQJI.js.map +0 -1
  285. package/dist/chunk-I5UXHIDT.js.map +0 -1
  286. package/dist/chunk-ISEJWBFF.js.map +0 -1
  287. package/dist/chunk-IVAWP6HH.js.map +0 -1
  288. package/dist/chunk-JKKU2WKG.js.map +0 -1
  289. package/dist/chunk-KHO5WNTP.js.map +0 -1
  290. package/dist/chunk-MGTDQLJJ.js.map +0 -1
  291. package/dist/chunk-MQPNU2CY.js.map +0 -1
  292. package/dist/chunk-NBYKLUF6.js.map +0 -1
  293. package/dist/chunk-NLE2J5P4.js.map +0 -1
  294. package/dist/chunk-O7SDRBVB.js.map +0 -1
  295. package/dist/chunk-OP4KBXUO.js.map +0 -1
  296. package/dist/chunk-OPXAP3AC.js.map +0 -1
  297. package/dist/chunk-PAPMUAR5.js.map +0 -1
  298. package/dist/chunk-PW42ZXCB.js.map +0 -1
  299. package/dist/chunk-RK6VBLXP.js.map +0 -1
  300. package/dist/chunk-UM5S7DKK.js.map +0 -1
  301. package/dist/chunk-UMQ6NBQR.js.map +0 -1
  302. package/dist/chunk-XC5SJ35F.js.map +0 -1
  303. package/dist/chunk-XD7MY6HB.js.map +0 -1
  304. package/dist/chunk-YPAXJ7EW.js.map +0 -1
  305. package/dist/chunk-YT43MUSJ.js.map +0 -1
  306. package/dist/chunk-Z2MRSS2I.js.map +0 -1
  307. package/dist/claude-code-GRWO6XBZ.js.map +0 -1
  308. package/dist/claude-code-NMRKTAP7.js.map +0 -1
  309. package/dist/cline-25KW25IN.js.map +0 -1
  310. package/dist/codebuddy-ZNYQACMF.js.map +0 -1
  311. package/dist/codebuff-ETYT5UBL.js.map +0 -1
  312. package/dist/codebuff-VR6JUDIY.js.map +0 -1
  313. package/dist/codex-CPNKTXG3.js.map +0 -1
  314. package/dist/codex-YAORPCOD.js.map +0 -1
  315. package/dist/continue-WU2IB5KS.js.map +0 -1
  316. package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
  317. package/dist/copilot-cli-PAP377OM.js.map +0 -1
  318. package/dist/crush-3AVQA4YU.js.map +0 -1
  319. package/dist/crush-QBXLNN7T.js.map +0 -1
  320. package/dist/cursor-DIW43TRZ.js.map +0 -1
  321. package/dist/cursor-GSLUET3V.js +0 -22
  322. package/dist/detect-6V4LC3MA.js.map +0 -1
  323. package/dist/devin-QMEM3OFA.js.map +0 -1
  324. package/dist/doctor-2YDUVSAU.js.map +0 -1
  325. package/dist/droid-MOKV53J4.js.map +0 -1
  326. package/dist/droid-U3ZBLQFO.js.map +0 -1
  327. package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
  328. package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
  329. package/dist/goose-4EI5MYTI.js.map +0 -1
  330. package/dist/goose-64V46Y2W.js.map +0 -1
  331. package/dist/grok-cli-QYQPDSVB.js.map +0 -1
  332. package/dist/hermes-F3EGTK6N.js.map +0 -1
  333. package/dist/hermes-F75OSK3Y.js.map +0 -1
  334. package/dist/hook-OEVRDMXD.js.map +0 -1
  335. package/dist/install-5NMCUDG5.js.map +0 -1
  336. package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
  337. package/dist/junie-DIFN2P22.js.map +0 -1
  338. package/dist/kilo-EMZJBEBB.js.map +0 -1
  339. package/dist/kilo-SW7BH6EG.js.map +0 -1
  340. package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
  341. package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
  342. package/dist/kimi-5FHEXGZD.js.map +0 -1
  343. package/dist/kimi-IQJOIY6B.js.map +0 -1
  344. package/dist/kiro-BYX4HEAJ.js.map +0 -1
  345. package/dist/kiro-WGOI5CGT.js.map +0 -1
  346. package/dist/leaderboard-XEKS7B3H.js.map +0 -1
  347. package/dist/mimo-code-7R53QDHB.js.map +0 -1
  348. package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
  349. package/dist/mux-NYCNGL43.js.map +0 -1
  350. package/dist/mux-XM6AP35L.js.map +0 -1
  351. package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
  352. package/dist/omp-562NK5BJ.js.map +0 -1
  353. package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
  354. package/dist/openclaw-5EQFGOWL.js.map +0 -1
  355. package/dist/openclaw-7EYBQS4R.js +0 -17
  356. package/dist/opencode-QGP4ALQY.js.map +0 -1
  357. package/dist/opencode-YS4WX2E2.js.map +0 -1
  358. package/dist/openhands-QMYIKNOS.js.map +0 -1
  359. package/dist/package-G25GVTH2.js.map +0 -1
  360. package/dist/pi-644FIN3A.js.map +0 -1
  361. package/dist/pi-GGDQREVG.js.map +0 -1
  362. package/dist/qwen-code-GQILBJ5B.js.map +0 -1
  363. package/dist/qwen-code-STDMX7EM.js.map +0 -1
  364. package/dist/roo-code-DLPIHHQ6.js.map +0 -1
  365. package/dist/roo-code-SUWI4BBN.js.map +0 -1
  366. package/dist/serve-XTMIUHKK.js.map +0 -1
  367. package/dist/status-VJAGO4Z7.js.map +0 -1
  368. package/dist/statusline-EY2E2UGF.js.map +0 -1
  369. package/dist/synthetic-6FRA7P5Q.js.map +0 -1
  370. package/dist/telemetry-OLZQBADT.js.map +0 -1
  371. package/dist/trae-CPPYASR2.js.map +0 -1
  372. package/dist/trae-UOYD6JZZ.js.map +0 -1
  373. package/dist/uninstall-AZEUPAND.js.map +0 -1
  374. package/dist/upgrade-CAUUHSSC.js.map +0 -1
  375. package/dist/usage-3KHZ4RYZ.js.map +0 -1
  376. package/dist/usage-event-TZWOZPRJ.js.map +0 -1
  377. package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
  378. package/dist/warp-NRBBJQR2.js.map +0 -1
  379. package/dist/warp-QYJGKURM.js.map +0 -1
  380. package/dist/windsurf-75LLZ37C.js.map +0 -1
  381. package/dist/zed-HKAZXEJW.js.map +0 -1
  382. package/dist/zed-XJFOOKCO.js.map +0 -1
  383. /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-PZXYAO3X.js.map} +0 -0
  384. /package/dist/{cursor-GSLUET3V.js.map → cursor-JRVK56QA.js.map} +0 -0
  385. /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-AOKFJGRQ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/usage/normalize.ts","../src/usage/aggregate.ts"],"sourcesContent":["/**\r\n * usage/normalize — model/provider/workspace normalization.\r\n *\r\n * `normalizeModelForGrouping` and `inferProvider` are FAITHFUL ports of\r\n * tokscale's order-dependent logic (tokscale-core/src/lib.rs\r\n * normalize_model_for_grouping + provider_identity.rs inferred_provider_from_model).\r\n * The matching order is load-bearing — keep the steps in sequence.\r\n *\r\n * `normalizeWorkspaceKey` / `workspaceLabelFromKey` port tokscale\r\n * sessions/mod.rs (same slash-collapsing + UNC-prefix handling) so a cwd string\r\n * from a host log maps to the same stable project key tokscale would produce.\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Model normalization (port of lib.rs normalize_model_for_grouping)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nconst REASONING_TIERS: ReadonlySet<string> = new Set([\r\n \"minimal\",\r\n \"low\",\r\n \"medium\",\r\n \"high\",\r\n \"xhigh\",\r\n \"auto\",\r\n \"none\",\r\n]);\r\n\r\n/**\r\n * Strip a `(level)` reasoning-effort suffix, returning the base model — or null\r\n * when the suffix is not one of the recognized tiers. Port of\r\n * strip_parenthesized_reasoning_tier: the base must be non-empty and already\r\n * trimmed (no surrounding whitespace).\r\n */\r\nfunction stripParenthesizedReasoningTier(modelId: string): string | null {\r\n if (!modelId.endsWith(\")\")) return null;\r\n const withoutClosing = modelId.slice(0, -1);\r\n const open = withoutClosing.lastIndexOf(\"(\");\r\n if (open < 0) return null;\r\n const base = withoutClosing.slice(0, open);\r\n const tier = withoutClosing.slice(open + 1);\r\n if (base === \"\" || base.trim() !== base) return null;\r\n if (!REASONING_TIERS.has(tier)) return null;\r\n return base;\r\n}\r\n\r\n/**\r\n * Map an `anthropic/claude-<major>-<minor>-<family>` id to the canonical\r\n * `claude-<family>-<major>-<minor>` form. Returns null when the shape does not\r\n * match exactly (too many parts, unknown family). Port of\r\n * normalize_anthropic_prefixed_claude_model.\r\n */\r\nfunction normalizeAnthropicPrefixedClaudeModel(modelId: string): string | null {\r\n const prefix = \"anthropic/claude-\";\r\n if (!modelId.startsWith(prefix)) return null;\r\n const rest = modelId.slice(prefix.length);\r\n const parts = rest.split(\"-\");\r\n const major = parts[0];\r\n const minor = parts[1];\r\n const family = parts[2];\r\n if (major === undefined || minor === undefined || family === undefined) return null;\r\n if (parts.length > 3) return null; // too many parts\r\n if (family !== \"opus\" && family !== \"sonnet\" && family !== \"haiku\") return null;\r\n return `claude-${family}-${major}-${minor}`;\r\n}\r\n\r\n/**\r\n * Normalize a raw model id to a stable grouping key. ORDER MATTERS — this is the\r\n * exact tokscale sequence:\r\n * 1. lowercase;\r\n * 2. strip a recognized `(tier)` reasoning suffix;\r\n * 3. strip a trailing `-YYYYMMDD` 8-digit date (only when len > 9 and the\r\n * 9th-from-end char is '-');\r\n * 4. for claude ids, replace dots between two digits with dashes\r\n * (e.g. \"claude-3.5-sonnet\" → \"claude-3-5-sonnet\");\r\n * 5. canonicalize an `anthropic/claude-*` prefix.\r\n */\r\nexport function normalizeModelForGrouping(modelId: string): string {\r\n let name = modelId.toLowerCase();\r\n\r\n const base = stripParenthesizedReasoningTier(name);\r\n if (base !== null) name = base;\r\n\r\n if (name.length > 9) {\r\n const potentialDate = name.slice(name.length - 8);\r\n const allDigits = /^[0-9]{8}$/.test(potentialDate);\r\n if (allDigits && name.charCodeAt(name.length - 9) === 0x2d /* '-' */) {\r\n name = name.slice(0, name.length - 9);\r\n }\r\n }\r\n\r\n if (name.includes(\"claude\")) {\r\n const chars = [...name];\r\n let result = \"\";\r\n for (let i = 0; i < chars.length; i++) {\r\n const c = chars[i] as string;\r\n const prev = chars[i - 1];\r\n const next = chars[i + 1];\r\n if (\r\n c === \".\" &&\r\n i > 0 &&\r\n i < chars.length - 1 &&\r\n prev !== undefined &&\r\n next !== undefined &&\r\n isAsciiDigit(prev) &&\r\n isAsciiDigit(next)\r\n ) {\r\n result += \"-\";\r\n } else {\r\n result += c;\r\n }\r\n }\r\n name = result;\r\n }\r\n\r\n const canonical = normalizeAnthropicPrefixedClaudeModel(name);\r\n if (canonical !== null) name = canonical;\r\n\r\n return name;\r\n}\r\n\r\nfunction isAsciiDigit(c: string): boolean {\r\n return c >= \"0\" && c <= \"9\";\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Provider inference (port of provider_identity.rs inferred_provider_from_model)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Whether `needle` occurs in `haystack` not flanked by alphanumerics on either\r\n * side (so \"o1\" matches \"o1-preview\" but not \"protocol1\"). Port of\r\n * contains_delimited.\r\n */\r\nfunction containsDelimited(haystack: string, needle: string): boolean {\r\n let from = 0;\r\n for (;;) {\r\n const pos = haystack.indexOf(needle, from);\r\n if (pos < 0) return false;\r\n const beforeOk = pos === 0 || !isAlphaNum(haystack.charCodeAt(pos - 1));\r\n const afterPos = pos + needle.length;\r\n const afterOk = afterPos === haystack.length || !isAlphaNum(haystack.charCodeAt(afterPos));\r\n if (beforeOk && afterOk) return true;\r\n from = pos + 1;\r\n }\r\n}\r\n\r\nfunction isAlphaNum(code: number): boolean {\r\n return (\r\n (code >= 0x30 && code <= 0x39) || // 0-9\r\n (code >= 0x41 && code <= 0x5a) || // A-Z\r\n (code >= 0x61 && code <= 0x7a) // a-z\r\n );\r\n}\r\n\r\n/**\r\n * Infer a canonical provider id from a model id. ORDER MATTERS — first match\r\n * wins, exactly as tokscale's inferred_provider_from_model. Returns null when no\r\n * family matches (caller supplies a platform default).\r\n */\r\nexport function inferProvider(model: string): string | null {\r\n const lower = model.toLowerCase();\r\n\r\n if (\r\n lower.includes(\"claude\") ||\r\n lower.includes(\"anthropic\") ||\r\n containsDelimited(lower, \"opus\") ||\r\n containsDelimited(lower, \"sonnet\") ||\r\n containsDelimited(lower, \"haiku\")\r\n ) {\r\n return \"anthropic\";\r\n }\r\n if (\r\n lower.includes(\"gpt\") ||\r\n lower.includes(\"openai\") ||\r\n containsDelimited(lower, \"o1\") ||\r\n containsDelimited(lower, \"o3\") ||\r\n containsDelimited(lower, \"o4\")\r\n ) {\r\n return \"openai\";\r\n }\r\n if (lower.includes(\"gemini\") || lower.includes(\"google\")) return \"google\";\r\n if (lower.includes(\"grok\")) return \"xai\";\r\n if (lower.includes(\"deepseek\")) return \"deepseek\";\r\n if (lower.includes(\"minimax\")) return \"minimax\";\r\n if (lower.includes(\"mistral\") || lower.includes(\"mixtral\")) return \"mistral\";\r\n if (lower.includes(\"llama\") || containsDelimited(lower, \"meta\")) return \"meta\";\r\n if (lower.includes(\"qwen\")) return \"qwen\";\r\n return null;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Agent-name normalization (port of sessions/mod.rs normalize_agent_name +\r\n// normalize_opencode_agent_name). The opencode reader MUST normalize the agent\r\n// before its dedup fingerprint, or fork-copied history (which differs only in a\r\n// raw agent string) escapes dedup and double-counts.\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n// U+200B ZWSP, U+200C ZWNJ, U+200D ZWJ, U+FEFF BOM/ZWNBSP.\r\nconst ZERO_WIDTH_RE = /[​‌‍]/g;\r\n\r\n/** Strip zero-width chars oh-my-openagent uses as invisible sort prefixes. */\r\nfunction stripZeroWidthChars(s: string): string {\r\n ZERO_WIDTH_RE.lastIndex = 0;\r\n return ZERO_WIDTH_RE.test(s) ? s.replace(ZERO_WIDTH_RE, \"\") : s;\r\n}\r\n\r\nconst AGENT_PREFIXES = [\"astrape:\", \"oh-my-claudecode:\", \"oh-my-codex:\"] as const;\r\n\r\n/** Strip a known namespace prefix (case-insensitive), else return as-is. */\r\nfunction stripAgentPrefix(name: string): string {\r\n for (const prefix of AGENT_PREFIXES) {\r\n if (name.length >= prefix.length && name.slice(0, prefix.length).toLowerCase() === prefix) {\r\n return name.slice(prefix.length);\r\n }\r\n }\r\n return name;\r\n}\r\n\r\n/** Collapse internal whitespace runs to single spaces (port of canonicalize_agent_name). */\r\nfunction canonicalizeAgentName(name: string): string {\r\n return name.split(/\\s+/).filter((w) => w !== \"\").join(\" \");\r\n}\r\n\r\n/** Titlecase one word, with UI/UX/API kept uppercase (port of titlecase_word). */\r\nfunction titlecaseWord(word: string): string {\r\n const lower = word.toLowerCase();\r\n if (lower === \"ui\") return \"UI\";\r\n if (lower === \"ux\") return \"UX\";\r\n if (lower === \"api\") return \"API\";\r\n if (word === \"\") return \"\";\r\n const chars = [...word];\r\n const first = chars[0] as string;\r\n return first.toUpperCase() + chars.slice(1).join(\"\");\r\n}\r\n\r\n/** Titlecase an agent name across '-' and whitespace boundaries (port of titlecase_agent). */\r\nfunction titlecaseAgent(name: string): string {\r\n if (name === \"\") return \"\";\r\n return name\r\n .split(\"-\")\r\n .flatMap((part) => part.split(/\\s+/))\r\n .filter((w) => w !== \"\")\r\n .map(titlecaseWord)\r\n .join(\" \");\r\n}\r\n\r\n/** Port of normalize_agent_name. */\r\nexport function normalizeAgentName(agent: string): string {\r\n const cleaned = stripZeroWidthChars(agent);\r\n const trimmed = cleaned.trim();\r\n const stripped = stripAgentPrefix(trimmed);\r\n const canonical = canonicalizeAgentName(stripped);\r\n const agentLower = canonical.toLowerCase();\r\n\r\n if (agentLower.includes(\"plan\")) {\r\n if (agentLower.includes(\"omo\") || agentLower.includes(\"sisyphus\")) {\r\n return \"Planner-Sisyphus\";\r\n }\r\n return titlecaseAgent(canonical);\r\n }\r\n if (agentLower === \"omo\" || agentLower === \"sisyphus\") return \"Sisyphus\";\r\n if (agentLower === \"orchestrator-sisyphus\") return \"Atlas\";\r\n return titlecaseAgent(canonical);\r\n}\r\n\r\n/** Port of normalize_oh_my_opencode_agent_name (returns undefined when no match). */\r\nfunction normalizeOhMyOpencodeAgentName(agentLower: string): string | undefined {\r\n switch (agentLower) {\r\n case \"sisyphus (ultraworker)\":\r\n case \"sisyphus - ultraworker\":\r\n case \"sisyphus ultraworker\":\r\n case \"sisyphus\":\r\n return \"Sisyphus\";\r\n case \"hephaestus (deep agent)\":\r\n case \"hephaestus - deep agent\":\r\n case \"hephaestus deep agent\":\r\n case \"hephaestus\":\r\n return \"Hephaestus\";\r\n case \"prometheus (plan builder)\":\r\n case \"prometheus - plan builder\":\r\n case \"prometheus plan builder\":\r\n case \"prometheus (planner)\":\r\n case \"prometheus\":\r\n return \"Prometheus\";\r\n case \"atlas (plan executor)\":\r\n case \"atlas - plan executor\":\r\n case \"atlas plan executor\":\r\n case \"atlas\":\r\n return \"Atlas\";\r\n case \"metis (plan consultant)\":\r\n case \"metis - plan consultant\":\r\n case \"metis plan consultant\":\r\n case \"metis\":\r\n return \"Metis\";\r\n case \"momus (plan critic)\":\r\n case \"momus - plan critic\":\r\n case \"momus plan critic\":\r\n case \"momus (plan reviewer)\":\r\n case \"momus\":\r\n return \"Momus\";\r\n case \"orchestrator-sisyphus\":\r\n return \"Atlas\";\r\n case \"sisyphus-junior\":\r\n return \"Sisyphus-Junior\";\r\n case \"planner-sisyphus\":\r\n return \"Planner-Sisyphus\";\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Port of normalize_opencode_agent_name (sessions/mod.rs:88-154). Applies the\r\n * shared cleaning pipeline, then the oh-my-opencode agent table, then falls back\r\n * to normalizeAgentName. Used by the opencode reader BEFORE its dedup fingerprint\r\n * so fork copies collapse (matching opencode.rs:272).\r\n */\r\nexport function normalizeOpencodeAgentName(agent: string): string {\r\n const cleaned = stripZeroWidthChars(agent);\r\n const trimmed = cleaned.trim();\r\n const stripped = stripAgentPrefix(trimmed);\r\n const canonical = canonicalizeAgentName(stripped);\r\n const agentLower = canonical.toLowerCase();\r\n\r\n const ohMy = normalizeOhMyOpencodeAgentName(agentLower);\r\n if (ohMy !== undefined) return ohMy;\r\n\r\n return normalizeAgentName(canonical);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Workspace key / label (port of sessions/mod.rs)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Normalize an arbitrary cwd/dir string to a stable workspace key. Port of\r\n * normalize_workspace_key: backslashes → forward slashes, collapse repeated\r\n * slashes, strip a trailing slash (preserving a leading `//` UNC prefix).\r\n * Returns undefined for an empty input.\r\n */\r\nexport function normalizeWorkspaceKey(raw: string): string | undefined {\r\n const trimmed = raw.trim();\r\n if (trimmed === \"\") return undefined;\r\n\r\n const preserveUnc = trimmed.startsWith(\"\\\\\\\\\") || trimmed.startsWith(\"//\");\r\n let normalized = trimmed.replace(/\\\\/g, \"/\");\r\n\r\n if (preserveUnc) {\r\n let body = normalized.replace(/^\\/+/, \"\");\r\n while (body.includes(\"//\")) body = body.replace(/\\/\\//g, \"/\");\r\n normalized = `//${body}`;\r\n } else {\r\n while (normalized.includes(\"//\")) normalized = normalized.replace(/\\/\\//g, \"/\");\r\n }\r\n\r\n const minLen = preserveUnc ? 2 : 1;\r\n if (normalized.length > minLen) {\r\n normalized = normalized.replace(/\\/+$/, \"\");\r\n }\r\n\r\n return normalized === \"\" ? undefined : normalized;\r\n}\r\n\r\n/**\r\n * Human-readable label from a workspace key: the last non-empty path segment.\r\n * Port of workspace_label_from_key. Returns undefined when no segment remains.\r\n */\r\nexport function workspaceLabelFromKey(key: string): string | undefined {\r\n const segments = key.split(\"/\");\r\n for (let i = segments.length - 1; i >= 0; i--) {\r\n const seg = segments[i];\r\n if (seg !== undefined && seg !== \"\") return seg;\r\n }\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Convert a cwd/dir string to both its normalized key and label in one call.\r\n * Both fields are undefined when the input does not yield a usable key.\r\n */\r\nexport function workspaceFromPath(raw: string): {\r\n projectKey?: string;\r\n projectLabel?: string;\r\n} {\r\n const key = normalizeWorkspaceKey(raw);\r\n if (key === undefined) return {};\r\n const label = workspaceLabelFromKey(key);\r\n return label === undefined ? { projectKey: key } : { projectKey: key, projectLabel: label };\r\n}\r\n","/**\r\n * usage/aggregate — token math, cross-source de-dup, and group-by rollups.\r\n *\r\n * Ports tokscale's shared aggregation model (docs/research/usage-shared-model.md\r\n * §4–5): a TokenBreakdown total is the sum of all five dimensions; dedup keeps\r\n * the FIRST occurrence per dedupKey in insertion order (un-keyed records always\r\n * pass through); group-by folds records into UsageSummary rows summing tokens /\r\n * cost / messages, counting distinct sessions, tracking the worst confidence and\r\n * the latest timestamp, sorted by total desc.\r\n */\r\n\r\nimport type {\r\n TokenBreakdown,\r\n UsageConfidence,\r\n UsageGroupBy,\r\n UsageRecord,\r\n UsageSummary,\r\n} from \"./types.js\";\r\nimport { normalizeModelForGrouping } from \"./normalize.js\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Token helpers\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** A fresh all-zero TokenBreakdown. */\r\nexport function emptyTokens(): TokenBreakdown {\r\n return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };\r\n}\r\n\r\n/** Element-wise sum of two breakdowns into a NEW object (operands untouched). */\r\nexport function addTokens(a: TokenBreakdown, b: TokenBreakdown): TokenBreakdown {\r\n return {\r\n input: a.input + b.input,\r\n output: a.output + b.output,\r\n cacheRead: a.cacheRead + b.cacheRead,\r\n cacheWrite: a.cacheWrite + b.cacheWrite,\r\n reasoning: a.reasoning + b.reasoning,\r\n };\r\n}\r\n\r\n/** Scalar total across all five token dimensions. */\r\nexport function sumTokens(t: TokenBreakdown): number {\r\n return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;\r\n}\r\n\r\n/** Accumulate `b` into `a` in place (used inside the rollup fold). */\r\nfunction addTokensInPlace(a: TokenBreakdown, b: TokenBreakdown): void {\r\n a.input += b.input;\r\n a.output += b.output;\r\n a.cacheRead += b.cacheRead;\r\n a.cacheWrite += b.cacheWrite;\r\n a.reasoning += b.reasoning;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Confidence\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Least-trustworthy (0) → most-trustworthy. host-estimated is the weaker. */\r\nconst CONFIDENCE_RANK: Record<UsageConfidence, number> = {\r\n \"host-estimated\": 0,\r\n \"host-reported\": 1,\r\n};\r\n\r\n/** The worse (least-confident) of two provenance labels. */\r\nexport function worstConfidence(a: UsageConfidence, b: UsageConfidence): UsageConfidence {\r\n return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// De-duplication\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Cross-source de-dup. Keep the FIRST record seen per `dedupKey` in insertion\r\n * order; records without a dedupKey always pass through (the per-reader dedup is\r\n * the real safeguard — this is the global backstop, per shared-model §5 and\r\n * design §6). Deterministic: same input order → same output.\r\n */\r\nexport function dedupe(records: UsageRecord[]): UsageRecord[] {\r\n const seen = new Set<string>();\r\n const out: UsageRecord[] = [];\r\n for (const rec of records) {\r\n const key = rec.dedupKey;\r\n if (key === undefined || key === \"\") {\r\n out.push(rec); // un-keyed → always include\r\n continue;\r\n }\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n out.push(rec);\r\n }\r\n // duplicate key → drop (keep-first)\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Group-by aggregation\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** Local YYYY-MM-DD for an epoch-ms timestamp (matches tokscale's local-tz date). */\r\nfunction localDay(ts: number): string {\r\n const d = new Date(ts);\r\n const y = d.getFullYear();\r\n const m = `${d.getMonth() + 1}`.padStart(2, \"0\");\r\n const day = `${d.getDate()}`.padStart(2, \"0\");\r\n return `${y}-${m}-${day}`;\r\n}\r\n\r\n/** The grouping key value for a record under the requested dimension. */\r\nfunction groupKey(rec: UsageRecord, by: UsageGroupBy): string {\r\n switch (by) {\r\n case \"platform\":\r\n return rec.platformId;\r\n case \"project\":\r\n return rec.projectLabel ?? rec.projectKey ?? \"(no project)\";\r\n case \"session\":\r\n return rec.sessionId;\r\n case \"model\":\r\n return normalizeModelForGrouping(rec.modelId);\r\n case \"day\":\r\n return localDay(rec.ts);\r\n }\r\n}\r\n\r\ninterface MutableSummary extends UsageSummary {\r\n /** Distinct session ids folded into this group (counted at finalization). */\r\n _sessions: Set<string>;\r\n}\r\n\r\n/**\r\n * Roll records up into {@link UsageSummary} rows by the chosen dimension. Tokens,\r\n * cost, and message counts sum; `sessions` counts distinct session ids; the row\r\n * carries the worst confidence and the latest timestamp seen. Sorted by total\r\n * tokens descending (ties broken by recency).\r\n */\r\nexport function aggregateBy(records: UsageRecord[], by: UsageGroupBy): UsageSummary[] {\r\n const groups = new Map<string, MutableSummary>();\r\n\r\n for (const rec of records) {\r\n const key = groupKey(rec, by);\r\n let g = groups.get(key);\r\n if (g === undefined) {\r\n g = {\r\n key,\r\n tokens: emptyTokens(),\r\n total: 0,\r\n sessions: 0,\r\n messages: 0,\r\n confidence: rec.confidence,\r\n lastTs: rec.ts,\r\n _sessions: new Set<string>(),\r\n };\r\n groups.set(key, g);\r\n }\r\n\r\n addTokensInPlace(g.tokens, rec.tokens);\r\n g.messages += rec.messageCount;\r\n g._sessions.add(rec.sessionId);\r\n g.confidence = worstConfidence(g.confidence, rec.confidence);\r\n if (rec.ts > g.lastTs) g.lastTs = rec.ts;\r\n if (rec.cost !== undefined) g.cost = (g.cost ?? 0) + rec.cost;\r\n }\r\n\r\n const rows: UsageSummary[] = [];\r\n for (const g of groups.values()) {\r\n const { _sessions, ...summary } = g;\r\n summary.total = sumTokens(summary.tokens);\r\n summary.sessions = _sessions.size;\r\n rows.push(summary);\r\n }\r\n\r\n rows.sort((a, b) => b.total - a.total || b.lastTs - a.lastTs);\r\n return rows;\r\n}\r\n"],"mappings":"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","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-RNVIK5YJ.js";
10
10
  import {
11
11
  detectRuntimeHost
12
- } from "./chunk-ISEJWBFF.js";
12
+ } from "./chunk-B6PKHS5I.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-DWZ5IWF4.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-DOPFBUCX.js";
32
32
  import {
33
33
  REGISTERED_PLATFORM_IDS,
34
34
  loadAdapter
35
- } from "./chunk-D4ELPBB5.js";
35
+ } from "./chunk-TOSNS2BY.js";
36
36
  import {
37
37
  detectLaunchMethod
38
- } from "./chunk-4NHXLXSD.js";
38
+ } from "./chunk-W7AGWAMH.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-7VSZXOXJ.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 host-native affordance (workflow, task, slash command, keybinding) or a\n * manual command points at the single stable home binary (`<homeBin> action\n * <platformId> <actionId> --connector <id>`, built by\n * core/spawn.buildHomeBinActionCommand). The CLI parses the flags and calls\n * {@link runAction}. This module:\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: no telemetry is recorded for action runs in v1 (out of scope). Adapters\n * with a verified native trigger surface emit host affordances; unsupported\n * hosts can still call this stable dispatch verb manually.\n */\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,SAAS,kBAAkB;;;ACQ3B,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,YAAY;AAkBd,SAAS,uBAAgC;AAC9C,QAAM,YAAY,QAAQ,IAAI,6BAA6B,IACxD,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,MAAM,EAAE;AACzB,QAAM,OAAO,QAAQ,IAAI,qBAAqB,IAAI,KAAK;AACvD,SAAO,SAAS,SAAS,WAAW,KAAK,QAAQ;AACnD;AAgBO,SAAS,kBAA0B;AACxC,SAAO,KAAK,SAAS,GAAG,kBAAkB;AAC5C;AAOA,IAAM,YAAY;AAClB,IAAM,YAAY;AAElB,SAAS,WAAW,OAA8B;AAChD,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAClD,MAAI,QAAQ,UAAW,QAAO;AAC9B,MAAI,QAAQ,UAAW,QAAO;AAC9B,SAAO;AACT;AAMO,SAAS,cAAkC;AAChD,QAAM,OAAO,gBAAgB;AAC7B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AACpD,QAAI,WAAW,QAAQ,OAAO,WAAW,SAAU,QAAO,CAAC;AAC3D,UAAM,MAA0B,CAAC;AACjC,eAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC/E,UAAI,UAAU,QAAQ,OAAO,UAAU,SAAU;AACjD,YAAM,MAAM;AACZ,YAAM,SAAS,IAAI,QAAQ;AAC3B,YAAM,UAAU,IAAI,SAAS;AAC7B,UACE,OAAO,WAAW,YAClB,OAAO,SAAS,MAAM,KACtB,SAAS,KACT,OAAO,YAAY,YACnB,OAAO,SAAS,OAAO,KACvB,WAAW,GACX;AACA,QAAC,IAA0C,MAAM,IAAI;AAAA,UACnD;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AASO,SAAS,aAAa,QAAqB,OAAqB;AACrE,QAAM,UAAU,WAAW,KAAK;AAChC,MAAI,YAAY,KAAM;AACtB,MAAI;AACF,UAAM,UAAU,YAAY;AAC5B,UAAM,QAAQ,QAAQ,MAAM;AAC5B,QAAI;AACJ,QAAI,UAAU,UAAa,MAAM,WAAW,GAAG;AAC7C,aAAO,EAAE,QAAQ,SAAS,SAAS,EAAE;AAAA,IACvC,OAAO;AACL,YAAM,UAAU,MAAM,UAAU;AAEhC,YAAM,SAAS,MAAM,UAAU,UAAU,MAAM,UAAU;AACzD,aAAO,EAAE,QAAQ,QAAQ;AAAA,IAC3B;AACA,UAAM,UAA8B,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,KAAK;AACjE,UAAM,OAAO,gBAAgB;AAC7B,cAAU,SAAS,CAAC;AACpB,kBAAc,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EACrE,QAAQ;AAAA,EAER;AACF;AAKA,IAAM,0BAA0B;AAGhC,IAAM,mBAAmB;AAezB,eAAsB,qBACpB,MACA,QAAgB,yBACQ;AACxB,QAAM,UAAU,QAAQ,IAAI,qBAAqB,IAAI,KAAK;AAC1D,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,kBAAkB;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa;AAAA,QACb,qBAAqB;AAAA,QACrB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,KAAK,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,UAAM,IAAI,KAAK;AACf,QAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG;AACzD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,IAAM,eAAe;AACrB,IAAM,yBAAyB;AAE/B,IAAI,cAAc;AAClB,IAAI,gBAAgB;AACpB,IAAI,oBAAoB;AAOjB,SAAS,aAAa,MAAc,KAAK,IAAI,GAAY;AAE9D,MAAI,MAAM,iBAAiB,KAAQ;AACjC,oBAAgB;AAChB,wBAAoB;AAAA,EACtB;AACA,iBAAe;AACf,MAAI,cAAc,iBAAiB,EAAG,QAAO;AAC7C,MAAI,qBAAqB,uBAAwB,QAAO;AACxD,uBAAqB;AACrB,SAAO;AACT;AAwBO,SAAS,iBACd,cACA,QACA,UAA8B,YAAY,GACzB;AACjB,MAAI,WAAW,aAAa;AAC1B,UAAM,IAAI,QAAQ,MAAM;AACxB,QAAI,MAAM,UAAa,EAAE,UAAU,KAAK,OAAO,SAAS,EAAE,MAAM,KAAK,EAAE,SAAS,GAAG;AACjF,aAAO;AAAA,QACL,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,eAAe,EAAE,MAAM,CAAC;AAAA,QACvD,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,cAAc,QAAQ,mBAAmB;AAC5D;AAGO,SAAS,eAAe,SAA8B;AAC3D,SAAO;AACT;;;ACpLA,SAAS,cAAc,OAAwB;AAC7C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,KAAK,KAAK;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAsB,cACpB,MACiB;AACjB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,mBAAmB,QAAQ,IAAI,8BAA8B;AAInE,QAAM,qBAAqB,qBAAqB;AAKhD,QAAM,QAAQ,WAAW,SAAS,MAAM;AAAA,IACtC,OAAO,CAAC,QAAQ,QAAQ,SAAS;AAAA,IACjC,KAAK,QAAQ;AAAA,EACf,CAAC;AAID,MAAI,SAAsB,iBAAiB,IAAI,eAAe;AAE9D,QAAM,UAAU,oBAAI,IAAyB;AAE7C,MAAI,MAAM;AAEV,MAAI,mBAAmB;AAQvB,WAAS,aAGP;AACA,WAAO;AAAA,MACL,IAAI,YAAY,KAAK;AAAA,MACrB,IAAI,KAAK,IAAI;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,MACrD,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AAOA,WAAS,gBAAgB,KAA2B;AAClD,UAAM,SAAS,IAAI;AACnB,QAAI,WAAW,cAAc;AAC3B,YAAM,SAAS,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS;AACnD,YAAM,aACJ,UAAU,SAAS,OAAO,YAAY,CAAC,IAClC,OAAO,YAAY,IACpB;AACN,YAAM,aACJ,cAAc,OAAO,WAAW,MAAM,MAAM,WACvC,WAAW,MAAM,IAClB;AACN,eAAS,iBAAiB,YAAY,eAAe;AACrD;AAAA,IACF;AAEA,QAAI,WAAW,cAAc;AAC3B,YAAM,MAAM,MAAM,IAAI,EAAE;AACxB,UAAI,QAAQ,KAAM;AAClB,YAAM,SAAS,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS;AACnD,YAAM,WACJ,UAAU,OAAO,OAAO,MAAM,MAAM,WAC/B,OAAO,MAAM,IACd;AACN,YAAM,WAAW,SAAS,OAAO,WAAW,IAAI;AAChD,cAAQ,IAAI,KAAK,EAAE,UAAU,MAAM,SAAS,CAAC;AAAA,IAC/C;AAAA,EACF;AAOA,WAAS,gBAAgB,KAA2B;AAClD,UAAM,MAAM,MAAM,IAAI,EAAE;AACxB,QAAI,QAAQ,KAAM;AAElB,UAAM,cAAc,QAAQ,IAAI,GAAG;AACnC,QAAI,gBAAgB,QAAW;AAC7B,cAAQ,OAAO,GAAG;AAClB,qBAAe,aAAa,GAAG;AAC/B;AAAA,IACF;AAIA,QAAI,yBAAyB,CAAC,kBAAkB;AAC9C,YAAM,SAAS,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS;AACnD,YAAM,QAAQ,SAAS,OAAO,OAAO,IAAI;AACzC,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,WAAS,eAAe,MAAmB,UAAgC;AACzE,UAAM,WAAW,SAAS,UAAU;AACpC,UAAM,SAAS,SAAS;AAGxB,UAAM,mBAAmB,WAAW,CAAC,IAAI;AACzC,UAAM,cAAc;AAAA,MAClB,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAMA,QAAI,cAAc,YAAY;AAC9B,QAAI,eAAe,YAAY;AAC/B,QAAI,mBAAqC,YAAY;AACrD,QAAI,sBAAsB,WAAW,aAAa;AAChD,UAAI;AACF,SAAC,EAAE,aAAa,cAAc,iBAAiB,IAAI;AAAA,UACjD,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY;AAAA,QACd;AACA,+BAAuB,KAAK,MAAM,YAAY,WAAW;AAAA,MAC3D,QAAQ;AAAA,MAGR;AAAA,IACF;AAIA,UAAM,gBACJ,SAAS,MAAM,KAAK,OAAO,SAAS,MAAM;AAC5C,UAAM,OAAO;AAAA,MACX,GAAG,WAAW;AAAA,MACd,UAAU,KAAK;AAAA,MACf,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,YAAY;AAAA,IACvB,CAAC;AAAA,EACH;AAQA,WAAS,gBACP,OACA,QACA,WACmF;AACnF,QAAI,cAAc,oBAAoB;AACpC,aAAO,EAAE,aAAa,OAAO,cAAc,QAAQ,kBAAkB,UAAU;AAAA,IACjF;AACA,UAAM,MAAM,iBAAiB,OAAO,MAAM;AAC1C,UAAM,OAAO,iBAAiB,QAAQ,MAAM;AAC5C,UAAM,aACJ,IAAI,WAAW,0BAA0B,KAAK,WAAW;AAC3D,WAAO;AAAA,MACL,aAAa,IAAI;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,kBAAkB,aAAa,yBAAyB;AAAA,IAC1D;AAAA,EACF;AAQA,WAAS,uBAAuBA,OAAe,mBAAiC;AAC9E,QAAI,qBAAqB,EAAG;AAC5B,QAAI,CAAC,aAAa,EAAG;AACrB,UAAM,OAAO,cAAcA,KAAI;AAC/B,QAAI,SAAS,GAAI;AAIjB,SAAK,qBAAqB,MAAM,eAAe,MAAM,CAAC,EACnD,KAAK,CAAC,UAAU;AACf,UAAI,UAAU,QAAQ,QAAQ,GAAG;AAC/B,qBAAa,QAAQ,QAAQ,iBAAiB;AAAA,MAChD;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AAAA,EACL;AAGA,WAAS,eAAe,OAAiC;AACvD,uBAAmB;AACnB,UAAM,QAAQ,gBAAgB,OAAO,QAAQ,SAAS;AACtD,UAAM,OAAO;AAAA,MACX,GAAG,WAAW;AAAA,MACd,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa,MAAM;AAAA,MACnB,cAAc;AAAA,MACd,kBAAkB,MAAM;AAAA,MACxB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,WAAS,WAAW,MAAc,WAAsC;AACtE,QAAI,CAAC,iBAAkB;AACvB,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAI,CAAC,SAAS,MAAM,EAAG;AACvB,YAAM;AAAA,IACR,QAAQ;AACN;AAAA,IACF;AACA,QAAI;AACF,UAAI,cAAc,SAAU,iBAAgB,GAAG;AAAA,UAC1C,iBAAgB,GAAG;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,WAAW,CAAC,SAAS,WAAW,MAAM,QAAQ,CAAC;AACvE,QAAM,cAAc,IAAI,WAAW,CAAC,SAAS,WAAW,MAAM,QAAQ,CAAC;AAMvE,UAAQ,MAAM,GAAG,QAAQ,CAAC,UAAkB;AAC1C,QAAI,MAAM,SAAS,MAAM,MAAM,UAAU;AACvC,YAAM,KAAK,MAAM,MAAM,MAAM,KAAK;AAClC,UAAI,CAAC,IAAI;AACP,gBAAQ,MAAM,MAAM;AACpB,cAAM,MAAM,KAAK,SAAS,MAAM,QAAQ,MAAM,OAAO,CAAC;AAAA,MACxD;AAAA,IACF;AACA,QAAI,kBAAkB;AACpB,UAAI;AACF,oBAAY,KAAK,KAAK;AAAA,MACxB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,CAAC;AACD,UAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,QAAI,MAAM,SAAS,CAAC,MAAM,MAAM,UAAW,OAAM,MAAM,IAAI;AAAA,EAC7D,CAAC;AAGD,MAAI,MAAM,QAAQ;AAChB,UAAM,cAAc,MAAM;AAC1B,gBAAY,GAAG,QAAQ,CAAC,UAAkB;AACxC,YAAM,KAAK,QAAQ,OAAO,MAAM,KAAK;AACrC,UAAI,CAAC,IAAI;AACP,oBAAY,MAAM;AAClB,gBAAQ,OAAO,KAAK,SAAS,MAAM,YAAY,OAAO,CAAC;AAAA,MACzD;AACA,UAAI,kBAAkB;AACpB,YAAI;AACF,sBAAY,KAAK,KAAK;AAAA,QACxB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgB,CAAC,WAAiC;AACtD,QAAI;AACF,UAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,MAAM;AAAA,IACtC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,WAAW,MAAY,cAAc,QAAQ;AACnD,QAAM,YAAY,MAAY,cAAc,SAAS;AACrD,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,SAAS;AAG/B,SAAO,MAAM,IAAI,QAAgB,CAAC,YAAY;AAC5C,UAAM,SAAS,CAAC,SAAuB;AACrC,cAAQ,eAAe,UAAU,QAAQ;AACzC,cAAQ,eAAe,WAAW,SAAS;AAC3C,UAAI;AACF,cAAM,MAAM;AAAA,MACd,QAAQ;AAAA,MAER;AACA,cAAQ,IAAI;AAAA,IACd;AAEA,UAAM,GAAG,SAAS,MAAM;AAGtB,aAAO,GAAG;AAAA,IACZ,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAClC,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,IAAI;AACX;AAAA,MACF;AAEA,UAAI,QAAQ;AACV,cAAM,MAAM,eAAe,MAAM;AACjC,eAAO,QAAQ,SAAY,IAAI,MAAM,GAAG;AACxC;AAAA,MACF;AACA,aAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAGA,SAAS,eAAe,QAA4C;AAClE,QAAM,QAAiD;AAAA,IACrD,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACA,SAAO,MAAM,MAAM;AACrB;;;AFlZA,SAAS,iBAAiB,MAAyB,QAAQ,KAAa;AACtE,SACE,IAAI,2BACJ,IAAI,qBACJ,IAAI,mBACJ,IAAI,mBACJ,IAAI,kBACJ,QAAQ,WAAW,CAAC;AAExB;AAEA,eAAsB,SAAS,MAAwC;AACrE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAOJ,MAAI,YAAY,UAAa,QAAQ,KAAK,MAAM,IAAI;AAClD,YAAQ,IAAI,2BAA2B;AAAA,EACzC;AAEA,QAAM,YAAY,mBAAmB,WAAW;AAChD,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,cAAc,WAAW;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,KAAK,gBAAgB,QAAQ,IAAI,CAAC;AAIxC,QAAM,eACJ,yBAAyB,UACzB,wBAAwB,IAAI,oBAAoB,IAC5C,uBACA,kBAAkB,EAAE;AAC1B,QAAM,YAAY,iBAAiB;AAInC,QAAM,eAAe,mBAAmB,eAAe,UAAU;AAEjE,QAAM,QAAQ,UAAU,CAAC,CAAC;AAC1B,QAAM,MAAM,aAAa;AAEzB,SAAO,cAAc;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,IACT,MAAM;AAAA,IACN;AAAA,IACA,WAAW;AAAA,IACX,iBAAiB,UAAU,UAAU;AAAA,IACrC;AAAA,IACA;AAAA,IACA,YAAY,GAAG;AAAA,IACf,YAAY,GAAG;AAAA,IACf,iBAAiB,UAAU,UAAU;AAAA,IACrC;AAAA,IACA;AAAA,EACF,CAAC;AACH;;;AGnGA,IAAM,OAA4B,EAAE,UAAU,GAAG,UAAU,MAAM;AAEjE,SAASC,UAAS,GAA0C;AAC1D,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAChE;AAGA,SAAS,YAAY,GAAoB;AACvC,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI;AAC1E,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,EAAG,QAAO;AACzC,SAAO,KAAK,MAAM,CAAC;AACrB;AAGA,SAAS,SAAS,KAA8B,MAA6C;AAC3F,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,OAAO,MAAM,YAAY,EAAE,KAAK,MAAM,GAAI,QAAO;AAAA,EACvD;AACA,SAAO;AACT;AAGA,SAAS,WACP,MACA,MACQ;AACR,aAAW,KAAK,MAAM;AACpB,QAAI,KAAK,KAAM,QAAO,YAAY,KAAK,CAAC,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAGA,SAAS,kBAAkB,MAAoE;AAC7F,aAAW,KAAK,CAAC,iBAAiB,gBAAgB,GAAG;AACnD,QAAIA,UAAS,KAAK,CAAC,CAAC,EAAG,QAAO,KAAK,CAAC;AAAA,EACtC;AAGA,aAAW,KAAK,CAAC,YAAY,iBAAiB,gBAAgB,GAAG;AAC/D,UAAM,SAAS,KAAK,CAAC;AACrB,QAAIA,UAAS,MAAM,GAAG;AACpB,iBAAW,MAAM,CAAC,iBAAiB,gBAAgB,GAAG;AACpD,YAAIA,UAAS,OAAO,EAAE,CAAC,EAAG,QAAO,OAAO,EAAE;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,IAAM,kBAAuC,oBAAI,IAAgB;AAAA,EAC/D;AAAA,EAAe;AAAA,EAAS;AAAA,EAAU;AAAA,EAAkB;AAAA,EACpD;AAAA,EAAe;AAAA,EAAc;AAAA,EAAY;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAQ;AAAA,EACrE;AAAA,EAAY;AAAA,EAAO;AAAA,EAAe;AAAA,EAAmB;AAAA,EAAQ;AAAA,EAC7D;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAO;AAAA,EAAS;AAAA,EAAY;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAY;AAAA,EACrE;AAAA,EAAS;AAAA,EAAS;AAAA,EAAa;AACjC,CAAC;AAED,SAAS,aAAa,IAAwB;AAC5C,SAAQ,gBAAgB,IAAI,EAAE,IAAI,KAAK;AACzC;AASA,eAAsB,cACpB,MAC8B;AAC9B,MAAI;AAGF,UAAM,UAAU,KAAK,MAAM,KAAK;AAChC,QAAI,YAAY,GAAI,QAAO;AAE3B,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,MAAM,OAAO;AAAA,IAC1B,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAACA,UAAS,GAAG,EAAG,QAAO;AAE3B,UAAM,OAAO,kBAAkB,GAAG;AAClC,QAAI,SAAS,OAAW,QAAO;AAE/B,UAAM,eAAe,WAAW,MAAM,CAAC,oBAAoB,oBAAoB,CAAC;AAChF,UAAM,aAAa,WAAW,MAAM,CAAC,wBAAwB,wBAAwB,CAAC;AACtF,UAAM,SAAS,WAAW,MAAM,CAAC,2BAA2B,4BAA4B,CAAC;AACzF,UAAM,WAAW,WAAW,MAAM,CAAC,sBAAsB,sBAAsB,CAAC;AAIhF,UAAM,cAAc,KAAK,IAAI,GAAG,eAAe,KAAK,IAAI,QAAQ,YAAY,CAAC;AAE7E,UAAM,eAAe,aAAa;AAGlC,QAAI,gBAAgB,KAAK,iBAAiB,EAAG,QAAO;AAIpD,UAAM,YACJ,SAAS,KAAK,CAAC,cAAc,aAAa,kBAAkB,iBAAiB,CAAC,KAAK;AACrF,UAAM,MAAM,SAAS,KAAK,CAAC,OAAO,aAAa,cAAc,aAAa,CAAC,KAAK,QAAQ,IAAI;AAC5F,UAAM,KAAK,gBAAgB,GAAG;AAE9B,UAAM,SAA0B;AAAA,MAC9B,IAAI,YAAY,CAAC;AAAA,MACjB,IAAI,KAAK,IAAI;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,UAAU;AAAA,MACV,OAAO;AAAA,MACP,cAAc,aAAa,KAAK,UAAU;AAAA,MAC1C;AAAA,MACA,YAAY,GAAG;AAAA,MACf,YAAY,GAAG;AAAA,MACf;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,SAAS;AAAA,IACX;AAEA,UAAM,QAAQ,UAAU,CAAC,CAAC;AAC1B,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,IACrB,UAAE;AACA,UAAI;AACF,cAAM,MAAM;AAAA,MACd,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,EAAE,UAAU,GAAG,UAAU,KAAK;AAAA,EACvC,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;;;AC5IA,IAAM,QAA6B,EAAE,UAAU,EAAE;AAOjD,SAAS,WAAW,OAAwB;AAC1C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,YAAY,GAAI,QAAO,CAAC;AAC5B,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAOA,eAAsB,cACpB,MAC8B;AAC9B,QAAM,EAAE,YAAY,aAAa,MAAM,IAAI;AAE3C,MAAI;AACF,UAAM,YAAY,MAAM,wBAAwB,WAAW;AAC3D,UAAM,UAAU,MAAM,YAAY,UAAU;AAI5C,QACE,CAAC,UAAU,cACX,OAAO,UAAU,WAAW,WAAW,cACvC,CAAC,WACD,CAAC,QAAQ,oBACT,CAAC,QAAQ,oBACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,WAAW,KAAK;AAC5B,UAAM,MAAM,QAAQ,iBAAiB,GAAG;AAGxC,QAAI,cAAc;AAKlB,QAAI,IAAI,gBAAgB,KAAM,KAAI,eAAe,QAAQ;AAKzD,QAAI,QAAQ,mBAAmB,WAAW,GAAG;AAC7C,QAAI,YAAY,uBAAuB,WAAW;AAIlD,6BAAyB,KAAK,WAAW;AAMzC,UAAM,UAAU,UAAU,WAAW,QAAQ,UAAwB,GAAG;AACxE,UAAM,SACJ,OAAO,YAAY,aAAa,UAAU,UAAU,WAAW;AAEjE,UAAM,WAAW,MAAM,OAAO,GAAG;AAGjC,UAAM,OAAO,YAAY,OAAO,KAAK,OAAO,QAAQ;AAEpD,UAAM,QAAQ,QAAQ,mBAAmB,IAAI;AAC7C,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC7D,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF,SAAS,KAAK;AAIZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAI,MAAM,cAAc,UAAU,KAAK,WAAW,aAAa,OAAO;AACtE,WAAO;AAAA,EACT;AACF;;;ACjFA,IAAM,qBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,MAAM;AAAA,EACN,cAAc;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,yBAAyB;AAAA,EACzB,YAAY,CAAC;AACf;AASA,eAAsB,UACpB,MAC0B;AAC1B,QAAM,EAAE,YAAY,aAAa,SAAS,IAAI;AAE9C,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,wBAAwB,WAAW;AAAA,EACvD,SAAS,KAAK;AAGZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,EAAE,UAAU,GAAG,QAAQ,QAAQ;AAAA,EACxC;AAEA,QAAM,SAAS,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAC9D,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,cAAc,QAAQ,kBAAkB,WAAW;AAAA,IAC7D;AAAA,EACF;AAIA,QAAM,UAAU,MAAM,YAAY,UAAU,EAAE,MAAM,MAAM,MAAS;AACnE,QAAM,OAAmB,wBAAwB,IAAI,UAAwB,IACxE,aACD;AACJ,QAAM,MAAe;AAAA,IACnB;AAAA,IACA,cAAc,SAAS,gBAAgB;AAAA;AAAA;AAAA;AAAA,IAIvC,OAAO,mBAAmB,WAAW,GAAG;AAAA,IACxC,WAAW,uBAAuB,WAAW;AAAA,EAC/C;AAKA,QAAM,UAAU,OAAO,QAAQ,UAAwB,GAAG;AAC1D,QAAM,MAAM,OAAO,YAAY,aAAa,UAAU,OAAO;AAE7D,MAAI;AACF,UAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,UAAM,UAAU,QAAQ;AACxB,QAAI,OAAO,YAAY,YAAY,YAAY,IAAI;AACjD,aAAO,EAAE,UAAU,GAAG,QAAQ,QAAQ;AAAA,IACxC;AAEA,WAAO,EAAE,UAAU,EAAE;AAAA,EACvB,SAAS,KAAK;AAIZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,WAAW,QAAQ,aAAa,OAAO;AAAA,IACjD;AAAA,EACF;AACF;","names":["args","isObject"]}
@@ -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-TOSNS2BY.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-B6PKHS5I.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":[]}
@@ -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,13 +3,13 @@ import {
3
3
  defineConnector,
4
4
  readConnectorPackageMetadataNearFile,
5
5
  withConnectorPackageMetadata
6
- } from "./chunk-5IOTP7LQ.js";
6
+ } from "./chunk-KWC5DQ5Z.js";
7
7
  import {
8
8
  assertNoSymlinkInPath,
9
9
  connectorDir,
10
10
  connectorsDir,
11
11
  ensureDir
12
- } from "./chunk-GZGLCCEE.js";
12
+ } from "./chunk-QYOUQCC6.js";
13
13
 
14
14
  // src/core/load-connector.ts
15
15
  import { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
@@ -193,4 +193,4 @@ export {
193
193
  connectorFromMeta,
194
194
  listRegisteredConnectors
195
195
  };
196
- //# sourceMappingURL=chunk-PW42ZXCB.js.map
196
+ //# sourceMappingURL=chunk-DOPFBUCX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/load-connector.ts"],"sourcesContent":["/**\r\n * core/load-connector — discover, load, and register connector definitions.\r\n *\r\n * Responsibilities:\r\n * 1. Locate the developer's `agent-connector.config.{mjs,js,json}` by walking\r\n * upward from a start directory (the project root convention).\r\n * 2. Load a config from a path into a LIVE ResolvedConnector (with handlers).\r\n * 3. Register a connector by persisting SERIALIZABLE metadata (no functions)\r\n * to `connectorDir(id)/connector.json`, pointing at the source module so\r\n * live handlers can be re-imported at runtime from a stable record.\r\n *\r\n * Why split live vs. serialized? Hook handlers are functions and cannot survive\r\n * JSON. The registry stores only what JSON can hold plus an absolute\r\n * `modulePath`; the runtime re-imports that module to recover the live handlers.\r\n */\r\n\r\nimport { existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\";\r\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\r\nimport { pathToFileURL } from \"node:url\";\r\n\r\nimport type {\r\n CommandDef,\r\n HookEventName,\r\n InstallScope,\r\n MemoryDef,\r\n PlatformId,\r\n PlatformOverride,\r\n ResolvedConnector,\r\n ServerDef,\r\n SkillDef,\r\n SubagentDef,\r\n} from \"./types.js\";\r\nimport { defineConnector } from \"./define-connector.js\";\r\nimport { assertNoSymlinkInPath, connectorDir, connectorsDir, ensureDir } from \"./paths.js\";\r\nimport {\r\n readConnectorPackageMetadataNearFile,\r\n withConnectorPackageMetadata,\r\n} from \"./package-metadata.js\";\r\n\r\n/** Candidate config filenames, in resolution-precedence order. */\r\nconst CONFIG_FILENAMES = [\r\n \"agent-connector.config.mjs\",\r\n \"agent-connector.config.js\",\r\n \"agent-connector.config.json\",\r\n] as const;\r\n\r\n/**\r\n * The serializable subset of a connector persisted to `connector.json`.\r\n * Hook handler functions are intentionally absent — only the absolute\r\n * `modulePath` is stored so the live ResolvedConnector can be re-imported.\r\n */\r\nexport interface RegisteredMeta {\r\n id: string;\r\n displayName: string;\r\n version: string;\r\n /** Package-derived identity, when available from config or package.json. */\r\n mcp?: ResolvedConnector[\"mcp\"];\r\n /** Absolute path to the source module that produced this connector. */\r\n modulePath: string;\r\n telemetry: ResolvedConnector[\"telemetry\"];\r\n /** Canonical event names the connector handles (handlers themselves stripped). */\r\n hookEvents: HookEventName[];\r\n hasServer: boolean;\r\n server: ServerDef | null;\r\n /**\r\n * Content surfaces (commands/skills/subagents). These are JSON-serializable\r\n * (no functions) so they persist cleanly and let uninstall locate the files\r\n * to remove even when the source module is gone.\r\n */\r\n commands: CommandDef[];\r\n skills: SkillDef[];\r\n subagents: SubagentDef[];\r\n /**\r\n * Memory entries (managed-block content surface). Plain content, persisted\r\n * whole so uninstall normally has the full entry list even when the source\r\n * module is gone (the marker prefix-scan is the fallback beyond that).\r\n */\r\n memory: MemoryDef[];\r\n targets: ResolvedConnector[\"targets\"];\r\n /** Per-platform overrides with any hook handler functions stripped. */\r\n platforms: Partial<Record<PlatformId, PlatformOverride>>;\r\n /**\r\n * The install scope this connector was registered under, persisted so the\r\n * runtime entrypoints (hook/statusline/action) can recover it cheaply (sync)\r\n * and stamp it onto the HostCtx/event — scope is an install-time property the\r\n * runtime otherwise has no access to. OPTIONAL: absent for an ad-hoc register\r\n * that passed no scope, or a record written before this field existed.\r\n * This is the install's RUN-WIDE DEFAULT scope; it can differ from the\r\n * effective per-host scope when a `platforms[host].scope` override is set (the\r\n * record is keyed by connector id only and holds no per-host scope map).\r\n */\r\n scope?: InstallScope;\r\n}\r\n\r\n/**\r\n * Search `startDir` and each ancestor for a connector config file. Returns the\r\n * absolute path to the first match (filesystem-root-stopping), or null.\r\n */\r\nexport function findConnectorConfig(startDir: string): string | null {\r\n let dir = resolve(startDir);\r\n // Walk upward until `dirname(dir) === dir` (filesystem root).\r\n for (;;) {\r\n for (const name of CONFIG_FILENAMES) {\r\n const candidate = join(dir, name);\r\n if (existsSync(candidate)) return candidate;\r\n }\r\n const parent = dirname(dir);\r\n if (parent === dir) break;\r\n dir = parent;\r\n }\r\n return null;\r\n}\r\n\r\n/** True if a value already looks like a ResolvedConnector (vs. a raw config). */\r\nfunction isResolvedConnector(value: unknown): value is ResolvedConnector {\r\n return (\r\n typeof value === \"object\" &&\r\n value !== null &&\r\n Array.isArray((value as { hookEvents?: unknown }).hookEvents)\r\n );\r\n}\r\n\r\n/**\r\n * Load a connector from an absolute or relative path.\r\n * - `.json` → JSON.parse, then defineConnector().\r\n * - `.mjs` / `.js` → dynamic import (via pathToFileURL, Windows-safe),\r\n * take the default export. If it already looks like a\r\n * ResolvedConnector use it as-is; otherwise treat it as\r\n * a ConnectorConfig and run it through defineConnector().\r\n * Returns the live connector plus the absolute module path it came from.\r\n */\r\nexport async function loadConnectorFromPath(\r\n path: string,\r\n): Promise<{ connector: ResolvedConnector; modulePath: string }> {\r\n let modulePath = isAbsolute(path) ? path : resolve(path);\r\n // Canonicalize before building the file:// URL for dynamic import. On Windows a\r\n // short (8.3) temp path like C:\\Users\\RUNNER~1\\...\\x.mjs round-trips the \"~\" as\r\n // %7E and fails to load; realpathSync.native expands it to the real long path.\r\n // It also resolves symlinked temp roots (e.g. macOS /var -> /private/var).\r\n // If the file does not exist yet, fall through with the resolve()'d path so the\r\n // subsequent read/import throws the real not-found error.\r\n try {\r\n modulePath = realpathSync.native(modulePath);\r\n } catch {\r\n /* keep the resolve()'d path */\r\n }\r\n\r\n if (modulePath.endsWith(\".json\")) {\r\n const text = readFileSync(modulePath, \"utf8\");\r\n const config = JSON.parse(text) as unknown;\r\n const connector = await withConnectorPackageMetadata(\r\n readConnectorPackageMetadataNearFile(modulePath),\r\n async () => defineConnector(config as Parameters<typeof defineConnector>[0]),\r\n );\r\n return { connector, modulePath };\r\n }\r\n\r\n // .mjs / .js — dynamic ESM import. pathToFileURL keeps Windows paths valid.\r\n const moduleUrl = pathToFileURL(modulePath).href;\r\n const connector = await withConnectorPackageMetadata(\r\n readConnectorPackageMetadataNearFile(modulePath),\r\n async () => {\r\n const mod = (await import(/* @vite-ignore */ moduleUrl)) as {\r\n default?: unknown;\r\n };\r\n const exported = mod.default;\r\n if (exported == null) {\r\n throw new Error(\r\n `Connector module has no default export: ${modulePath}`,\r\n );\r\n }\r\n return isResolvedConnector(exported)\r\n ? exported\r\n : defineConnector(exported as Parameters<typeof defineConnector>[0]);\r\n },\r\n );\r\n return { connector, modulePath };\r\n}\r\n\r\n/**\r\n * Strip any function-valued `hooks` from per-platform overrides so the result\r\n * is JSON-serializable. A boolean `hooks` override is preserved; an object\r\n * override is dropped (it can only carry handler functions, which are not\r\n * serializable). `nativeHooks` declarations are persisted HANDLER-LESS (event\r\n * name + matcher only) so meta-based inspection (doctor / health checks / the\r\n * hook CLI) can still see WHICH native events were declared — the live handlers\r\n * are re-imported from `modulePath` at runtime, exactly like normalized hooks.\r\n * Everything else on the override is kept verbatim — notably `configPatch`\r\n * declarations, which are pure JSON (key/value/reason/docsUrl, no functions)\r\n * and persist WHOLE so uninstall/doctor can reason about them even when the\r\n * source module is gone.\r\n */\r\nfunction serializablePlatforms(\r\n platforms: Partial<Record<PlatformId, PlatformOverride>>,\r\n): Partial<Record<PlatformId, PlatformOverride>> {\r\n const out: Partial<Record<PlatformId, PlatformOverride>> = {};\r\n for (const [id, override] of Object.entries(platforms) as [\r\n PlatformId,\r\n PlatformOverride | undefined,\r\n ][]) {\r\n if (!override) continue;\r\n const { hooks, nativeHooks, ...rest } = override;\r\n const clean: PlatformOverride = { ...rest };\r\n // Keep only a boolean hooks flag; object hooks carry handler functions.\r\n if (typeof hooks === \"boolean\") clean.hooks = hooks;\r\n if (nativeHooks && typeof nativeHooks === \"object\") {\r\n const stripped: Record<string, { matcher?: string }> = {};\r\n for (const [event, def] of Object.entries(nativeHooks)) {\r\n stripped[event] =\r\n typeof def?.matcher === \"string\" ? { matcher: def.matcher } : {};\r\n }\r\n // Cast: the persisted record deliberately omits the (non-serializable)\r\n // handler; consumers of meta-derived connectors never dispatch handlers.\r\n clean.nativeHooks = stripped as PlatformOverride[\"nativeHooks\"];\r\n }\r\n out[id] = clean;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Persist a connector's serializable metadata to `connectorDir(id)/connector.json`.\r\n * Handlers are NOT serialized — they are re-imported from `modulePath` at runtime.\r\n * The optional `scope` (the install scope the caller deployed under) is persisted\r\n * so the runtime entrypoints can recover it and stamp it onto the HostCtx/event.\r\n * Returns the absolute path of the written record.\r\n */\r\nexport function registerConnector(\r\n connector: ResolvedConnector,\r\n modulePath: string,\r\n scope?: InstallScope,\r\n): string {\r\n const meta: RegisteredMeta = {\r\n id: connector.id,\r\n displayName: connector.displayName,\r\n version: connector.version,\r\n ...(connector.mcp ? { mcp: connector.mcp } : {}),\r\n modulePath: resolve(modulePath),\r\n telemetry: connector.telemetry,\r\n hookEvents: connector.hookEvents,\r\n hasServer: !!connector.server,\r\n server: connector.server ?? null,\r\n commands: connector.commands,\r\n skills: connector.skills,\r\n subagents: connector.subagents,\r\n memory: connector.memory,\r\n targets: connector.targets,\r\n platforms: serializablePlatforms(connector.platforms),\r\n // Persist the install scope when the caller supplied one (the installer\r\n // passes its install scope). Omitted from the record when undefined so an\r\n // ad-hoc register stays minimal and round-trips as \"no scope known\".\r\n ...(scope !== undefined ? { scope } : {}),\r\n };\r\n\r\n const dir = connectorDir(connector.id);\r\n const outPath = join(dir, \"connector.json\");\r\n assertNoSymlinkInPath(outPath);\r\n ensureDir(dir);\r\n writeFileSync(outPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\r\n return outPath;\r\n}\r\n\r\n/**\r\n * Deregister a connector by removing its DATA-dir record directory\r\n * (`connectorDir(id)`) recursively. This is the inverse of {@link registerConnector}:\r\n * registerConnector writes `connectorDir(id)/connector.json`, but per-target\r\n * uninstall only strips host-native registrations — it never removes this record,\r\n * leaving an orphan behind. Best-effort + guarded: returns the directory path and\r\n * whether it existed before removal.\r\n */\r\nexport function deregisterConnector(id: string): {\r\n removed: boolean;\r\n path: string;\r\n} {\r\n const dir = connectorDir(id);\r\n assertNoSymlinkInPath(dir);\r\n const existed = existsSync(dir);\r\n if (existed) rmSync(dir, { recursive: true, force: true });\r\n return { removed: existed, path: dir };\r\n}\r\n\r\n/**\r\n * Read the registered metadata record for `id`, or null if not registered or\r\n * the record is unreadable/corrupt.\r\n */\r\nexport function readRegisteredMeta(id: string): RegisteredMeta | null {\r\n const recordPath = join(connectorDir(id), \"connector.json\");\r\n if (!existsSync(recordPath)) return null;\r\n try {\r\n return JSON.parse(readFileSync(recordPath, \"utf8\")) as RegisteredMeta;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Load the LIVE ResolvedConnector (with hook handlers) for a registered id by\r\n * re-importing its source module. Throws a clear error when the connector is\r\n * not registered or its source module no longer resolves to it.\r\n */\r\nexport async function loadRegisteredConnector(\r\n id: string,\r\n): Promise<ResolvedConnector> {\r\n const meta = readRegisteredMeta(id);\r\n if (!meta) {\r\n throw new Error(\r\n `Connector \"${id}\" is not registered (no record at ${join(connectorDir(id), \"connector.json\")}). ` +\r\n `Run an install/register step first.`,\r\n );\r\n }\r\n if (!meta.modulePath || !existsSync(meta.modulePath)) {\r\n throw new Error(\r\n `Connector \"${id}\" record points at a missing module: ${meta.modulePath}`,\r\n );\r\n }\r\n const { connector } = await loadConnectorFromPath(meta.modulePath);\r\n return connector;\r\n}\r\n\r\n/**\r\n * Build a handler-less ResolvedConnector from stored metadata. Suitable for\r\n * inspection/doctor where live hook handlers are not needed (and where the\r\n * source module may even be gone). `hooks` is empty; `hookEvents` is preserved.\r\n */\r\nexport function connectorFromMeta(meta: RegisteredMeta): ResolvedConnector {\r\n return {\r\n id: meta.id,\r\n displayName: meta.displayName,\r\n version: meta.version,\r\n ...(meta.mcp ? { mcp: meta.mcp } : {}),\r\n ...(meta.server ? { server: meta.server } : {}),\r\n hooks: {},\r\n hookEvents: meta.hookEvents ?? [],\r\n telemetry: meta.telemetry,\r\n commands: meta.commands ?? [],\r\n skills: meta.skills ?? [],\r\n subagents: meta.subagents ?? [],\r\n memory: meta.memory ?? [],\r\n // Actions carry live `run` handlers, so they are NOT serialized into the\r\n // record (like the statusline render). A handler-less meta-derived connector\r\n // gets [] — inspection/uninstall never dispatch action handlers, and the\r\n // BaseAdapter action default already tolerates an empty list.\r\n actions: [],\r\n platforms: meta.platforms ?? {},\r\n targets: meta.targets,\r\n };\r\n}\r\n\r\n/**\r\n * List every registered connector as a handler-less ResolvedConnector (read\r\n * from `connectorsDir()`). Used by doctor to health-check what is actually\r\n * installed, rather than guessing from the local working directory.\r\n */\r\nexport function listRegisteredConnectors(): ResolvedConnector[] {\r\n const dir = connectorsDir();\r\n if (!existsSync(dir)) return [];\r\n const out: ResolvedConnector[] = [];\r\n for (const entry of readdirSync(dir)) {\r\n const meta = readRegisteredMeta(entry);\r\n if (meta) out.push(connectorFromMeta(meta));\r\n }\r\n return out;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;AAgBA,SAAS,YAAY,aAAa,cAAc,cAAc,QAAQ,qBAAqB;AAC3F,SAAS,SAAS,YAAY,MAAM,eAAe;AACnD,SAAS,qBAAqB;AAsB9B,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AAsDO,SAAS,oBAAoB,UAAiC;AACnE,MAAI,MAAM,QAAQ,QAAQ;AAE1B,aAAS;AACP,eAAW,QAAQ,kBAAkB;AACnC,YAAM,YAAY,KAAK,KAAK,IAAI;AAChC,UAAI,WAAW,SAAS,EAAG,QAAO;AAAA,IACpC;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,OAA4C;AACvE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,MAAM,QAAS,MAAmC,UAAU;AAEhE;AAWA,eAAsB,sBACpB,MAC+D;AAC/D,MAAI,aAAa,WAAW,IAAI,IAAI,OAAO,QAAQ,IAAI;AAOvD,MAAI;AACF,iBAAa,aAAa,OAAO,UAAU;AAAA,EAC7C,QAAQ;AAAA,EAER;AAEA,MAAI,WAAW,SAAS,OAAO,GAAG;AAChC,UAAM,OAAO,aAAa,YAAY,MAAM;AAC5C,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAMA,aAAY,MAAM;AAAA,MACtB,qCAAqC,UAAU;AAAA,MAC/C,YAAY,gBAAgB,MAA+C;AAAA,IAC7E;AACA,WAAO,EAAE,WAAAA,YAAW,WAAW;AAAA,EACjC;AAGA,QAAM,YAAY,cAAc,UAAU,EAAE;AAC5C,QAAM,YAAY,MAAM;AAAA,IACtB,qCAAqC,UAAU;AAAA,IAC/C,YAAY;AACV,YAAM,MAAO,MAAM;AAAA;AAAA,QAA0B;AAAA;AAG7C,YAAM,WAAW,IAAI;AACrB,UAAI,YAAY,MAAM;AACpB,cAAM,IAAI;AAAA,UACR,2CAA2C,UAAU;AAAA,QACvD;AAAA,MACF;AACA,aAAO,oBAAoB,QAAQ,IAC/B,WACA,gBAAgB,QAAiD;AAAA,IACvE;AAAA,EACF;AACA,SAAO,EAAE,WAAW,WAAW;AACjC;AAeA,SAAS,sBACP,WAC+C;AAC/C,QAAM,MAAqD,CAAC;AAC5D,aAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,SAAS,GAGhD;AACH,QAAI,CAAC,SAAU;AACf,UAAM,EAAE,OAAO,aAAa,GAAG,KAAK,IAAI;AACxC,UAAM,QAA0B,EAAE,GAAG,KAAK;AAE1C,QAAI,OAAO,UAAU,UAAW,OAAM,QAAQ;AAC9C,QAAI,eAAe,OAAO,gBAAgB,UAAU;AAClD,YAAM,WAAiD,CAAC;AACxD,iBAAW,CAAC,OAAO,GAAG,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,iBAAS,KAAK,IACZ,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,MACnE;AAGA,YAAM,cAAc;AAAA,IACtB;AACA,QAAI,EAAE,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AASO,SAAS,kBACd,WACA,YACA,OACQ;AACR,QAAM,OAAuB;AAAA,IAC3B,IAAI,UAAU;AAAA,IACd,aAAa,UAAU;AAAA,IACvB,SAAS,UAAU;AAAA,IACnB,GAAI,UAAU,MAAM,EAAE,KAAK,UAAU,IAAI,IAAI,CAAC;AAAA,IAC9C,YAAY,QAAQ,UAAU;AAAA,IAC9B,WAAW,UAAU;AAAA,IACrB,YAAY,UAAU;AAAA,IACtB,WAAW,CAAC,CAAC,UAAU;AAAA,IACvB,QAAQ,UAAU,UAAU;AAAA,IAC5B,UAAU,UAAU;AAAA,IACpB,QAAQ,UAAU;AAAA,IAClB,WAAW,UAAU;AAAA,IACrB,QAAQ,UAAU;AAAA,IAClB,SAAS,UAAU;AAAA,IACnB,WAAW,sBAAsB,UAAU,SAAS;AAAA;AAAA;AAAA;AAAA,IAIpD,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,EACzC;AAEA,QAAM,MAAM,aAAa,UAAU,EAAE;AACrC,QAAM,UAAU,KAAK,KAAK,gBAAgB;AAC1C,wBAAsB,OAAO;AAC7B,YAAU,GAAG;AACb,gBAAc,SAAS,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACnE,SAAO;AACT;AAUO,SAAS,oBAAoB,IAGlC;AACA,QAAM,MAAM,aAAa,EAAE;AAC3B,wBAAsB,GAAG;AACzB,QAAM,UAAU,WAAW,GAAG;AAC9B,MAAI,QAAS,QAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACzD,SAAO,EAAE,SAAS,SAAS,MAAM,IAAI;AACvC;AAMO,SAAS,mBAAmB,IAAmC;AACpE,QAAM,aAAa,KAAK,aAAa,EAAE,GAAG,gBAAgB;AAC1D,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AACpC,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,YAAY,MAAM,CAAC;AAAA,EACpD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,wBACpB,IAC4B;AAC5B,QAAM,OAAO,mBAAmB,EAAE;AAClC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR,cAAc,EAAE,qCAAqC,KAAK,aAAa,EAAE,GAAG,gBAAgB,CAAC;AAAA,IAE/F;AAAA,EACF;AACA,MAAI,CAAC,KAAK,cAAc,CAAC,WAAW,KAAK,UAAU,GAAG;AACpD,UAAM,IAAI;AAAA,MACR,cAAc,EAAE,wCAAwC,KAAK,UAAU;AAAA,IACzE;AAAA,EACF;AACA,QAAM,EAAE,UAAU,IAAI,MAAM,sBAAsB,KAAK,UAAU;AACjE,SAAO;AACT;AAOO,SAAS,kBAAkB,MAAyC;AACzE,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IACpC,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,IAC7C,OAAO,CAAC;AAAA,IACR,YAAY,KAAK,cAAc,CAAC;AAAA,IAChC,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK,YAAY,CAAC;AAAA,IAC5B,QAAQ,KAAK,UAAU,CAAC;AAAA,IACxB,WAAW,KAAK,aAAa,CAAC;AAAA,IAC9B,QAAQ,KAAK,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKxB,SAAS,CAAC;AAAA,IACV,WAAW,KAAK,aAAa,CAAC;AAAA,IAC9B,SAAS,KAAK;AAAA,EAChB;AACF;AAOO,SAAS,2BAAgD;AAC9D,QAAM,MAAM,cAAc;AAC1B,MAAI,CAAC,WAAW,GAAG,EAAG,QAAO,CAAC;AAC9B,QAAM,MAA2B,CAAC;AAClC,aAAW,SAAS,YAAY,GAAG,GAAG;AACpC,UAAM,OAAO,mBAAmB,KAAK;AACrC,QAAI,KAAM,KAAI,KAAK,kBAAkB,IAAI,CAAC;AAAA,EAC5C;AACA,SAAO;AACT;","names":["connector"]}
@@ -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-DOPFBUCX.js";
16
16
  import {
17
17
  REGISTERED_PLATFORM_IDS,
18
18
  loadAdapter
19
- } from "./chunk-D4ELPBB5.js";
19
+ } from "./chunk-TOSNS2BY.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-DWZ5IWF4.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":[]}