@ken-jo/agent-connector 0.4.97 → 0.4.99

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 (387) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +6 -6
  3. package/README.md +604 -596
  4. package/dist/{action-IPFDPOZU.js → action-5OEM3QC4.js} +17 -16
  5. package/dist/action-5OEM3QC4.js.map +1 -0
  6. package/dist/{amazon-q-IT44LEWV.js → amazon-q-7DPVJXJN.js} +6 -6
  7. package/dist/amazon-q-7DPVJXJN.js.map +1 -0
  8. package/dist/{amp-CEWOSWQO.js → amp-BQZOL5GR.js} +4 -4
  9. package/dist/amp-BQZOL5GR.js.map +1 -0
  10. package/dist/{amp-P35FRU2Z.js → amp-VYXZWHXW.js} +7 -7
  11. package/dist/amp-VYXZWHXW.js.map +1 -0
  12. package/dist/{antigravity-LHFEFNMC.js → antigravity-EA4L225L.js} +5 -5
  13. package/dist/antigravity-EA4L225L.js.map +1 -0
  14. package/dist/antigravity-VXHL6ZPM.js +18 -0
  15. package/dist/{antigravity-cli-MOKFSWOU.js → antigravity-cli-B2HXZFYQ.js} +5 -5
  16. package/dist/antigravity-cli-B2HXZFYQ.js.map +1 -0
  17. package/dist/{antigravity-cli-MPE27LPI.js → antigravity-cli-JHQ7JGNX.js} +10 -9
  18. package/dist/antigravity-cli-JHQ7JGNX.js.map +1 -0
  19. package/dist/{audit-3E5JAOP2.js → audit-BO7JQYIH.js} +8 -8
  20. package/dist/{chunk-7C2IPIIB.js → chunk-2Y2MZRLP.js} +3 -3
  21. package/dist/chunk-2Y2MZRLP.js.map +1 -0
  22. package/dist/{chunk-GZZCHJDP.js → chunk-4KIXXJ7B.js} +2 -2
  23. package/dist/chunk-4KIXXJ7B.js.map +1 -0
  24. package/dist/chunk-4NFDNY3C.js +34 -0
  25. package/dist/chunk-4NFDNY3C.js.map +1 -0
  26. package/dist/{chunk-C63AAXK7.js → chunk-4QXCNZMD.js} +5 -5
  27. package/dist/chunk-4QXCNZMD.js.map +1 -0
  28. package/dist/{chunk-Q7PR2WGT.js → chunk-6TF7MHG5.js} +2 -2
  29. package/dist/chunk-6TF7MHG5.js.map +1 -0
  30. package/dist/{chunk-7ZFK2WEO.js → chunk-6WN37NVN.js} +113 -10
  31. package/dist/chunk-6WN37NVN.js.map +1 -0
  32. package/dist/{chunk-GCPEPUD7.js → chunk-6XMV2OOA.js} +6 -6
  33. package/dist/chunk-6XMV2OOA.js.map +1 -0
  34. package/dist/{chunk-XXLCMSD3.js → chunk-BEFVJ2OE.js} +1 -1
  35. package/dist/chunk-BEFVJ2OE.js.map +1 -0
  36. package/dist/{chunk-N4ENLZEU.js → chunk-BV5WK6VQ.js} +19 -19
  37. package/dist/chunk-BV5WK6VQ.js.map +1 -0
  38. package/dist/{chunk-WPTNMYYM.js → chunk-BXPSDNIB.js} +6 -6
  39. package/dist/chunk-BXPSDNIB.js.map +1 -0
  40. package/dist/{chunk-Q4ISQHUQ.js → chunk-FBJQGRET.js} +1 -1
  41. package/dist/chunk-FBJQGRET.js.map +1 -0
  42. package/dist/{chunk-THI4PI4H.js → chunk-FJCUKXGX.js} +2 -2
  43. package/dist/chunk-FJCUKXGX.js.map +1 -0
  44. package/dist/{chunk-L2C5UMKW.js → chunk-FKW5A6HE.js} +23 -7
  45. package/dist/chunk-FKW5A6HE.js.map +1 -0
  46. package/dist/{chunk-NDMNMAKP.js → chunk-G5HAIYRM.js} +43 -43
  47. package/dist/chunk-G5HAIYRM.js.map +1 -0
  48. package/dist/{chunk-SLJI5BOM.js → chunk-GDIAHWNY.js} +2 -2
  49. package/dist/chunk-GDIAHWNY.js.map +1 -0
  50. package/dist/{chunk-QYOUQCC6.js → chunk-GZGLCCEE.js} +1 -1
  51. package/dist/chunk-GZGLCCEE.js.map +1 -0
  52. package/dist/{chunk-PSGTHJQU.js → chunk-H53QZQJI.js} +1 -1
  53. package/dist/chunk-H53QZQJI.js.map +1 -0
  54. package/dist/{chunk-7HHW23AC.js → chunk-IRUYJD23.js} +29 -29
  55. package/dist/chunk-IRUYJD23.js.map +1 -0
  56. package/dist/{chunk-IOJDZJ72.js → chunk-IVAWP6HH.js} +2 -2
  57. package/dist/chunk-IVAWP6HH.js.map +1 -0
  58. package/dist/{chunk-3FINQ2ZF.js → chunk-JTN3AKZO.js} +30 -20
  59. package/dist/chunk-JTN3AKZO.js.map +1 -0
  60. package/dist/{chunk-4GH2KA7V.js → chunk-KHO5WNTP.js} +1 -1
  61. package/dist/chunk-KHO5WNTP.js.map +1 -0
  62. package/dist/{chunk-MLU7NDAZ.js → chunk-KLZBRNFP.js} +12 -12
  63. package/dist/chunk-KLZBRNFP.js.map +1 -0
  64. package/dist/{chunk-3EP5V2F4.js → chunk-M6OS4OLU.js} +3 -3
  65. package/dist/chunk-M6OS4OLU.js.map +1 -0
  66. package/dist/{chunk-UZZTWWGG.js → chunk-MQPNU2CY.js} +1 -1
  67. package/dist/chunk-MQPNU2CY.js.map +1 -0
  68. package/dist/{chunk-LQ55E7QI.js → chunk-N4STCQGP.js} +7 -7
  69. package/dist/chunk-N4STCQGP.js.map +1 -0
  70. package/dist/{chunk-KFPSJRHR.js → chunk-NBYKLUF6.js} +2 -2
  71. package/dist/chunk-NBYKLUF6.js.map +1 -0
  72. package/dist/{chunk-KM2YXO6V.js → chunk-O7SDRBVB.js} +1 -1
  73. package/dist/chunk-O7SDRBVB.js.map +1 -0
  74. package/dist/{chunk-LFWYYWLV.js → chunk-OP4KBXUO.js} +1 -1
  75. package/dist/chunk-OP4KBXUO.js.map +1 -0
  76. package/dist/{chunk-XN7S44U4.js → chunk-OPXAP3AC.js} +1 -1
  77. package/dist/chunk-OPXAP3AC.js.map +1 -0
  78. package/dist/{chunk-TGZFDXNH.js → chunk-PWCBWAM4.js} +6 -4
  79. package/dist/chunk-PWCBWAM4.js.map +1 -0
  80. package/dist/{chunk-Y3K6KJMS.js → chunk-QSY74NAP.js} +2 -2
  81. package/dist/chunk-QSY74NAP.js.map +1 -0
  82. package/dist/{chunk-ZSOS2KOR.js → chunk-RK6VBLXP.js} +1 -1
  83. package/dist/chunk-RK6VBLXP.js.map +1 -0
  84. package/dist/{chunk-FHYR5Q5T.js → chunk-SFY6YLVK.js} +19 -12
  85. package/dist/chunk-SFY6YLVK.js.map +1 -0
  86. package/dist/{chunk-C747DYNT.js → chunk-UM5S7DKK.js} +1 -1
  87. package/dist/chunk-UM5S7DKK.js.map +1 -0
  88. package/dist/{chunk-ILWHLGIC.js → chunk-UMQ6NBQR.js} +2 -2
  89. package/dist/chunk-UMQ6NBQR.js.map +1 -0
  90. package/dist/{chunk-7PSNXUFK.js → chunk-XD7MY6HB.js} +1 -1
  91. package/dist/chunk-XD7MY6HB.js.map +1 -0
  92. package/dist/{chunk-XVEJAFKW.js → chunk-XY3N2U6L.js} +5 -5
  93. package/dist/chunk-XY3N2U6L.js.map +1 -0
  94. package/dist/{chunk-XZWMJLIT.js → chunk-YT43MUSJ.js} +1 -1
  95. package/dist/chunk-YT43MUSJ.js.map +1 -0
  96. package/dist/{claude-code-QBWDT5RT.js → claude-code-LJ6DVXWQ.js} +24 -14
  97. package/dist/claude-code-LJ6DVXWQ.js.map +1 -0
  98. package/dist/{claude-code-FS42PXZO.js → claude-code-NMRKTAP7.js} +4 -4
  99. package/dist/claude-code-NMRKTAP7.js.map +1 -0
  100. package/dist/cli/sdk.js +7 -7
  101. package/dist/cli/sdk.js.map +1 -1
  102. package/dist/cli.js +1 -1
  103. package/dist/cli.js.map +1 -1
  104. package/dist/{cline-JKEAYWVR.js → cline-JAPJNOZQ.js} +6 -6
  105. package/dist/cline-JAPJNOZQ.js.map +1 -0
  106. package/dist/{codebuddy-EB6EYPC3.js → codebuddy-GC7VAE2Y.js} +7 -7
  107. package/dist/codebuddy-GC7VAE2Y.js.map +1 -0
  108. package/dist/{codebuff-5RSXFU6G.js → codebuff-3VODFPD4.js} +6 -6
  109. package/dist/codebuff-3VODFPD4.js.map +1 -0
  110. package/dist/{codebuff-6KWBW6CT.js → codebuff-ETYT5UBL.js} +4 -4
  111. package/dist/codebuff-ETYT5UBL.js.map +1 -0
  112. package/dist/{codex-ART46CDD.js → codex-RXIS5BHM.js} +9 -9
  113. package/dist/codex-RXIS5BHM.js.map +1 -0
  114. package/dist/{codex-DKBAAUCO.js → codex-YAORPCOD.js} +4 -4
  115. package/dist/codex-YAORPCOD.js.map +1 -0
  116. package/dist/{continue-JHZCMSPH.js → continue-MECVDV4Z.js} +8 -8
  117. package/dist/continue-MECVDV4Z.js.map +1 -0
  118. package/dist/{copilot-cli-CLBJACQ5.js → copilot-cli-MZTAWMPW.js} +4 -4
  119. package/dist/copilot-cli-MZTAWMPW.js.map +1 -0
  120. package/dist/{copilot-cli-QVAU5CFM.js → copilot-cli-WVWAWNSM.js} +7 -7
  121. package/dist/copilot-cli-WVWAWNSM.js.map +1 -0
  122. package/dist/{crush-A5C5USRL.js → crush-QBXLNN7T.js} +4 -4
  123. package/dist/crush-QBXLNN7T.js.map +1 -0
  124. package/dist/{crush-7TORBYUL.js → crush-YB3SK6NK.js} +7 -7
  125. package/dist/crush-YB3SK6NK.js.map +1 -0
  126. package/dist/{cursor-D5SWDVSW.js → cursor-DIW43TRZ.js} +3 -3
  127. package/dist/cursor-DIW43TRZ.js.map +1 -0
  128. package/dist/cursor-RSLHA2CY.js +22 -0
  129. package/dist/{define-connector-DTWb46UB.d.ts → define-connector-odRHEC4H.d.ts} +1 -1
  130. package/dist/{detect-JHSA3G3U.js → detect-ALP4G7WG.js} +4 -4
  131. package/dist/detect-ALP4G7WG.js.map +1 -0
  132. package/dist/{devin-SSR7S4WX.js → devin-WPDN3MXE.js} +7 -7
  133. package/dist/devin-WPDN3MXE.js.map +1 -0
  134. package/dist/{doctor-CQJ7TZPA.js → doctor-YEQWCWZJ.js} +27 -26
  135. package/dist/doctor-YEQWCWZJ.js.map +1 -0
  136. package/dist/{droid-45PJ7JGF.js → droid-HS6MX3IL.js} +10 -8
  137. package/dist/droid-HS6MX3IL.js.map +1 -0
  138. package/dist/{droid-PFQ2PZSF.js → droid-U3ZBLQFO.js} +4 -4
  139. package/dist/droid-U3ZBLQFO.js.map +1 -0
  140. package/dist/{gemini-cli-Y6RJMU24.js → gemini-cli-NMVVBO3K.js} +4 -4
  141. package/dist/gemini-cli-NMVVBO3K.js.map +1 -0
  142. package/dist/{gemini-cli-24PFDK7D.js → gemini-cli-VJ6X5DNF.js} +8 -8
  143. package/dist/gemini-cli-VJ6X5DNF.js.map +1 -0
  144. package/dist/{goose-U5D3LCPC.js → goose-3X7ID7YW.js} +9 -9
  145. package/dist/goose-3X7ID7YW.js.map +1 -0
  146. package/dist/{goose-K2HKUTBK.js → goose-64V46Y2W.js} +4 -4
  147. package/dist/goose-64V46Y2W.js.map +1 -0
  148. package/dist/{grok-cli-L2MHAPBY.js → grok-cli-4VFVF2CQ.js} +7 -7
  149. package/dist/grok-cli-4VFVF2CQ.js.map +1 -0
  150. package/dist/{hermes-2S5JUTTJ.js → hermes-AIVUE7LX.js} +10 -8
  151. package/dist/hermes-AIVUE7LX.js.map +1 -0
  152. package/dist/{hermes-MF3NX4ZY.js → hermes-F3EGTK6N.js} +4 -4
  153. package/dist/hermes-F3EGTK6N.js.map +1 -0
  154. package/dist/{hook-VHICXHXL.js → hook-F63I3SD5.js} +17 -16
  155. package/dist/hook-F63I3SD5.js.map +1 -0
  156. package/dist/index.d.ts +3 -3
  157. package/dist/index.js +5 -5
  158. package/dist/{install-WLENR4KF.js → install-23WRRFBY.js} +21 -21
  159. package/dist/install-23WRRFBY.js.map +1 -0
  160. package/dist/{introspect-dNekqPDY.d.ts → introspect-gTx6GzpW.d.ts} +1 -1
  161. package/dist/{jetbrains-copilot-Z4FC3RM3.js → jetbrains-copilot-XBO2JZRO.js} +6 -6
  162. package/dist/jetbrains-copilot-XBO2JZRO.js.map +1 -0
  163. package/dist/{junie-ULGJJIHB.js → junie-UCOS6Q37.js} +6 -6
  164. package/dist/junie-UCOS6Q37.js.map +1 -0
  165. package/dist/{kilo-SMHPOHJ5.js → kilo-SW7BH6EG.js} +4 -4
  166. package/dist/kilo-SW7BH6EG.js.map +1 -0
  167. package/dist/{kilo-WWIJOVWD.js → kilo-T4OXNUYY.js} +8 -8
  168. package/dist/kilo-T4OXNUYY.js.map +1 -0
  169. package/dist/{kilo-cli-QKE2EQLS.js → kilo-cli-I2S3W245.js} +8 -8
  170. package/dist/kilo-cli-I2S3W245.js.map +1 -0
  171. package/dist/{kilo-cli-T2SRRRJS.js → kilo-cli-ZF272ZHN.js} +5 -5
  172. package/dist/kilo-cli-ZF272ZHN.js.map +1 -0
  173. package/dist/{kimi-MSIDEMCI.js → kimi-5FHEXGZD.js} +4 -4
  174. package/dist/kimi-5FHEXGZD.js.map +1 -0
  175. package/dist/{kimi-JMUFTVSF.js → kimi-VLSUFLYV.js} +7 -7
  176. package/dist/kimi-VLSUFLYV.js.map +1 -0
  177. package/dist/{kiro-UWUCRKR3.js → kiro-AQFXGOJX.js} +10 -8
  178. package/dist/kiro-AQFXGOJX.js.map +1 -0
  179. package/dist/{kiro-NYRRBIKW.js → kiro-WGOI5CGT.js} +4 -4
  180. package/dist/kiro-WGOI5CGT.js.map +1 -0
  181. package/dist/{leaderboard-NJWKGMXP.js → leaderboard-YBD3IKC2.js} +7 -7
  182. package/dist/leaderboard-YBD3IKC2.js.map +1 -0
  183. package/dist/{mimo-code-BPNYYIVM.js → mimo-code-KETL7W7D.js} +7 -7
  184. package/dist/mimo-code-KETL7W7D.js.map +1 -0
  185. package/dist/{mistral-vibe-LT3DAJ3L.js → mistral-vibe-3AU7N2CA.js} +6 -6
  186. package/dist/mistral-vibe-3AU7N2CA.js.map +1 -0
  187. package/dist/{mux-AHNUAY5Z.js → mux-BO2L5CTW.js} +6 -6
  188. package/dist/mux-BO2L5CTW.js.map +1 -0
  189. package/dist/{mux-7PIJ7HUQ.js → mux-XM6AP35L.js} +4 -4
  190. package/dist/mux-XM6AP35L.js.map +1 -0
  191. package/dist/{nemoclaw-RE5RZASZ.js → nemoclaw-WA7RSKF2.js} +7 -7
  192. package/dist/nemoclaw-WA7RSKF2.js.map +1 -0
  193. package/dist/{omp-D2EXEPH7.js → omp-UDYJ2PYY.js} +9 -7
  194. package/dist/omp-UDYJ2PYY.js.map +1 -0
  195. package/dist/{open-interpreter-SSGBEUKX.js → open-interpreter-WMBNM4MQ.js} +6 -6
  196. package/dist/open-interpreter-WMBNM4MQ.js.map +1 -0
  197. package/dist/openclaw-3DHVHX5P.js +17 -0
  198. package/dist/{openclaw-KAFTNLR2.js → openclaw-5EQFGOWL.js} +4 -4
  199. package/dist/openclaw-5EQFGOWL.js.map +1 -0
  200. package/dist/{opencode-YKUKZY3J.js → opencode-IKMI3RI7.js} +7 -7
  201. package/dist/opencode-IKMI3RI7.js.map +1 -0
  202. package/dist/{opencode-Z65JYILL.js → opencode-QGP4ALQY.js} +4 -4
  203. package/dist/opencode-QGP4ALQY.js.map +1 -0
  204. package/dist/{openhands-SEZMCUC3.js → openhands-QGXMQR6I.js} +7 -7
  205. package/dist/openhands-QGXMQR6I.js.map +1 -0
  206. package/dist/{package-JECVQEFT.js → package-TAUTPP7Z.js} +15 -15
  207. package/dist/package-TAUTPP7Z.js.map +1 -0
  208. package/dist/{pi-YPYV2ULH.js → pi-A2HCGRJG.js} +9 -7
  209. package/dist/pi-A2HCGRJG.js.map +1 -0
  210. package/dist/{pi-DLKCPOJE.js → pi-GGDQREVG.js} +4 -4
  211. package/dist/pi-GGDQREVG.js.map +1 -0
  212. package/dist/{qwen-code-ZHCLCYMC.js → qwen-code-DNQTGJOF.js} +35 -17
  213. package/dist/qwen-code-DNQTGJOF.js.map +1 -0
  214. package/dist/{qwen-code-26CSFDX6.js → qwen-code-GQILBJ5B.js} +4 -4
  215. package/dist/qwen-code-GQILBJ5B.js.map +1 -0
  216. package/dist/{roo-code-HSAE2CJU.js → roo-code-SUWI4BBN.js} +4 -4
  217. package/dist/roo-code-SUWI4BBN.js.map +1 -0
  218. package/dist/{roo-code-7F4HYB34.js → roo-code-VI2ZKTZC.js} +6 -6
  219. package/dist/roo-code-VI2ZKTZC.js.map +1 -0
  220. package/dist/runtime/index.d.ts +9 -8
  221. package/dist/runtime/index.js +15 -14
  222. package/dist/sdk/index.d.ts +4 -4
  223. package/dist/sdk/index.js +6 -6
  224. package/dist/sdk/index.js.map +1 -1
  225. package/dist/sdk/test.d.ts +2 -2
  226. package/dist/sdk/test.js +12 -11
  227. package/dist/{serve-LYSBH43G.js → serve-6MDLRQS2.js} +17 -16
  228. package/dist/serve-6MDLRQS2.js.map +1 -0
  229. package/dist/{status-PHKKGEW5.js → status-IUP75XHP.js} +11 -11
  230. package/dist/status-IUP75XHP.js.map +1 -0
  231. package/dist/{statusline-ZDD4OZ4F.js → statusline-PRODZV2J.js} +16 -15
  232. package/dist/statusline-PRODZV2J.js.map +1 -0
  233. package/dist/{synthetic-LUT6V6SD.js → synthetic-6FRA7P5Q.js} +4 -4
  234. package/dist/synthetic-6FRA7P5Q.js.map +1 -0
  235. package/dist/{telemetry-HDGMI2BK.js → telemetry-X4PLZEDB.js} +10 -10
  236. package/dist/telemetry-X4PLZEDB.js.map +1 -0
  237. package/dist/{trae-SIQI3C66.js → trae-CPPYASR2.js} +4 -4
  238. package/dist/trae-CPPYASR2.js.map +1 -0
  239. package/dist/{trae-Y67HSDOW.js → trae-VOJXUYRN.js} +6 -6
  240. package/dist/trae-VOJXUYRN.js.map +1 -0
  241. package/dist/{types-JyhpJHJa.d.ts → types-DWXnnx-N.d.ts} +96 -22
  242. package/dist/{uninstall-RWO2GM2I.js → uninstall-BSIJ4QXN.js} +21 -21
  243. package/dist/uninstall-BSIJ4QXN.js.map +1 -0
  244. package/dist/{upgrade-YJCXNUEE.js → upgrade-ZSICU3NE.js} +21 -21
  245. package/dist/upgrade-ZSICU3NE.js.map +1 -0
  246. package/dist/{usage-IWO4RQN6.js → usage-EQUNVSCT.js} +4 -4
  247. package/dist/usage-EQUNVSCT.js.map +1 -0
  248. package/dist/{usage-event-AFFKCLHE.js → usage-event-J5CQ6LNT.js} +16 -15
  249. package/dist/usage-event-J5CQ6LNT.js.map +1 -0
  250. package/dist/{vscode-copilot-5CI5B3X6.js → vscode-copilot-ZBXRPKGG.js} +7 -7
  251. package/dist/vscode-copilot-ZBXRPKGG.js.map +1 -0
  252. package/dist/{warp-5YBSP7FP.js → warp-NRBBJQR2.js} +4 -4
  253. package/dist/warp-NRBBJQR2.js.map +1 -0
  254. package/dist/{warp-INAKVGBV.js → warp-S444B2S3.js} +9 -7
  255. package/dist/warp-S444B2S3.js.map +1 -0
  256. package/dist/{windsurf-4HM57QAY.js → windsurf-R6D2JUTN.js} +6 -6
  257. package/dist/windsurf-R6D2JUTN.js.map +1 -0
  258. package/dist/{zed-YN5X37PQ.js → zed-DQV3ZUCU.js} +10 -8
  259. package/dist/zed-DQV3ZUCU.js.map +1 -0
  260. package/dist/{zed-XXCNYOIN.js → zed-XJFOOKCO.js} +4 -4
  261. package/dist/zed-XJFOOKCO.js.map +1 -0
  262. package/package.json +88 -88
  263. package/dist/action-IPFDPOZU.js.map +0 -1
  264. package/dist/amazon-q-IT44LEWV.js.map +0 -1
  265. package/dist/amp-CEWOSWQO.js.map +0 -1
  266. package/dist/amp-P35FRU2Z.js.map +0 -1
  267. package/dist/antigravity-LHFEFNMC.js.map +0 -1
  268. package/dist/antigravity-TWIWXTQH.js +0 -18
  269. package/dist/antigravity-cli-MOKFSWOU.js.map +0 -1
  270. package/dist/antigravity-cli-MPE27LPI.js.map +0 -1
  271. package/dist/chunk-3EP5V2F4.js.map +0 -1
  272. package/dist/chunk-3FINQ2ZF.js.map +0 -1
  273. package/dist/chunk-4GH2KA7V.js.map +0 -1
  274. package/dist/chunk-7C2IPIIB.js.map +0 -1
  275. package/dist/chunk-7HHW23AC.js.map +0 -1
  276. package/dist/chunk-7PSNXUFK.js.map +0 -1
  277. package/dist/chunk-7ZFK2WEO.js.map +0 -1
  278. package/dist/chunk-C63AAXK7.js.map +0 -1
  279. package/dist/chunk-C747DYNT.js.map +0 -1
  280. package/dist/chunk-FHYR5Q5T.js.map +0 -1
  281. package/dist/chunk-GCPEPUD7.js.map +0 -1
  282. package/dist/chunk-GZZCHJDP.js.map +0 -1
  283. package/dist/chunk-ILWHLGIC.js.map +0 -1
  284. package/dist/chunk-IOJDZJ72.js.map +0 -1
  285. package/dist/chunk-KFPSJRHR.js.map +0 -1
  286. package/dist/chunk-KM2YXO6V.js.map +0 -1
  287. package/dist/chunk-L2C5UMKW.js.map +0 -1
  288. package/dist/chunk-LFWYYWLV.js.map +0 -1
  289. package/dist/chunk-LQ55E7QI.js.map +0 -1
  290. package/dist/chunk-MLU7NDAZ.js.map +0 -1
  291. package/dist/chunk-N4ENLZEU.js.map +0 -1
  292. package/dist/chunk-NDMNMAKP.js.map +0 -1
  293. package/dist/chunk-PSGTHJQU.js.map +0 -1
  294. package/dist/chunk-Q4ISQHUQ.js.map +0 -1
  295. package/dist/chunk-Q7PR2WGT.js.map +0 -1
  296. package/dist/chunk-QYOUQCC6.js.map +0 -1
  297. package/dist/chunk-SLJI5BOM.js.map +0 -1
  298. package/dist/chunk-TGZFDXNH.js.map +0 -1
  299. package/dist/chunk-THI4PI4H.js.map +0 -1
  300. package/dist/chunk-UZZTWWGG.js.map +0 -1
  301. package/dist/chunk-WPTNMYYM.js.map +0 -1
  302. package/dist/chunk-XN7S44U4.js.map +0 -1
  303. package/dist/chunk-XVEJAFKW.js.map +0 -1
  304. package/dist/chunk-XXLCMSD3.js.map +0 -1
  305. package/dist/chunk-XZWMJLIT.js.map +0 -1
  306. package/dist/chunk-Y3K6KJMS.js.map +0 -1
  307. package/dist/chunk-ZSOS2KOR.js.map +0 -1
  308. package/dist/claude-code-FS42PXZO.js.map +0 -1
  309. package/dist/claude-code-QBWDT5RT.js.map +0 -1
  310. package/dist/cline-JKEAYWVR.js.map +0 -1
  311. package/dist/codebuddy-EB6EYPC3.js.map +0 -1
  312. package/dist/codebuff-5RSXFU6G.js.map +0 -1
  313. package/dist/codebuff-6KWBW6CT.js.map +0 -1
  314. package/dist/codex-ART46CDD.js.map +0 -1
  315. package/dist/codex-DKBAAUCO.js.map +0 -1
  316. package/dist/continue-JHZCMSPH.js.map +0 -1
  317. package/dist/copilot-cli-CLBJACQ5.js.map +0 -1
  318. package/dist/copilot-cli-QVAU5CFM.js.map +0 -1
  319. package/dist/crush-7TORBYUL.js.map +0 -1
  320. package/dist/crush-A5C5USRL.js.map +0 -1
  321. package/dist/cursor-D5SWDVSW.js.map +0 -1
  322. package/dist/cursor-W7I4QUFO.js +0 -22
  323. package/dist/detect-JHSA3G3U.js.map +0 -1
  324. package/dist/devin-SSR7S4WX.js.map +0 -1
  325. package/dist/doctor-CQJ7TZPA.js.map +0 -1
  326. package/dist/droid-45PJ7JGF.js.map +0 -1
  327. package/dist/droid-PFQ2PZSF.js.map +0 -1
  328. package/dist/gemini-cli-24PFDK7D.js.map +0 -1
  329. package/dist/gemini-cli-Y6RJMU24.js.map +0 -1
  330. package/dist/goose-K2HKUTBK.js.map +0 -1
  331. package/dist/goose-U5D3LCPC.js.map +0 -1
  332. package/dist/grok-cli-L2MHAPBY.js.map +0 -1
  333. package/dist/hermes-2S5JUTTJ.js.map +0 -1
  334. package/dist/hermes-MF3NX4ZY.js.map +0 -1
  335. package/dist/hook-VHICXHXL.js.map +0 -1
  336. package/dist/install-WLENR4KF.js.map +0 -1
  337. package/dist/jetbrains-copilot-Z4FC3RM3.js.map +0 -1
  338. package/dist/junie-ULGJJIHB.js.map +0 -1
  339. package/dist/kilo-SMHPOHJ5.js.map +0 -1
  340. package/dist/kilo-WWIJOVWD.js.map +0 -1
  341. package/dist/kilo-cli-QKE2EQLS.js.map +0 -1
  342. package/dist/kilo-cli-T2SRRRJS.js.map +0 -1
  343. package/dist/kimi-JMUFTVSF.js.map +0 -1
  344. package/dist/kimi-MSIDEMCI.js.map +0 -1
  345. package/dist/kiro-NYRRBIKW.js.map +0 -1
  346. package/dist/kiro-UWUCRKR3.js.map +0 -1
  347. package/dist/leaderboard-NJWKGMXP.js.map +0 -1
  348. package/dist/mimo-code-BPNYYIVM.js.map +0 -1
  349. package/dist/mistral-vibe-LT3DAJ3L.js.map +0 -1
  350. package/dist/mux-7PIJ7HUQ.js.map +0 -1
  351. package/dist/mux-AHNUAY5Z.js.map +0 -1
  352. package/dist/nemoclaw-RE5RZASZ.js.map +0 -1
  353. package/dist/omp-D2EXEPH7.js.map +0 -1
  354. package/dist/open-interpreter-SSGBEUKX.js.map +0 -1
  355. package/dist/openclaw-IJ33K45M.js +0 -17
  356. package/dist/openclaw-KAFTNLR2.js.map +0 -1
  357. package/dist/opencode-YKUKZY3J.js.map +0 -1
  358. package/dist/opencode-Z65JYILL.js.map +0 -1
  359. package/dist/openhands-SEZMCUC3.js.map +0 -1
  360. package/dist/package-JECVQEFT.js.map +0 -1
  361. package/dist/pi-DLKCPOJE.js.map +0 -1
  362. package/dist/pi-YPYV2ULH.js.map +0 -1
  363. package/dist/qwen-code-26CSFDX6.js.map +0 -1
  364. package/dist/qwen-code-ZHCLCYMC.js.map +0 -1
  365. package/dist/roo-code-7F4HYB34.js.map +0 -1
  366. package/dist/roo-code-HSAE2CJU.js.map +0 -1
  367. package/dist/serve-LYSBH43G.js.map +0 -1
  368. package/dist/status-PHKKGEW5.js.map +0 -1
  369. package/dist/statusline-ZDD4OZ4F.js.map +0 -1
  370. package/dist/synthetic-LUT6V6SD.js.map +0 -1
  371. package/dist/telemetry-HDGMI2BK.js.map +0 -1
  372. package/dist/trae-SIQI3C66.js.map +0 -1
  373. package/dist/trae-Y67HSDOW.js.map +0 -1
  374. package/dist/uninstall-RWO2GM2I.js.map +0 -1
  375. package/dist/upgrade-YJCXNUEE.js.map +0 -1
  376. package/dist/usage-IWO4RQN6.js.map +0 -1
  377. package/dist/usage-event-AFFKCLHE.js.map +0 -1
  378. package/dist/vscode-copilot-5CI5B3X6.js.map +0 -1
  379. package/dist/warp-5YBSP7FP.js.map +0 -1
  380. package/dist/warp-INAKVGBV.js.map +0 -1
  381. package/dist/windsurf-4HM57QAY.js.map +0 -1
  382. package/dist/zed-XXCNYOIN.js.map +0 -1
  383. package/dist/zed-YN5X37PQ.js.map +0 -1
  384. /package/dist/{antigravity-TWIWXTQH.js.map → antigravity-VXHL6ZPM.js.map} +0 -0
  385. /package/dist/{audit-3E5JAOP2.js.map → audit-BO7JQYIH.js.map} +0 -0
  386. /package/dist/{cursor-W7I4QUFO.js.map → cursor-RSLHA2CY.js.map} +0 -0
  387. /package/dist/{openclaw-IJ33K45M.js.map → openclaw-3DHVHX5P.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/continue/index.ts"],"sourcesContent":["/**\r\n * adapters/continue — Continue (the `cn` terminal agent / Continue.dev) adapter.\r\n *\r\n * Continue is a **json-stdio** host: its `cn` CLI ships a Claude-Code-COMPATIBLE\r\n * hooks system (primary-verified — continuedev/continue PR #11029,\r\n * extensions/cli/src/hooks/{types.ts,hookConfig.ts}). The hooks live in a\r\n * SEPARATE settings.json file (NOT the YAML config.yaml that holds MCP servers),\r\n * so getServerConfigPath ≠ getHookConfigPath:\r\n *\r\n * - MCP servers → config.yaml `mcpServers` (a YAML ARRAY — see below). UNCHANGED.\r\n * - Hooks → settings.json under `hooks`, keyed by PascalCase event name,\r\n * each value an array of { matcher?, hooks:[{ type:\"command\",\r\n * command }] } — BYTE-IDENTICAL to Claude Code's shape.\r\n *\r\n * Hook config path (hookConfig.ts getSettingsFilePaths):\r\n * - user-global → ~/.continue/settings.json (honor CONTINUE_GLOBAL_DIR env if\r\n * set, else ~/.continue);\r\n * - project → <projectDir>/.continue/settings.json.\r\n *\r\n * Supported events (continue's HOOK_EVENT_NAMES ∩ AC canonical): PreToolUse,\r\n * PostToolUse, PostToolUseFailure, UserPromptSubmit, SessionStart, SessionEnd,\r\n * Stop, Notification, SubagentStart, SubagentStop, PermissionRequest, PreCompact\r\n * — all PascalCase 1:1 with the canonical name. Continue has NO PostCompact (not\r\n * in HOOK_EVENT_NAMES), so that one warn-skips at install.\r\n *\r\n * Continue's HOOK_EVENT_NAMES ALSO carries five host-specific events with no\r\n * canonical analog (ConfigChange, TeammateIdle, TaskCompleted, WorktreeCreate,\r\n * WorktreeRemove — each a real HookInput member). They sit below the ≥3-host\r\n * promotion bar, so they are reachable via the nativeHooks passthrough\r\n * (supportsNativeHooks: true) rather than the normalized API — the SAME proven\r\n * pattern as copilot-cli / hermes / jetbrains-copilot.\r\n *\r\n * Output contract is Claude-identical (HookOutput): blocking = exit code 2 OR\r\n * top-level { decision:\"block\", reason }; PreToolUse permission via\r\n * hookSpecificOutput.permissionDecision (allow|deny|ask) + updatedInput; context\r\n * injection via hookSpecificOutput.additionalContext; PermissionRequest via the\r\n * nested decision{ behavior }. So parseEvent/formatReply MIRROR the claude-code\r\n * adapter exactly (the install layout mirrors droid's separate-hooks-file host).\r\n *\r\n * MCP config is YAML (primary-verified — docs.continue.dev/customize/deep-dives/\r\n * mcp + /reference + /customize/mcp-tools + /guides/cli):\r\n * - user/global scope → ~/.continue/config.yaml\r\n * (Windows %USERPROFILE%\\.continue\\config.yaml — covered\r\n * by homedir())\r\n * - project scope → <projectDir>/.continue/config.yaml (workspace root)\r\n *\r\n * Root key `mcpServers` is a YAML **ARRAY** of server objects (NOT a keyed\r\n * object/map like the JSON hosts). Each entry:\r\n * { name (required — the server's display id), command (required for stdio),\r\n * type? (stdio|sse|streamable-http; defaults to stdio), args? (string[]),\r\n * env? (map), cwd? (string), url (required for sse/streamable-http remote) }.\r\n * The connector id is written as the entry's `name`; install is set-if-absent by\r\n * name (append when missing; leave a present entry untouched unless it differs →\r\n * update), preserving every sibling entry. Uninstall removes ONLY the entry whose\r\n * name === connector id.\r\n *\r\n * Because the file is YAML, the BaseAdapter JSON helpers do not apply; this\r\n * adapter merges via core/yaml's readYaml/writeYaml (same lib as goose/hermes),\r\n * preserving any unrelated config the user authored.\r\n *\r\n * NOT primary-verified → deliberately NOT emitted: `apiKey`, `requestOptions`,\r\n * `connectionTimeout`. Per-server block files (.continue/mcpServers/*.yaml)\r\n * auto-discovery for the `cn` CLI is also unverified — both scopes use the\r\n * verified config.yaml `mcpServers` array.\r\n *\r\n * Memory / rules (primary-verified — docs.continue.dev/customize/deep-dives/\r\n * rules): a `.continue/rules` folder at the WORKSPACE ROOT holds `.md` rule\r\n * files; each carries a YAML frontmatter whose `alwaysApply: true` field makes\r\n * the rule \"always included, regardless of file context\" (the docs' literal\r\n * Activation behavior table). AC owns a DEDICATED file there\r\n * (`.continue/rules/agent-connector.md`) — unlike the cline/amazon-q memory\r\n * targets (which use the base managed-block engine), this file MUST LEAD with\r\n * the always-on frontmatter, so we write the WHOLE file (frontmatter + body) via\r\n * the content-file writers and own it end-to-end (install creates, uninstall\r\n * deletes). The connector's memory `content` is host-agnostic (no frontmatter of\r\n * its own — MemoryDef forbids it), so the always-on directive is an adapter-level\r\n * wrapper, not part of the connector payload. PROJECT SCOPE ONLY (the `cn` CLI's\r\n * user/global rules directory is not primary-verified → user scope skip-warns).\r\n */\r\n\r\nimport { existsSync, statSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { basename, join } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n NotificationEvent,\r\n PermissionRequestEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PostToolUseFailureEvent,\r\n PreCompactEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n StopEvent,\r\n SubagentStartEvent,\r\n SubagentStopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { readYaml, writeYaml } from \"../../core/yaml.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n buildHomeBinHookCommand,\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport {\r\n type ClaudeHookEvent,\r\n type ClaudeWireInput,\r\n extractSessionId,\r\n normalizeSessionSource,\r\n toolResponseToString,\r\n} from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"continue\";\r\n/** Root key under which Continue stores MCP servers in config.yaml — a YAML ARRAY. */\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/**\r\n * Canonical events Continue actually fires (continue's HOOK_EVENT_NAMES ∩ the AC\r\n * canonical set; PR #11029 extensions/cli/src/hooks/types.ts). Names are\r\n * Claude-identical PascalCase, registered directly. Continue ships PreCompact\r\n * (PreCompactInput in the HookInput union) but NOT PostCompact — that one\r\n * warn-skips at install time.\r\n */\r\nconst SUPPORTED_EVENTS: ReadonlySet<HookEventName> = new Set<HookEventName>([\r\n \"PreToolUse\",\r\n \"PostToolUse\",\r\n \"PostToolUseFailure\",\r\n \"UserPromptSubmit\",\r\n \"SessionStart\",\r\n \"SessionEnd\",\r\n \"Stop\",\r\n \"Notification\",\r\n \"SubagentStart\",\r\n \"SubagentStop\",\r\n \"PermissionRequest\",\r\n \"PreCompact\",\r\n]);\r\n\r\n/** A single Continue native hook registration entry (Claude-shaped, nested). */\r\ninterface ContinueHookEntry {\r\n matcher: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of Continue's settings.json (only the parts the hook install touches). */\r\ninterface ContinueSettingsFile {\r\n hooks?: Record<string, ContinueHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Native Continue MCP server entry shapes. `name` is the required display id (we\r\n * key on it). stdio omits `type` (the documented default); remote emits an\r\n * explicit `type` + `url` and carries no command. NO apiKey/requestOptions/\r\n * connectionTimeout (not primary-verified → never emitted).\r\n */\r\ninterface ContinueStdioServer {\r\n name: string;\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n cwd?: string;\r\n}\r\ninterface ContinueRemoteServer {\r\n name: string;\r\n type: \"sse\" | \"streamable-http\";\r\n url: string;\r\n}\r\ntype ContinueServer = ContinueStdioServer | ContinueRemoteServer;\r\n\r\nexport class ContinueAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Continue\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: WIRED. Continue reads `.continue/rules/*.md` (NOT\r\n // AGENTS.md), so the AGENTS.md-first BaseAdapter default does not apply — the\r\n // installMemory/uninstallMemory overrides below write a DEDICATED\r\n // agent-connector-owned file (<projectDir>/.continue/rules/agent-connector.md)\r\n // that LEADS with `alwaysApply: true` frontmatter (always-included). Project\r\n // scope only; user scope skip-warns (no verified user/global rules dir).\r\n supportsMemory: true,\r\n //\r\n // json-stdio: the `cn` CLI ships a Claude-Code-COMPATIBLE hooks system (PR\r\n // #11029). Every event in HOOK_EVENT_NAMES ∩ canonical fires natively; the\r\n // names are PascalCase 1:1 with Claude. PreCompact IS in continue's set\r\n // (PreCompactInput); only PostCompact is absent → that stays false (install\r\n // warn-skips it).\r\n preToolUse: true,\r\n postToolUse: true,\r\n postToolUseFailure: true,\r\n preCompact: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n userPromptSubmit: true,\r\n stop: true,\r\n notification: true,\r\n subagentStart: true,\r\n subagentStop: true,\r\n permissionRequest: true,\r\n // Continue's PreToolUse output carries updatedInput (HookOutput\r\n // PreToolUseHookOutput.updatedInput) → it CAN rewrite tool input. It cannot\r\n // rewrite already-emitted tool output (PostToolUse exposes updatedMCPToolOutput,\r\n // but — matching the claude-code adapter's conservative stance — we do not\r\n // wire output rewrite). It honors hookSpecificOutput.additionalContext.\r\n canModifyArgs: true,\r\n canModifyOutput: false,\r\n canInjectSessionContext: true,\r\n // Continue registers stdio + remote (SSE / Streamable HTTP) MCP servers\r\n // (primary-verified: type ∈ stdio|sse|streamable-http). Mapped to the\r\n // framework's transport enum: stdio + sse + http (http = streamable-http).\r\n transports: [\"stdio\", \"sse\", \"http\"],\r\n // Native (passthrough) hooks: continue's HOOK_EVENT_NAMES carries five\r\n // host-specific events with NO canonical analog — ConfigChange, TeammateIdle,\r\n // TaskCompleted, WorktreeCreate, WorktreeRemove (each a real HookInput\r\n // member, types.ts:214-241). They sit below the ≥3-host promotion bar, so a\r\n // connector reaches them via platforms.continue.nativeHooks; installHooks\r\n // files the event-name key VERBATIM and the generic uninstallHooks reverses\r\n // it by connector-id ownership (same proven pattern as copilot-cli/hermes/\r\n // jetbrains-copilot).\r\n supportsNativeHooks: true,\r\n // Content surfaces: Continue's Rules/prompts are NOT wired here. Leave the\r\n // supports* flags UNSET (base skip-warns).\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = this.userConfigDir();\r\n const userCfg = join(userDir, \"config.yaml\");\r\n const projCfg = join(projectDir, \".continue\", \"config.yaml\");\r\n\r\n const userInstalled = existsSync(userDir) || existsSync(userCfg);\r\n const projInstalled = existsSync(projCfg);\r\n const installed = userInstalled || projInstalled;\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const configPath = scope === \"project\" ? projCfg : userCfg;\r\n\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? `found Continue config (${scope}) at ${configPath}`\r\n : `no Continue config at ${userCfg} or ${projCfg}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n /** ~/.continue (user) or <projectDir>/.continue (project). */\r\n override getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".continue\")\r\n : this.userConfigDir();\r\n }\r\n\r\n /** MCP config: config.yaml under the per-scope .continue dir. */\r\n override getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"config.yaml\");\r\n }\r\n\r\n /**\r\n * Hooks live in a SEPARATE settings.json (NOT the YAML config.yaml that holds\r\n * MCP servers): user-global → <CONTINUE_GLOBAL_DIR|~/.continue>/settings.json;\r\n * project → <projectDir>/.continue/settings.json (hookConfig.ts\r\n * getSettingsFilePaths). The user-scope dir honors CONTINUE_GLOBAL_DIR exactly\r\n * as the `cn` CLI's hook loader does — this is INTENTIONALLY independent of\r\n * getConfigDir/userConfigDir (the MCP path), which the hook env must not perturb.\r\n */\r\n override getHookConfigPath(ctx: InstallContext): string {\r\n const dir =\r\n ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".continue\")\r\n : process.env.CONTINUE_GLOBAL_DIR || join(homedir(), \".continue\");\r\n return join(dir, \"settings.json\");\r\n }\r\n\r\n /** ~/.continue — homedir() covers %USERPROFILE% on Windows. */\r\n private userConfigDir(): string {\r\n return join(homedir(), \".continue\");\r\n }\r\n\r\n // ── Memory surface: the `.continue/rules` content tree ────────────────────\r\n // A DEDICATED agent-connector-owned file that MUST LEAD with `alwaysApply:\r\n // true` frontmatter (always-included). Because of that leading frontmatter we\r\n // do NOT use the base managed-block engine (it appends a block at EOF and\r\n // cannot guarantee a leading frontmatter, nor delete a file that retains\r\n // frontmatter on uninstall) — instead we own the whole file via the\r\n // content-file writers: install writes/updates it idempotently, uninstall\r\n // deletes it. PROJECT SCOPE ONLY (the user/global rules dir is unverified).\r\n\r\n /** <projectDir>/.continue/rules/agent-connector.md — the dedicated owned file. */\r\n private memoryFilePath(ctx: InstallContext): string {\r\n return join(ctx.projectDir, \".continue\", \"rules\", \"agent-connector.md\");\r\n }\r\n\r\n /** True when `<projectDir>/.continue/rules` exists and is a regular FILE. */\r\n private rulesDirIsFile(ctx: InstallContext): boolean {\r\n const p = join(ctx.projectDir, \".continue\", \"rules\");\r\n if (!existsSync(p)) return false;\r\n try {\r\n return statSync(p).isFile();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Compose the dedicated rule file: `alwaysApply: true` frontmatter (the\r\n * always-on activation directive) + every declared memory entry's content,\r\n * joined with a blank line. The connector's content is host-agnostic markdown\r\n * (MemoryDef forbids its own frontmatter), so the directive is ours to add.\r\n */\r\n private renderMemoryFile(ctx: InstallContext): string {\r\n const body = (ctx.connector.memory ?? [])\r\n .map((m) => m.content.trim())\r\n .filter((c) => c.length > 0)\r\n .join(\"\\n\\n\");\r\n return this.renderFrontmatterMd({ alwaysApply: true }, body);\r\n }\r\n\r\n override installMemory(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const entries = connector.memory ?? [];\r\n if (connector.platforms[this.id]?.memory === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: `memory disabled for ${this.id}` }];\r\n }\r\n if (entries.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no memory\" }];\r\n }\r\n // Continue's verified rules dir is PROJECT scope only.\r\n if (ctx.scope !== \"project\") {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n detail:\r\n `no ${ctx.scope}-scope rules dir verified for continue ` +\r\n `(.continue/rules is project-scope); ${entries.length} skipped`,\r\n },\r\n ];\r\n }\r\n // Rules-path collision: never write under a `.continue/rules` that is a FILE.\r\n if (this.rulesDirIsFile(ctx)) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"warn\",\r\n path: join(ctx.projectDir, \".continue\", \"rules\"),\r\n detail:\r\n \"existing .continue/rules is a file, not a directory; left untouched — \" +\r\n \"convert it to a .continue/rules/ directory to receive agent-connector memory\",\r\n },\r\n ];\r\n }\r\n return [this.writeContentFile(this.memoryFilePath(ctx), this.renderMemoryFile(ctx), ctx.dryRun)];\r\n }\r\n\r\n override uninstallMemory(ctx: InstallContext): ChangeRecord[] {\r\n // Only the project-scope dedicated file was ever written; a `.continue/rules`\r\n // that is a FILE was never ours (installMemory warned), so leave it.\r\n if (ctx.scope !== \"project\" || this.rulesDirIsFile(ctx)) {\r\n const path = this.memoryFilePath(ctx);\r\n return [\r\n { platform: this.id, action: \"skip\", path, detail: `${basename(path)} absent` },\r\n ];\r\n }\r\n return [this.removeContentFile(this.memoryFilePath(ctx), ctx.dryRun)];\r\n }\r\n\r\n // ── MCP server install / uninstall (YAML — ARRAY merge) ───────────────────\r\n\r\n override installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector, dryRun } = ctx;\r\n const path = this.getServerConfigPath(ctx);\r\n const server = this.effectiveServer(ctx);\r\n\r\n if (!server) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: connector.server\r\n ? \"server registration disabled for continue\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n const entry = this.renderServerEntry(ctx, server);\r\n const symlink = this.symlinkPathWarning(path);\r\n if (symlink) return [symlink];\r\n\r\n // Merge into existing YAML, preserving every other config key + sibling\r\n // mcpServers entry. mcpServers is a YAML ARRAY; we key on `name`.\r\n const cfg = readYaml<Record<string, unknown>>(path) ?? {};\r\n // NEVER clobber a malformed `mcpServers`: if it exists and is not an array,\r\n // skip-and-warn (symmetric with uninstallServer) rather than silently\r\n // replacing the user's hand-written value.\r\n const existingRoot = cfg[MCP_ROOT_KEY];\r\n if (existingRoot !== undefined && !Array.isArray(existingRoot)) {\r\n return [{ platform: this.id, action: \"skip\", path, detail: `${MCP_ROOT_KEY} is not a YAML array — left untouched (manual fix needed)` }];\r\n }\r\n\r\n const list = this.serverArray(cfg);\r\n const idx = list.findIndex((e) => this.entryName(e) === connector.id);\r\n\r\n let action: ChangeRecord[\"action\"];\r\n if (idx < 0) {\r\n list.push(entry);\r\n action = \"create\";\r\n } else if (JSON.stringify(list[idx]) === JSON.stringify(entry)) {\r\n action = \"skip\";\r\n } else {\r\n list[idx] = entry;\r\n action = \"update\";\r\n }\r\n\r\n if (action !== \"skip\") {\r\n cfg[MCP_ROOT_KEY] = list;\r\n writeYaml(path, cfg, dryRun);\r\n }\r\n return [{ platform: this.id, action, path, detail: `${MCP_ROOT_KEY}[name=${connector.id}]` }];\r\n }\r\n\r\n override uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector, dryRun } = ctx;\r\n const path = this.getServerConfigPath(ctx);\r\n const symlink = this.symlinkPathWarning(path);\r\n if (symlink) return [symlink];\r\n\r\n const cfg = readYaml<Record<string, unknown>>(path);\r\n const rawList = cfg?.[MCP_ROOT_KEY];\r\n if (!cfg || !Array.isArray(rawList)) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `${MCP_ROOT_KEY}[name=${connector.id}] absent`,\r\n },\r\n ];\r\n }\r\n\r\n const list = rawList as ContinueServer[];\r\n const kept = list.filter((e) => this.entryName(e) !== connector.id);\r\n if (kept.length === list.length) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `${MCP_ROOT_KEY}[name=${connector.id}] absent`,\r\n },\r\n ];\r\n }\r\n\r\n cfg[MCP_ROOT_KEY] = kept;\r\n writeYaml(path, cfg, dryRun);\r\n return [\r\n { platform: this.id, action: \"remove\", path, detail: `${MCP_ROOT_KEY}[name=${connector.id}]` },\r\n ];\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into Continue's native mcpServers entry.\r\n * Continue has no documented native ${env:VAR} token — resolve every reference\r\n * to a literal at install time (the safe path, matching amazon-q/goose/hermes).\r\n * Honors the telemetry serve-wrapper for stdio.\r\n */\r\n private renderServerEntry(ctx: InstallContext, server: ServerDef): ContinueServer {\r\n const transport: Transport = server.transport;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n command = resolveEnvRefsDeep(command);\r\n args = resolveEnvRefsDeep(args);\r\n\r\n // stdio omits `type` (Continue's documented default).\r\n const entry: ContinueStdioServer = {\r\n name: ctx.connector.id,\r\n command,\r\n };\r\n if (args.length > 0) entry.args = args;\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n if (typeof server.cwd === \"string\" && server.cwd.length > 0) {\r\n entry.cwd = resolveEnvRefsDeep(server.cwd);\r\n }\r\n return entry;\r\n }\r\n\r\n // Remote (sse / http / ws) — Continue registers an explicit type + url. The\r\n // framework's `http` transport maps to Continue's \"streamable-http\"; `sse`\r\n // maps to \"sse\". (ws has no Continue analog → fall through to streamable-http\r\n // URL form, which is the closest documented remote shape.)\r\n const type: ContinueRemoteServer[\"type\"] = transport === \"sse\" ? \"sse\" : \"streamable-http\";\r\n return {\r\n name: ctx.connector.id,\r\n type,\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n };\r\n }\r\n\r\n /**\r\n * Render stdio env values. Continue has no documented native interpolation\r\n * token, so resolve `${env:VAR}` references to literals at install time.\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(resolveEnvRefsDeep({ ...env }))) {\r\n out[k] = String(v);\r\n }\r\n return out;\r\n }\r\n\r\n // ── Hook install / uninstall (separate settings.json, Claude-shaped) ──────\r\n // Continue's hooks live in settings.json (NOT the YAML config.yaml that holds\r\n // MCP servers), under `hooks` keyed by PascalCase event, each value an array\r\n // of { matcher?, hooks:[{ type:\"command\", command }] } — BYTE-IDENTICAL to\r\n // Claude Code. Events outside SUPPORTED_EVENTS (PostCompact) warn-skip; the\r\n // five host-specific events with no canonical analog (ConfigChange,\r\n // TeammateIdle, TaskCompleted, WorktreeCreate, WorktreeRemove) ride the\r\n // nativeHooks passthrough below. Merge-preserving: the user's own hooks + any\r\n // sibling connector's hooks survive; uninstall strips ONLY this connector's\r\n // home-bin command (anchored).\r\n\r\n override installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST];\r\n const hooksDisabled = override?.hooks === false;\r\n // `hooks: false` disables only the NORMALIZED events; nativeHooks is a\r\n // sibling, continue-scoped declaration that installs regardless.\r\n const normalizedEvents = hooksDisabled ? [] : connector.hookEvents;\r\n const nativeHooks = override?.nativeHooks ?? {};\r\n const nativeEvents = Object.keys(nativeHooks);\r\n\r\n if (normalizedEvents.length === 0 && nativeEvents.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: hooksDisabled\r\n ? \"hooks disabled for continue\"\r\n : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n const hookPath = this.getHookConfigPath(ctx);\r\n // Merge into any existing settings.json so the user's own hooks survive.\r\n const file = this.readJson<ContinueSettingsFile>(hookPath) ?? {};\r\n const __skip = this.malformedHookRootSkip(hookPath, (file as Record<string, unknown>).hooks);\r\n if (__skip) return [__skip];\r\n const hooks = (file.hooks ??= {});\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of normalizedEvents) {\r\n if (!SUPPORTED_EVENTS.has(event)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: hookPath,\r\n detail: `${event} has no Continue hook equivalent — skipped`,\r\n });\r\n continue;\r\n }\r\n\r\n // Continue's event names are Claude-identical (PascalCase) — register the\r\n // canonical event name directly.\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);\r\n const matcher = connector.hooks[event]?.matcher ?? \"\";\r\n const entry: ContinueHookEntry = {\r\n matcher,\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n\r\n const bucket = (hooks[event] ??= []);\r\n const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hookPath,\r\n detail: `hooks.${event} already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hookPath,\r\n detail: `hooks.${event}`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hookPath,\r\n detail: `hooks.${event}`,\r\n });\r\n }\r\n mutated = true;\r\n }\r\n\r\n // NATIVE passthrough events: continue-native event-name keys (ConfigChange,\r\n // TeammateIdle, TaskCompleted, WorktreeCreate, WorktreeRemove) filed VERBATIM\r\n // into the hooks map — no SUPPORTED_EVENTS gate, since they ARE continue\r\n // events. Same Claude-shaped nested { matcher, hooks:[{type,command}] } entry;\r\n // matched by EXACT command so a native key that coincides with a normalized\r\n // one never clobbers it.\r\n for (const nativeEvent of nativeEvents) {\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, nativeEvent, connector.id);\r\n const entry: ContinueHookEntry = {\r\n matcher: nativeHooks[nativeEvent]?.matcher ?? \"\",\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n const bucket = (hooks[nativeEvent] ??= []);\r\n const existingIdx = bucket.findIndex((e) => e.hooks?.[0]?.command === command);\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hookPath,\r\n detail: `hooks.${nativeEvent} (native) already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hookPath,\r\n detail: `hooks.${nativeEvent} (native)`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hookPath,\r\n detail: `hooks.${nativeEvent} (native)`,\r\n });\r\n }\r\n mutated = true;\r\n }\r\n\r\n if (mutated) this.writeJson(hookPath, file, ctx.dryRun);\r\n return changes;\r\n }\r\n\r\n override uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const hookPath = this.getHookConfigPath(ctx);\r\n const file = this.readJson<ContinueSettingsFile>(hookPath);\r\n const hooks = file?.hooks;\r\n if (!file || !hooks) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hookPath,\r\n detail: \"no hooks section present\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of Object.keys(hooks)) {\r\n const bucket = hooks[event];\r\n if (!Array.isArray(bucket)) continue;\r\n\r\n // Strip our hook command from each entry; drop entries left empty so we\r\n // never remove another connector's (or the user's own) hook commands. The\r\n // id token is anchored (isHomeBinHookCommand) so a shared-prefix connector\r\n // id is never affected.\r\n const next: ContinueHookEntry[] = [];\r\n let removed = 0;\r\n for (const e of bucket) {\r\n const innerBefore = e.hooks?.length ?? 0;\r\n const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));\r\n removed += innerBefore - inner.length;\r\n if (inner.length > 0) next.push({ matcher: e.matcher ?? \"\", hooks: inner });\r\n }\r\n\r\n if (removed > 0) {\r\n if (next.length > 0) hooks[event] = next;\r\n else delete hooks[event];\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path: hookPath,\r\n detail: `hooks.${event} (${removed})`,\r\n });\r\n mutated = true;\r\n }\r\n }\r\n\r\n if (mutated) this.writeJson(hookPath, file, ctx.dryRun);\r\n if (changes.length === 0) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hookPath,\r\n detail: \"no matching hook entries\",\r\n });\r\n }\r\n return changes;\r\n }\r\n\r\n private entryHasOurCommand(entry: ContinueHookEntry, ctx: InstallContext): boolean {\r\n return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));\r\n }\r\n\r\n /** True when a hook command references our home binary AND this connector id\r\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Runtime: parse Continue stdin JSON → normalized event ────────────────\r\n // Continue's stdin wire is Claude-identical snake_case (PR #11029 types.ts\r\n // HookInput), so the claude-code wire helpers (ClaudeWireInput / extractSessionId\r\n // / toolResponseToString) apply verbatim.\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as ClaudeWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = extractSessionId(input);\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(toolResponseToString(input.tool_response) !== undefined\r\n ? { toolOutput: toolResponseToString(input.tool_response) }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUseFailure\": {\r\n const ev: PostToolUseFailureEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n error: typeof input.error === \"string\" ? input.error : \"\",\r\n ...(typeof input.tool_use_id === \"string\" ? { toolUseId: input.tool_use_id } : {}),\r\n ...(typeof input.is_interrupt === \"boolean\" ? { isInterrupt: input.is_interrupt } : {}),\r\n ...(typeof input.duration_ms === \"number\" ? { durationMs: input.duration_ms } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n const ev: SessionEndEvent = {\r\n ...base,\r\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n const ev: StopEvent = {\r\n ...base,\r\n ...(typeof input.stop_hook_active === \"boolean\"\r\n ? { stopHookActive: input.stop_hook_active }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"Notification\": {\r\n const ev: NotificationEvent = {\r\n ...base,\r\n message: typeof input.message === \"string\" ? input.message : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"PermissionRequest\": {\r\n const ev: PermissionRequestEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n ...(Array.isArray(input.permission_suggestions)\r\n ? { permissionSuggestions: input.permission_suggestions }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStart\": {\r\n const ev: SubagentStartEvent = {\r\n ...base,\r\n ...(typeof input.agent_id === \"string\" ? { agentId: input.agent_id } : {}),\r\n ...(typeof input.agent_type === \"string\" ? { agentType: input.agent_type } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStop\": {\r\n // agent_id/agent_type stay optional — hosts do not reliably populate\r\n // agent_type on SubagentStop (Claude-compatible quirk).\r\n const ev: SubagentStopEvent = {\r\n ...base,\r\n ...(typeof input.agent_id === \"string\" ? { agentId: input.agent_id } : {}),\r\n ...(typeof input.agent_type === \"string\" ? { agentType: input.agent_type } : {}),\r\n ...(typeof input.agent_transcript_path === \"string\"\r\n ? { agentTranscriptPath: input.agent_transcript_path }\r\n : {}),\r\n ...(typeof input.last_assistant_message === \"string\"\r\n ? { lastAssistantMessage: input.last_assistant_message }\r\n : {}),\r\n ...(typeof input.stop_hook_active === \"boolean\"\r\n ? { stopHookActive: input.stop_hook_active }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PreCompact\": {\r\n // Observe/context event — continue's PreCompactInput is HookInputBase +\r\n // trigger (\"manual\"|\"auto\") + custom_instructions. Mirror the claude-code\r\n // adapter exactly: keep only the normalized `trigger`; everything else\r\n // rides on `raw`.\r\n const ev: PreCompactEvent = {\r\n ...base,\r\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\r\n ? { trigger: input.trigger }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n default: {\r\n // Continue never delivers PostCompact (not in HOOK_EVENT_NAMES — see\r\n // SUPPORTED_EVENTS). If the runtime dispatches one anyway, surface it\r\n // loudly rather than silently mis-parse.\r\n throw new Error(`unsupported continue hook event: ${String(event)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Continue native (Claude-shaped) reply ──\r\n // Continue's HookOutput is Claude-identical, so this mirrors the claude-code\r\n // adapter's formatReply byte-for-byte:\r\n // PermissionRequest → nested decision{ behavior:\"allow\"|\"deny\" } envelope;\r\n // PostToolUseFailure / SubagentStart → context-only (deny degrades to\r\n // additionalContext carrying the reason);\r\n // Stop / SubagentStop / UserPromptSubmit / PostToolUse deny → TOP-LEVEL\r\n // { decision:\"block\", reason };\r\n // other deny → hookSpecificOutput.permissionDecision:\"deny\"; ask → \"ask\";\r\n // modify (PreToolUse) → updatedInput; context → additionalContext.\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const hookEventName = event as ClaudeHookEvent;\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // PermissionRequest: nested decision{behavior} envelope. An EXPLICIT allow is\r\n // an active grant; ask/context/void fall through to the native dialog.\r\n if (event === \"PermissionRequest\") {\r\n if (response.decision === \"deny\") {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n decision: { behavior: \"deny\", message: response.reason ?? \"Blocked by hook\" },\r\n },\r\n });\r\n }\r\n if (\r\n response.decision === \"allow\" ||\r\n (response.decision === \"modify\" && response.updatedInput)\r\n ) {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n decision: {\r\n behavior: \"allow\",\r\n ...(response.updatedInput ? { updatedInput: response.updatedInput } : {}),\r\n },\r\n },\r\n });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // PostToolUseFailure (feedback beside the error) and SubagentStart (context\r\n // at the start of the subagent's conversation) are observe/context-only:\r\n // \"context\" emits additionalContext, \"deny\" degrades to the same carrying\r\n // the reason. Everything else passes through.\r\n if (event === \"PostToolUseFailure\" || event === \"SubagentStart\") {\r\n const context =\r\n decision === \"context\"\r\n ? response.additionalContext\r\n : decision === \"deny\"\r\n ? response.reason ?? response.additionalContext\r\n : undefined;\r\n if (context) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, additionalContext: context },\r\n });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a reason (exit 0; JSON carries the decision).\r\n // The deny shape is EVENT-SPECIFIC: Stop / SubagentStop / UserPromptSubmit /\r\n // PostToolUse honor only the TOP-LEVEL { decision:\"block\", reason }; the rest\r\n // use hookSpecificOutput.permissionDecision. (A SubagentStop block keeps the\r\n // subagent running with `reason` as its next instruction — Stop semantics.)\r\n if (decision === \"deny\") {\r\n if (\r\n event === \"Stop\" ||\r\n event === \"SubagentStop\" ||\r\n event === \"UserPromptSubmit\" ||\r\n event === \"PostToolUse\"\r\n ) {\r\n return this.stdout({\r\n decision: \"block\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"deny\",\r\n permissionDecisionReason: response.reason ?? \"Blocked by hook\",\r\n },\r\n });\r\n }\r\n\r\n // ask → prompt the user to confirm.\r\n if (decision === \"ask\") {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"ask\",\r\n permissionDecisionReason: response.reason ?? \"Confirmation required by hook\",\r\n },\r\n });\r\n }\r\n\r\n // modify → rewrite PreToolUse input (only where Continue supports it).\r\n if (decision === \"modify\") {\r\n if (event === \"PreToolUse\" && response.updatedInput) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, updatedInput: response.updatedInput },\r\n });\r\n }\r\n // Output rewrite is unsupported; fall through to allow.\r\n }\r\n\r\n // context → inject soft guidance (also the SessionStart context path).\r\n if (decision === \"context\" && response.additionalContext) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, additionalContext: response.additionalContext },\r\n });\r\n }\r\n\r\n // allow / void / unsupported-degradation → pass through with exit 0.\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const path = this.getServerConfigPath(ctx);\r\n const id = ctx.connector.id;\r\n return [\r\n {\r\n name: `${this.name}: config.yaml present`,\r\n check: () =>\r\n existsSync(path)\r\n ? { status: \"OK\", detail: path }\r\n : { status: \"FAIL\", detail: `not found: ${path}` },\r\n },\r\n {\r\n name: `${this.name}: ${MCP_ROOT_KEY}[name=${id}] registered`,\r\n check: () => {\r\n if (!ctx.connector.server) return { status: \"OK\", detail: \"no MCP server declared\" };\r\n const cfg = readYaml<Record<string, unknown>>(path);\r\n const list = cfg?.[MCP_ROOT_KEY];\r\n const present =\r\n Array.isArray(list) &&\r\n (list as ContinueServer[]).some((e) => this.entryName(e) === id);\r\n return present\r\n ? { status: \"OK\", detail: `${MCP_ROOT_KEY}[name=${id}]` }\r\n : { status: \"FAIL\", detail: `${MCP_ROOT_KEY}[name=${id}] not found in ${path}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Internal helpers ──────────────────────────────────────────────────────\r\n\r\n /** Get-or-create the mcpServers ARRAY at `cfg.mcpServers` (tolerate non-array). */\r\n private serverArray(cfg: Record<string, unknown>): ContinueServer[] {\r\n const existing = cfg[MCP_ROOT_KEY];\r\n if (Array.isArray(existing)) return existing as ContinueServer[];\r\n const fresh: ContinueServer[] = [];\r\n cfg[MCP_ROOT_KEY] = fresh;\r\n return fresh;\r\n }\r\n\r\n /** The `name` of an array entry, or undefined when it is not a named object. */\r\n private entryName(entry: unknown): string | undefined {\r\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return undefined;\r\n const name = (entry as { name?: unknown }).name;\r\n return typeof name === \"string\" ? name : undefined;\r\n }\r\n\r\n /** Resolve the per-platform server override into an effective ServerDef. */\r\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\r\n const override = ctx.connector.platforms[this.id]?.server;\r\n if (override === false) return undefined;\r\n const base = ctx.connector.server;\r\n if (!base) return undefined;\r\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\r\n }\r\n}\r\n\r\nexport const adapter = new ContinueAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/usage/readers/copilot-cli.ts"],"sourcesContent":["/**\r\n * usage/readers/copilot-cli — GitHub Copilot CLI / VS Code Copilot Chat OTEL reader.\r\n *\r\n * Faithful port of tokscale sessions/copilot.rs. Parses file-exported\r\n * OpenTelemetry JSONL emitted by Copilot CLI and VS Code Copilot Chat monitoring:\r\n * ~/.local/share/Copilot/telemetry/*.jsonl (XDG data home / Copilot/telemetry)\r\n * ~/.config/copilot/telemetry/*.jsonl (XDG config home / copilot/telemetry)\r\n * (host roots resolved by paths.ts hostRoots(\"copilot-cli\"), env-overridable).\r\n *\r\n * Each JSONL line is one OTel record. Four record shapes carry token usage, in\r\n * descending trust order:\r\n * 1. ChatSpan — a span with gen_ai.operation.name == \"chat\" (or name \"chat …\")\r\n * 2. InferenceLog — a log with event.name == \"gen_ai.client.inference.operation.details\"\r\n * (or body \"GenAI inference:\")\r\n * 3. AgentTurnLog — a log with event.name == \"copilot_chat.agent.turn\"\r\n * 4. AgentSummarySpan — a span with gen_ai.operation.name == \"invoke_agent\" (aggregate, fallback only)\r\n *\r\n * Tokens (per record `attributes`):\r\n * gen_ai.usage.input_tokens → input (incl. cache reads)\r\n * gen_ai.usage.output_tokens → output\r\n * gen_ai.usage.cache_read.input_tokens → cacheRead\r\n * gen_ai.usage.cache_write.input_tokens | cache_creation.… → cacheWrite\r\n * gen_ai.usage.reasoning.output_tokens | reasoning_tokens → reasoning\r\n * OTEL reports input_tokens inclusive of cache reads, so net input =\r\n * input - min(cacheRead, input), while the reported cache buckets are kept intact.\r\n *\r\n * DEDUP / DOUBLE-COUNT GUARD (the load-bearing bit): chat span, inference log and\r\n * agent-turn log can all describe the SAME response. Cross-source suppression keys\r\n * off two stable per-event identifiers — the OTel `trace_id` and `gen_ai.response.id`\r\n * — and drops a lower-priority lane whenever EITHER matches a higher-priority lane.\r\n * Coarse session attributes (e.g. gen_ai.conversation.id) span many turns and are\r\n * intentionally NOT used for suppression. The surviving records then carry a stable\r\n * per-record `dedupKey`:\r\n * ChatSpan / AgentSummarySpan : \"trace_id:span_id\" (fallback \"span:session:ts:index\")\r\n * InferenceLog : \"log:trace_id:span_id\" (fallback \"log:session:ts:index\")\r\n * AgentTurnLog : \"agent-turn:trace_id:turn.index\" (fallback variants)\r\n *\r\n * model/provider: model from gen_ai.response.model | gen_ai.request.model (else the\r\n * trace context, else \"unknown\"); provider inferred from the model, defaulting to\r\n * \"github-copilot\". session: best session attr, else trace context, else trace_id,\r\n * else \"unknown-session\". timestamp: endTime/startTime/hrTime/… (epoch ms), else file\r\n * mtime. No project attribution (Copilot does not log cwd). Confidence \"host-reported\".\r\n *\r\n * Fail-open: no root → []; unreadable/malformed file or line → skipped (never throws).\r\n */\r\n\r\nimport type { UsageReader, UsageRecord, TokenBreakdown } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { fileMtimeMs, readJsonlLines } from \"../jsonl.js\";\r\nimport { inferProvider } from \"../normalize.js\";\r\nimport { firstExistingRoot, walkFiles } from \"../paths.js\";\r\n\r\nconst PLATFORM_ID = \"copilot-cli\" as const;\r\nconst DEFAULT_MODEL = \"unknown\";\r\nconst DEFAULT_PROVIDER = \"github-copilot\";\r\nconst DEFAULT_SESSION = \"unknown-session\";\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Record source / priority model (port of CopilotUsageSource + SessionIdPriority)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nconst enum Source {\r\n ChatSpan = 0,\r\n InferenceLog = 1,\r\n AgentTurnLog = 2,\r\n AgentSummarySpan = 3,\r\n}\r\n\r\nconst enum SessionPriority {\r\n Missing = 0,\r\n Response = 1,\r\n Interaction = 2,\r\n Session = 3,\r\n}\r\n\r\n/** Model attribute keys, in preference order. */\r\nconst MODEL_ATTRS = [\"gen_ai.response.model\", \"gen_ai.request.model\"] as const;\r\n\r\n/** Session attribute keys with their priority (port of SESSION_ATTRS). */\r\nconst SESSION_ATTRS: ReadonlyArray<readonly [string, SessionPriority]> = [\r\n [\"gen_ai.conversation.id\", SessionPriority.Session],\r\n [\"copilot_chat.session_id\", SessionPriority.Session],\r\n [\"copilot_chat.chat_session_id\", SessionPriority.Session],\r\n [\"session.id\", SessionPriority.Session],\r\n [\"github.copilot.interaction_id\", SessionPriority.Interaction],\r\n [\"gen_ai.response.id\", SessionPriority.Response],\r\n];\r\n\r\ntype JsonObject = Record<string, unknown>;\r\n\r\ninterface TraceContext {\r\n model?: string;\r\n sessionId?: string;\r\n sessionIdPriority: SessionPriority;\r\n}\r\n\r\ninterface Candidate {\r\n source: Source;\r\n traceId?: string;\r\n responseId?: string;\r\n model: string;\r\n providerId: string;\r\n sessionId: string;\r\n ts: number;\r\n tokens: TokenBreakdown;\r\n dedupKey: string;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Value coercion helpers (port of value_as_i64 / attr_* / timestamp helpers)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction isObject(v: unknown): v is JsonObject {\r\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\r\n}\r\n\r\nfunction asString(v: unknown): string | undefined {\r\n return typeof v === \"string\" ? v : undefined;\r\n}\r\n\r\n/** Port of value_as_i64: number / numeric-string → integer, else undefined. */\r\nfunction valueAsI64(v: unknown): number | undefined {\r\n if (typeof v === \"number\") {\r\n if (!Number.isFinite(v)) return undefined;\r\n return Math.trunc(v);\r\n }\r\n if (typeof v === \"string\") {\r\n // Rust parses as i64 (integer-only) for value_as_i64's str branch.\r\n const trimmed = v.trim();\r\n if (!/^[+-]?\\d+$/.test(trimmed)) return undefined;\r\n const n = Number(trimmed);\r\n return Number.isFinite(n) ? Math.trunc(n) : undefined;\r\n }\r\n return undefined;\r\n}\r\n\r\n/** attr_i64: non-negative integer at `key` (0 on absence/garbage), clamped ≥ 0. */\r\nfunction attrI64(attrs: JsonObject, key: string): number {\r\n const raw = valueAsI64(attrs[key]);\r\n if (raw === undefined) return 0;\r\n return Math.max(0, raw);\r\n}\r\n\r\n/** attr_i64_first: first key whose attr_i64 value is > 0, else 0. */\r\nfunction attrI64First(attrs: JsonObject, keys: readonly string[]): number {\r\n for (const key of keys) {\r\n const v = attrI64(attrs, key);\r\n if (v > 0) return v;\r\n }\r\n return 0;\r\n}\r\n\r\nfunction attrStr(attrs: JsonObject, key: string): string | undefined {\r\n return asString(attrs[key]);\r\n}\r\n\r\n/** first_non_empty_attr: first key whose string value is non-empty (trimmed). */\r\nfunction firstNonEmptyAttr(attrs: JsonObject, keys: readonly string[]): string | undefined {\r\n for (const key of keys) {\r\n const s = asString(attrs[key]);\r\n if (s !== undefined && s.trim() !== \"\") return s;\r\n }\r\n return undefined;\r\n}\r\n\r\n/** best_session_attr: the session attr with the highest priority (LAST wins on ties). */\r\nfunction bestSessionAttr(attrs: JsonObject): { id: string; priority: SessionPriority } | undefined {\r\n let best: { id: string; priority: SessionPriority } | undefined;\r\n for (const [key, priority] of SESSION_ATTRS) {\r\n const value = asString(attrs[key]);\r\n if (value === undefined || value.trim() === \"\") continue;\r\n // Rust's max_by_key keeps the LAST element among equal maxima; SESSION_ATTRS\r\n // is NOT priority-descending (four Session-priority keys lead the list), so a\r\n // later equal-priority key must win. Use >= so e.g. `session.id` beats an\r\n // earlier `gen_ai.conversation.id` when both are present (matches copilot.rs).\r\n if (best === undefined || priority >= best.priority) {\r\n best = { id: value, priority };\r\n }\r\n }\r\n return best;\r\n}\r\n\r\nfunction recordBody(value: JsonObject): string | undefined {\r\n return asString(value[\"body\"]) ?? asString(value[\"_body\"]);\r\n}\r\n\r\nfunction getObject(value: JsonObject, key: string): JsonObject | undefined {\r\n const v = value[key];\r\n return isObject(v) ? v : undefined;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Trace / span identity (port of trace_id_from_record / span_id_from_record)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction traceIdFromRecord(value: JsonObject): string | undefined {\r\n const top = asString(value[\"traceId\"]);\r\n if (top !== undefined) return top;\r\n const ctx = getObject(value, \"spanContext\");\r\n return ctx === undefined ? undefined : asString(ctx[\"traceId\"]);\r\n}\r\n\r\nfunction spanIdFromRecord(value: JsonObject): string | undefined {\r\n const top = asString(value[\"spanId\"]);\r\n if (top !== undefined) return top;\r\n const ctx = getObject(value, \"spanContext\");\r\n return ctx === undefined ? undefined : asString(ctx[\"spanId\"]);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Timestamp + duration parsing (port of timestamp_ms_from_* / duration helpers)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** OTel hrTime/startTime/endTime: [seconds, nanos] array → epoch ms. */\r\nfunction timestampMsFromValue(value: unknown): number | undefined {\r\n if (!Array.isArray(value)) return undefined;\r\n const seconds = valueAsI64(value[0]);\r\n if (seconds === undefined) return undefined;\r\n const nanos = valueAsI64(value[1]);\r\n if (nanos === undefined) return undefined;\r\n return seconds * 1000 + Math.trunc(nanos / 1_000_000);\r\n}\r\n\r\n/** Scalar epoch with magnitude auto-detection (ns/us/ms/s). */\r\nfunction timestampMsFromScalar(value: unknown): number | undefined {\r\n const raw = valueAsI64(value);\r\n if (raw === undefined) return undefined;\r\n const abs = Math.abs(raw);\r\n if (abs >= 100_000_000_000_000_000) return Math.trunc(raw / 1_000_000); // ns\r\n if (abs >= 100_000_000_000_000) return Math.trunc(raw / 1_000); // us\r\n if (abs >= 100_000_000_000) return raw; // ms\r\n return raw * 1000; // s\r\n}\r\n\r\n/** OTel timeUnixNano is unsigned; refuse ≤ 0 so a malformed value falls through. */\r\nfunction timestampMsFromUnixNanos(value: unknown): number | undefined {\r\n const raw = valueAsI64(value);\r\n if (raw === undefined || raw <= 0) return undefined;\r\n return Math.trunc(raw / 1_000_000);\r\n}\r\n\r\nfunction timestampMsFromRecord(value: JsonObject): number | undefined {\r\n return (\r\n timestampMsFromValue(value[\"endTime\"]) ??\r\n timestampMsFromValue(value[\"startTime\"]) ??\r\n timestampMsFromValue(value[\"hrTime\"]) ??\r\n timestampMsFromValue(value[\"_hrTime\"]) ??\r\n timestampMsFromValue(value[\"time\"]) ??\r\n timestampMsFromScalar(value[\"timestamp\"]) ??\r\n timestampMsFromScalar(value[\"observedTimestamp\"]) ??\r\n timestampMsFromUnixNanos(value[\"timeUnixNano\"])\r\n );\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Token normalization (port of normalize_input_tokens)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction normalizeInputTokens(\r\n input: number,\r\n output: number,\r\n cacheRead: number,\r\n cacheWrite: number,\r\n reasoning: number,\r\n): TokenBreakdown {\r\n // input_tokens is reported inclusive of cache reads; subtract only the cached\r\n // portion out of input, keeping the reported cache buckets intact.\r\n const cacheReadForInput = Math.min(Math.max(cacheRead, 0), Math.max(input, 0));\r\n const tokens = emptyTokens();\r\n tokens.input = Math.max(0, Math.max(input, 0) - cacheReadForInput);\r\n tokens.output = Math.max(output, 0);\r\n tokens.cacheRead = Math.max(cacheRead, 0);\r\n tokens.cacheWrite = Math.max(cacheWrite, 0);\r\n tokens.reasoning = Math.max(reasoning, 0);\r\n return tokens;\r\n}\r\n\r\nfunction tokenTotal(t: TokenBreakdown): number {\r\n return t.input + t.output + t.cacheRead + t.cacheWrite + t.reasoning;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Record classification (port of is_*_record)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction isSpanRecord(value: JsonObject): boolean {\r\n // VS Code Copilot Chat exports omit `type:\"span\"`; infer span-ness from a\r\n // top-level `name` plus span identity / timing / kind. Inference- and\r\n // agent-turn-log records carry NO top-level `name`, which disambiguates them.\r\n const type = asString(value[\"type\"]);\r\n if (type === \"span\") return true;\r\n if (type !== undefined) return false;\r\n\r\n const hasName = asString(value[\"name\"]) !== undefined;\r\n const hasSpanIdentity =\r\n asString(value[\"spanId\"]) !== undefined || asString(value[\"traceId\"]) !== undefined;\r\n const hasSpanTiming =\r\n value[\"startTime\"] !== undefined ||\r\n value[\"endTime\"] !== undefined ||\r\n value[\"duration\"] !== undefined;\r\n\r\n return hasName && (hasSpanIdentity || hasSpanTiming || value[\"kind\"] !== undefined);\r\n}\r\n\r\nfunction isChatSpanRecord(value: JsonObject, attrs: JsonObject): boolean {\r\n if (!isSpanRecord(value)) return false;\r\n if (attrStr(attrs, \"gen_ai.operation.name\") === \"chat\") return true;\r\n const name = asString(value[\"name\"]);\r\n return name !== undefined && name.startsWith(\"chat \");\r\n}\r\n\r\nfunction isAgentSummarySpanRecord(value: JsonObject, attrs: JsonObject): boolean {\r\n if (!isSpanRecord(value)) return false;\r\n if (attrStr(attrs, \"gen_ai.operation.name\") === \"invoke_agent\") return true;\r\n const name = asString(value[\"name\"]);\r\n return name !== undefined && name.startsWith(\"invoke_agent \");\r\n}\r\n\r\nfunction isInferenceLogRecord(value: JsonObject, attrs: JsonObject): boolean {\r\n if (isSpanRecord(value)) return false;\r\n if (attrStr(attrs, \"event.name\") === \"gen_ai.client.inference.operation.details\") return true;\r\n const body = recordBody(value);\r\n return body !== undefined && body.startsWith(\"GenAI inference:\");\r\n}\r\n\r\nfunction isAgentTurnLogRecord(value: JsonObject, attrs: JsonObject): boolean {\r\n if (isSpanRecord(value)) return false;\r\n if (attrStr(attrs, \"event.name\") === \"copilot_chat.agent.turn\") return true;\r\n const body = recordBody(value);\r\n return body !== undefined && body.startsWith(\"copilot_chat.agent.turn\");\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Trace context collection (port of collect_trace_contexts)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction collectTraceContexts(records: JsonObject[]): Map<string, TraceContext> {\r\n const contexts = new Map<string, TraceContext>();\r\n\r\n for (const record of records) {\r\n const traceId = traceIdFromRecord(record);\r\n if (traceId === undefined) continue;\r\n const attrs = getObject(record, \"attributes\");\r\n if (attrs === undefined) continue;\r\n\r\n let context = contexts.get(traceId);\r\n if (context === undefined) {\r\n context = { sessionIdPriority: SessionPriority.Missing };\r\n contexts.set(traceId, context);\r\n }\r\n\r\n if (context.model === undefined) {\r\n context.model = firstNonEmptyAttr(attrs, MODEL_ATTRS);\r\n }\r\n\r\n const best = bestSessionAttr(attrs);\r\n if (best !== undefined && best.priority > context.sessionIdPriority) {\r\n context.sessionId = best.id;\r\n context.sessionIdPriority = best.priority;\r\n }\r\n }\r\n\r\n return contexts;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Dedup key (port of dedup_key_for_record)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction dedupKeyForRecord(\r\n source: Source,\r\n record: JsonObject,\r\n attrs: JsonObject,\r\n traceId: string | undefined,\r\n sessionId: string,\r\n ts: number,\r\n index: number,\r\n): string {\r\n const spanId = spanIdFromRecord(record);\r\n\r\n switch (source) {\r\n case Source.ChatSpan:\r\n case Source.AgentSummarySpan:\r\n if (traceId !== undefined && spanId !== undefined) return `${traceId}:${spanId}`;\r\n return `span:${sessionId}:${ts}:${index}`;\r\n case Source.InferenceLog:\r\n if (traceId !== undefined && spanId !== undefined) return `log:${traceId}:${spanId}`;\r\n return `log:${sessionId}:${ts}:${index}`;\r\n case Source.AgentTurnLog: {\r\n // Use a real turn.index when present (stable across re-runs); otherwise fall\r\n // back to the line index so two turn-less records in the same trace differ.\r\n let turnPart: string | undefined;\r\n for (const key of [\"turn.index\", \"copilot_chat.turn.index\"]) {\r\n const v = valueAsI64(attrs[key]);\r\n if (v !== undefined) {\r\n turnPart = String(v);\r\n break;\r\n }\r\n }\r\n const part = turnPart ?? `idx-${index}`;\r\n if (traceId !== undefined) return `agent-turn:${traceId}:${part}`;\r\n return `agent-turn:${sessionId}:${part}:${index}`;\r\n }\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Candidate construction (port of usage_candidate_from_record / candidate_from_attributes)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction classify(value: JsonObject, attrs: JsonObject): Source | undefined {\r\n if (isChatSpanRecord(value, attrs)) return Source.ChatSpan;\r\n if (isInferenceLogRecord(value, attrs)) return Source.InferenceLog;\r\n if (isAgentTurnLogRecord(value, attrs)) return Source.AgentTurnLog;\r\n if (isAgentSummarySpanRecord(value, attrs)) return Source.AgentSummarySpan;\r\n return undefined;\r\n}\r\n\r\nfunction candidateFromRecord(\r\n record: JsonObject,\r\n index: number,\r\n fallbackTimestamp: number,\r\n traceContexts: Map<string, TraceContext>,\r\n): Candidate | undefined {\r\n const attrs = getObject(record, \"attributes\");\r\n if (attrs === undefined) return undefined;\r\n\r\n const source = classify(record, attrs);\r\n if (source === undefined) return undefined;\r\n\r\n const traceId = traceIdFromRecord(record);\r\n const traceContext = traceId === undefined ? undefined : traceContexts.get(traceId);\r\n\r\n const input = attrI64First(attrs, [\"gen_ai.usage.input_tokens\"]);\r\n const output = attrI64First(attrs, [\"gen_ai.usage.output_tokens\"]);\r\n const cacheRead = attrI64First(attrs, [\"gen_ai.usage.cache_read.input_tokens\"]);\r\n const cacheWrite = attrI64First(attrs, [\r\n \"gen_ai.usage.cache_write.input_tokens\",\r\n \"gen_ai.usage.cache_creation.input_tokens\",\r\n ]);\r\n const reasoning = attrI64First(attrs, [\r\n \"gen_ai.usage.reasoning.output_tokens\",\r\n \"gen_ai.usage.reasoning_tokens\",\r\n ]);\r\n\r\n const tokens = normalizeInputTokens(input, output, cacheRead, cacheWrite, reasoning);\r\n if (tokenTotal(tokens) === 0) return undefined;\r\n\r\n const responseIdRaw = asString(attrs[\"gen_ai.response.id\"]);\r\n const responseId =\r\n responseIdRaw !== undefined && responseIdRaw.trim() !== \"\" ? responseIdRaw.trim() : undefined;\r\n\r\n const model =\r\n firstNonEmptyAttr(attrs, MODEL_ATTRS) ?? traceContext?.model ?? DEFAULT_MODEL;\r\n const providerId = inferProvider(model) ?? DEFAULT_PROVIDER;\r\n\r\n const sessionId =\r\n bestSessionAttr(attrs)?.id ?? traceContext?.sessionId ?? traceId ?? DEFAULT_SESSION;\r\n\r\n const ts = timestampMsFromRecord(record) ?? fallbackTimestamp;\r\n const dedupKey = dedupKeyForRecord(source, record, attrs, traceId, sessionId, ts, index);\r\n\r\n const candidate: Candidate = {\r\n source,\r\n model,\r\n providerId,\r\n sessionId,\r\n ts,\r\n tokens,\r\n dedupKey,\r\n };\r\n if (traceId !== undefined) candidate.traceId = traceId;\r\n if (responseId !== undefined) candidate.responseId = responseId;\r\n return candidate;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Cross-source suppression (port of should_emit_candidate)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction traceIdsForSource(candidates: Candidate[], source: Source): Set<string> {\r\n const set = new Set<string>();\r\n for (const c of candidates) {\r\n if (c.source === source && c.traceId !== undefined) set.add(c.traceId);\r\n }\r\n return set;\r\n}\r\n\r\nfunction responseIdsForSource(candidates: Candidate[], source: Source): Set<string> {\r\n const set = new Set<string>();\r\n for (const c of candidates) {\r\n if (c.source === source && c.responseId !== undefined) set.add(c.responseId);\r\n }\r\n return set;\r\n}\r\n\r\nfunction shouldEmit(\r\n candidate: Candidate,\r\n chatTraces: Set<string>,\r\n inferenceTraces: Set<string>,\r\n agentTurnTraces: Set<string>,\r\n chatResponseIds: Set<string>,\r\n inferenceResponseIds: Set<string>,\r\n agentTurnResponseIds: Set<string>,\r\n): boolean {\r\n // Suppression keys off the per-event trace_id and gen_ai.response.id — either\r\n // match is sufficient to drop a lower-priority lane. Coarse session attributes\r\n // (gen_ai.conversation.id, …) span multiple turns and are intentionally unused.\r\n const { traceId, responseId } = candidate;\r\n const traceMatch = (traces: Set<string>): boolean =>\r\n traceId !== undefined && traces.has(traceId);\r\n const responseMatch = (ids: Set<string>): boolean =>\r\n responseId !== undefined && ids.has(responseId);\r\n\r\n switch (candidate.source) {\r\n case Source.ChatSpan:\r\n return true;\r\n case Source.InferenceLog:\r\n return !traceMatch(chatTraces) && !responseMatch(chatResponseIds);\r\n case Source.AgentTurnLog:\r\n return (\r\n !traceMatch(chatTraces) &&\r\n !traceMatch(inferenceTraces) &&\r\n !responseMatch(chatResponseIds) &&\r\n !responseMatch(inferenceResponseIds)\r\n );\r\n case Source.AgentSummarySpan:\r\n return (\r\n !traceMatch(chatTraces) &&\r\n !traceMatch(inferenceTraces) &&\r\n !traceMatch(agentTurnTraces) &&\r\n !responseMatch(chatResponseIds) &&\r\n !responseMatch(inferenceResponseIds) &&\r\n !responseMatch(agentTurnResponseIds)\r\n );\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// File parse (port of parse_copilot_file)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nfunction parseCopilotFile(path: string): UsageRecord[] {\r\n const lines = readJsonlLines(path);\r\n if (lines.length === 0) return [];\r\n\r\n const fallbackTimestamp = fileMtimeMs(path);\r\n const records: JsonObject[] = [];\r\n for (const raw of lines) {\r\n if (isObject(raw)) records.push(raw);\r\n }\r\n\r\n const traceContexts = collectTraceContexts(records);\r\n\r\n const candidates: Candidate[] = [];\r\n records.forEach((record, index) => {\r\n const candidate = candidateFromRecord(record, index, fallbackTimestamp, traceContexts);\r\n if (candidate !== undefined) candidates.push(candidate);\r\n });\r\n\r\n const chatTraces = traceIdsForSource(candidates, Source.ChatSpan);\r\n const inferenceTraces = traceIdsForSource(candidates, Source.InferenceLog);\r\n const agentTurnTraces = traceIdsForSource(candidates, Source.AgentTurnLog);\r\n const chatResponseIds = responseIdsForSource(candidates, Source.ChatSpan);\r\n const inferenceResponseIds = responseIdsForSource(candidates, Source.InferenceLog);\r\n const agentTurnResponseIds = responseIdsForSource(candidates, Source.AgentTurnLog);\r\n\r\n const out: UsageRecord[] = [];\r\n for (const candidate of candidates) {\r\n if (\r\n !shouldEmit(\r\n candidate,\r\n chatTraces,\r\n inferenceTraces,\r\n agentTurnTraces,\r\n chatResponseIds,\r\n inferenceResponseIds,\r\n agentTurnResponseIds,\r\n )\r\n ) {\r\n continue;\r\n }\r\n\r\n out.push({\r\n platformId: PLATFORM_ID,\r\n modelId: candidate.model,\r\n providerId: candidate.providerId,\r\n sessionId: candidate.sessionId,\r\n tokens: candidate.tokens,\r\n ts: candidate.ts,\r\n messageCount: 1,\r\n dedupKey: candidate.dedupKey,\r\n confidence: \"host-reported\",\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Reader singleton\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\nconst copilotReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const root = firstExistingRoot(PLATFORM_ID);\r\n if (root === undefined) return []; // no telemetry root → fail-open\r\n\r\n // <root>/*.jsonl (and any nested .jsonl the export produces).\r\n const files = walkFiles(root, (name) => name.endsWith(\".jsonl\"));\r\n\r\n const records: UsageRecord[] = [];\r\n for (const file of files) {\r\n let rows: UsageRecord[];\r\n try {\r\n rows = parseCopilotFile(file);\r\n } catch {\r\n continue; // fail-open per file\r\n }\r\n for (const row of rows) {\r\n if (sinceMs !== undefined && row.ts < sinceMs) continue;\r\n records.push(row);\r\n }\r\n }\r\n return records;\r\n },\r\n};\r\n\r\nexport default copilotReader;\r\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/copilot-cli/index.ts"],"sourcesContent":["/**\r\n * adapters/copilot-cli — GitHub Copilot CLI platform adapter for agent-connector.\r\n *\r\n * GitHub Copilot CLI is a json-stdio host: the host pipes a JSON payload to a\r\n * hook command on stdin and reads JSON/exit-code back. Its reply shape is\r\n * Claude-compatible (`hookSpecificOutput` wrapper), but its hooks-file event\r\n * KEYS are NOT — the CLI's loader honors ONLY a fixed set of lowerCamelCase\r\n * keys and SILENTLY DROPS anything else (see EVENT_WIRE_KEY below), so a\r\n * PascalCase→camelCase rename table IS required when writing the config.\r\n *\r\n * Native config (user/global only — Copilot CLI has no project-scoped config):\r\n * - MCP servers: ~/.copilot/mcp-config.json, root key \"mcpServers\". An stdio\r\n * server is written with type \"local\" (the host also accepts \"stdio\") plus\r\n * `tools: [\"*\"]`. Remote servers use type \"http\".\r\n * - Hooks: a hooks file shaped `{ version: 1, hooks: { … } }` discovered from\r\n * ~/.copilot/hooks/*.json. We write a single dedicated file,\r\n * ~/.copilot/hooks/agent-connector.json, so we never disturb a user's own\r\n * hook files and removal is a clean, scoped operation. Each event maps to an\r\n * array of flat command entries `{ matcher?, hooks: [{ type:\"command\", command }] }`\r\n * keyed by the CLI's lowerCamelCase wire name (EVENT_WIRE_KEY).\r\n * - Reply: the installed CLI 1.0.63 reads the PreToolUse permission decision\r\n * FLAT at the TOP LEVEL of the hook's stdout JSON — `{ permissionDecision\r\n * (allow|deny|ask), permissionDecisionReason, modifiedArgs? }` — there is NO\r\n * `hookSpecificOutput` wrapper in 1.0.63 (verified: the token appears zero\r\n * times in app.js), and PreToolUse is the ONLY event whose reply the host\r\n * reads. PreToolUse is fail-closed; exit 0 + the flat JSON refines the\r\n * decision. The host has no additionalContext mechanism, so context\r\n * injection is a no-op on this version. (PermissionRequest/SubagentStop keep\r\n * their Claude-shaped envelopes for forward-compat; they are unread on 1.0.63.)\r\n *\r\n * Env handling: the host is not documented to support `${env:VAR}` interpolation\r\n * inside mcp-config.json, so env/header/url refs are resolved to literals at\r\n * install time via resolveEnvRefsDeep (the safe default for a no-native-interp\r\n * host, matching the Codex adapter's approach).\r\n *\r\n * Content surfaces (surfaces-design §4-5): Copilot CLI exposes skills and\r\n * subagents but NO prompt-file command surface, so commands inherit the\r\n * BaseAdapter skip/warn default (supportsCommands stays false).\r\n * - skills: folder-per-skill `<dir>/skills/<name>/SKILL.md` (+ resource files).\r\n * user scope → ~/.copilot/skills; project scope → <projectDir>/.github/skills.\r\n * - subagents: user scope → ~/.copilot/agents/<name>.agent.md; project scope →\r\n * <projectDir>/.github/agents/<name>.agent.md (md + frontmatter:\r\n * name, description, tools, model).\r\n * The .github/ tree is shared with the vscode-copilot and jetbrains-copilot\r\n * connectors; we write identical, idempotent content and on uninstall remove\r\n * only the files this connector wrote.\r\n *\r\n * Grounded in docs/research/understand-report.md §2 (Platform Integration\r\n * Matrix, \"GitHub Copilot CLI\" row).\r\n */\r\n\r\nimport { existsSync } from \"node:fs\";\r\nimport { homedir } from \"node:os\";\r\nimport { join } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, MemoryTarget, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n NotificationEvent,\r\n PermissionRequestEvent,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PostToolUseEvent,\r\n PostToolUseFailureEvent,\r\n PostCompactEvent,\r\n PreCompactEvent,\r\n PreToolUseEvent,\r\n ServerDef,\r\n SessionEndEvent,\r\n SessionStartEvent,\r\n StopEvent,\r\n SubagentStartEvent,\r\n SubagentStopEvent,\r\n Transport,\r\n UserPromptSubmitEvent,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport {\r\n buildHomeBinHookCommand,\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd, renderSubagentMd } from \"../claude-code/render.js\";\r\nimport { normalizeSessionSource } from \"../claude-code/wire.js\";\r\n\r\nconst HOST: PlatformId = \"copilot-cli\";\r\nconst MCP_ROOT_KEY = \"mcpServers\";\r\n\r\n/** Native hooks-file version Copilot CLI expects (`{ version: 1, hooks: {…} }`). */\r\nconst COPILOT_HOOKS_VERSION = 1;\r\n\r\n/**\r\n * EVENT→WIRE-KEY map for the hooks file. VERIFIED against the installed GitHub\r\n * Copilot CLI 1.0.63 bundle (app.js): the hook loader validates every\r\n * `config.hooks` key against a hardcoded Set of NINE lowerCamelCase event names —\r\n * { sessionStart, sessionEnd, userPromptSubmitted, preToolUse, postToolUse,\r\n * errorOccurred, agentStop, subagentStop, preCompact }\r\n * — and any key NOT in that Set is SILENTLY DROPPED (logged only at debug:\r\n * \"Ignoring unknown hook event(s)\"). The Zod hooks schema defines the same nine\r\n * camelCase keys and nothing else. So a PascalCase key like `PreToolUse` never\r\n * registers → the hook NEVER FIRES. We therefore key the file by the camelCase\r\n * WIRE name, NOT the PascalCase HookEventName.\r\n *\r\n * Two mappings are NOT a naive lowercase-first-letter: Claude's `Stop` is the\r\n * CLI's `agentStop`, and `UserPromptSubmit` is `userPromptSubmitted`. The rest\r\n * are first-letter-lowercased. (PascalCase aliases are documented for a newer\r\n * CLI that selects the snake_case payload dialect, but they are unknown to the\r\n * loader in 1.0.63 and dropped — camelCase is the only safe wire form here.)\r\n *\r\n * Only events Copilot CLI's loader actually accepts appear here. Events the\r\n * installed loader has no file-hook key for (Notification, PermissionRequest,\r\n * SubagentStart, PostToolUseFailure) are demoted on `capabilities` and warn-skip\r\n * at install — they are NOT in this map.\r\n */\r\nconst EVENT_WIRE_KEY: Partial<Record<HookEventName, string>> = {\r\n SessionStart: \"sessionStart\",\r\n SessionEnd: \"sessionEnd\",\r\n UserPromptSubmit: \"userPromptSubmitted\",\r\n PreToolUse: \"preToolUse\",\r\n PostToolUse: \"postToolUse\",\r\n PreCompact: \"preCompact\",\r\n Stop: \"agentStop\",\r\n SubagentStop: \"subagentStop\",\r\n};\r\n\r\n/**\r\n * CAPABILITY FILTER for installHooks — maps each canonical HookEventName to the\r\n * `capabilities` flag that gates it. The adapter's `capabilities` literal is the\r\n * single source of truth for what Copilot CLI's runtime actually fires; declared\r\n * events (connector.hookEvents is the connector-level set, NOT pre-filtered by\r\n * host) are screened through this map so an unsupported event — Copilot CLI has\r\n * NO post-compaction hook, so `postCompact` stays unset — is reported as a\r\n * graceful warn/skip instead of being written as a dead `hooks.PostCompact` the\r\n * host would never fire. Same shape as the goose adapter's EVENT_CAPABILITY.\r\n */\r\nconst EVENT_CAPABILITY: Record<HookEventName, keyof PlatformCapabilities> = {\r\n SessionStart: \"sessionStart\",\r\n SessionEnd: \"sessionEnd\",\r\n UserPromptSubmit: \"userPromptSubmit\",\r\n PreToolUse: \"preToolUse\",\r\n PostToolUse: \"postToolUse\",\r\n PreCompact: \"preCompact\",\r\n Stop: \"stop\",\r\n Notification: \"notification\",\r\n PermissionRequest: \"permissionRequest\",\r\n PostToolUseFailure: \"postToolUseFailure\",\r\n SubagentStart: \"subagentStart\",\r\n SubagentStop: \"subagentStop\",\r\n // Copilot CLI has no post-compaction hook — postCompact stays unset on\r\n // `capabilities`, so a declared PostCompact warn-skips at install.\r\n PostCompact: \"postCompact\",\r\n};\r\n\r\n/**\r\n * A single Copilot CLI hook registration entry (Claude-shaped).\r\n *\r\n * `matcher` is OPTIONAL and, when present, MUST be non-empty. Copilot CLI's\r\n * hooks schema is `matcher: z.string().min(1).optional()` (live-verified on CLI\r\n * 1.0.63): an OMITTED matcher passes validation, but an EMPTY STRING fails it —\r\n * the loader logs \"Invalid hook configuration … matcher cannot be empty\" and\r\n * DISCARDS THE ENTIRE hook file, registering zero hooks. So we OMIT the key\r\n * entirely when there is no matcher and emit it only when non-empty; we must\r\n * never write `matcher: \"\"`.\r\n */\r\ninterface CopilotHookEntry {\r\n matcher?: string;\r\n hooks: Array<{ type: \"command\"; command: string }>;\r\n}\r\n\r\n/** The shape of the Copilot CLI hooks file we own. */\r\ninterface CopilotHooksFile {\r\n version?: number;\r\n hooks?: Record<string, CopilotHookEntry[]>;\r\n}\r\n\r\n/** Native MCP server entry shapes Copilot CLI accepts under `mcpServers`. */\r\ninterface CopilotLocalServer {\r\n /** stdio transport is registered as type \"local\" (host also accepts \"stdio\"). */\r\n type: \"local\";\r\n command: string;\r\n args?: string[];\r\n env?: Record<string, string>;\r\n cwd?: string;\r\n tools: string[];\r\n}\r\ninterface CopilotHttpServer {\r\n /** Remote transport: \"http\" (Streamable HTTP) or \"sse\" (legacy). */\r\n type: \"http\" | \"sse\";\r\n url: string;\r\n headers?: Record<string, string>;\r\n tools: string[];\r\n}\r\n\r\n/** Raw Copilot CLI hook stdin payload.\r\n *\r\n * The installed CLI 1.0.63 bundle (app.js) builds tool-event stdin in\r\n * lowerCamelCase via a single shared getter, NOT the Claude-style snake_case\r\n * this adapter once assumed:\r\n * PreToolUse → { sessionId, timestamp, cwd, toolName, toolArgs }\r\n * PostToolUse → { sessionId, timestamp, cwd, toolName, toolArgs,\r\n * toolResult: { resultType, textResultForLlm } }\r\n * where `toolArgs` is a JSON-**string** (the writer does\r\n * `toolArgs: JSON.stringify(t.toolArgs, null, 2)`). The snake_case fields are\r\n * retained as a tolerant fallback so a future/alternate dialect still parses,\r\n * but camelCase is the live wire form on 1.0.63. */\r\ninterface CopilotWireInput {\r\n connector?: unknown;\r\n // session id: camelCase on the live wire; snake_case kept as a fallback.\r\n sessionId?: string;\r\n session_id?: string;\r\n transcript_path?: string;\r\n cwd?: string;\r\n hook_event_name?: string;\r\n\r\n // tool events — camelCase is the live 1.0.63 wire form; `toolArgs` is a\r\n // JSON-STRING. snake_case retained as a tolerant fallback.\r\n toolName?: string;\r\n toolArgs?: unknown;\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n // PostToolUse: the live bundle ships the result under camelCase\r\n // `toolResult.{resultType,textResultForLlm}`; the snake_case `tool_result`\r\n // form is kept as a fallback. PostToolUse fires success-only.\r\n toolResult?: { resultType?: string; textResultForLlm?: string };\r\n tool_result?: { result_type?: string; text_result_for_llm?: string };\r\n\r\n // SessionStart\r\n source?: string;\r\n // SessionEnd\r\n reason?: string;\r\n // UserPromptSubmit\r\n prompt?: string;\r\n // PreCompact\r\n trigger?: string;\r\n // Notification\r\n message?: string;\r\n\r\n // PostToolUseFailure — payload is {tool_name, tool_input, error} + base\r\n // (hooks-reference.md:421-430). No tool_use_id/is_interrupt/duration_ms.\r\n error?: string;\r\n\r\n // SubagentStart / SubagentStop — VS Code dialect identity is `agent_name`\r\n // (+ optional `agent_display_name`); there is no agent_id/agent_type\r\n // (hooks-reference.md:467-476,497-507).\r\n agent_name?: string;\r\n agent_display_name?: string;\r\n}\r\n\r\nexport class CopilotCliAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"GitHub Copilot CLI\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block (project <projectDir>/AGENTS.md\r\n // via the base default; user scope → ~/.copilot/copilot-instructions.md below).\r\n supportsMemory: true,\r\n // Copilot CLI's file-hook loader (1.0.63) accepts exactly these of the\r\n // canonical lifecycle events — every one is a member of its hardcoded\r\n // camelCase validator Set (see EVENT_WIRE_KEY).\r\n preToolUse: true,\r\n postToolUse: true,\r\n preCompact: true,\r\n sessionStart: true,\r\n sessionEnd: true,\r\n userPromptSubmit: true, // → wire key userPromptSubmitted\r\n stop: true, // → wire key agentStop\r\n subagentStop: true,\r\n // DEMOTED — NOT in the installed CLI 1.0.63 file-hook validator Set, so a\r\n // hooks-file key for any of these is silently dropped and never fires:\r\n // notification, permissionRequest, subagentStart, postToolUseFailure.\r\n // (The github/docs `main` hooks-reference describes them for a NEWER CLI;\r\n // re-promote per-event once a verified bundle ships them in the Set — until\r\n // then the fail-safe is false so installHooks warn-skips instead of writing\r\n // a dead key.)\r\n notification: false,\r\n permissionRequest: false,\r\n postToolUseFailure: false,\r\n subagentStart: false,\r\n // PreToolUse is fail-closed and can rewrite tool input (the host reads a\r\n // flat top-level `modifiedArgs`); a PostToolUse hook cannot rewrite\r\n // already-emitted tool output.\r\n canModifyArgs: true,\r\n canModifyOutput: false,\r\n // DEMOTED to false (fail-safe): the installed CLI 1.0.63 bundle has NO\r\n // additionalContext mechanism — the token appears zero times in app.js, so a\r\n // context-injection reply (SessionStart/PreCompact/etc.) is a silent no-op.\r\n // Claiming the capability would advertise an injection path the host never\r\n // honors; re-promote once a verified bundle ships an additionalContext reader.\r\n canInjectSessionContext: false,\r\n // Copilot CLI's mcp-config.json `type` accepts stdio (written \"local\"),\r\n // http (Streamable HTTP), and sse (legacy) — per GitHub's add-mcp-servers docs.\r\n transports: [\"stdio\", \"http\", \"sse\"],\r\n // Content surfaces: Copilot CLI exposes skills + subagents, but has no\r\n // prompt-file command surface, so commands stay false (inherits BaseAdapter\r\n // skip/warn).\r\n supportsCommands: false,\r\n supportsSkills: true,\r\n supportsSubagents: true,\r\n // Native passthrough: Copilot CLI has an `errorOccurred` lifecycle event (a\r\n // member of the camelCase validator Set) with NO canonical HookEventName\r\n // analog — below the >=3-host core bar (docs/research/host-specific-hook-events-design.md).\r\n // A connector reaches it via platforms[\"copilot-cli\"].nativeHooks; installHooks\r\n // files the event-name key VERBATIM (the author must supply the CLI's exact\r\n // camelCase key, e.g. `errorOccurred` — the loader drops unknown keys) with\r\n // the same home-bin command shape, and the generic uninstall reverses it.\r\n supportsNativeHooks: true,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(_projectDir: string): DetectedPlatform {\r\n const userDir = join(homedir(), \".copilot\");\r\n const mcpConfig = join(userDir, \"mcp-config.json\");\r\n const hooksDir = join(userDir, \"hooks\");\r\n const installed =\r\n existsSync(userDir) || existsSync(mcpConfig) || existsSync(hooksDir);\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath: mcpConfig,\r\n scope: \"user\",\r\n reason: installed\r\n ? `found GitHub Copilot CLI config under ${userDir}`\r\n : `no GitHub Copilot CLI config at ${userDir}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Memory surface: user-scope global instructions file ─────────────────\r\n // Project scope stays on the AGENTS.md base default (root AGENTS.md is the\r\n // primary instructions file). User scope targets Copilot CLI's documented\r\n // $HOME/.copilot/copilot-instructions.md — a markdown file shared with other\r\n // writers, so the managed block (not whole-file ownership) applies as usual.\r\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\r\n if (this.memoryOverride(ctx)?.path || ctx.scope !== \"user\") {\r\n return super.memoryTargets(ctx);\r\n }\r\n return [\r\n {\r\n path: join(homedir(), \".copilot\", \"copilot-instructions.md\"),\r\n reason: \"copilot-cli global instructions (~/.copilot/copilot-instructions.md)\",\r\n },\r\n ];\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n /** Copilot CLI is user/global only — scope is ignored. */\r\n getConfigDir(_ctx: InstallContext): string {\r\n return join(homedir(), \".copilot\");\r\n }\r\n\r\n getServerConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"mcp-config.json\");\r\n }\r\n\r\n /**\r\n * Copilot CLI discovers hooks from any `~/.copilot/hooks/*.json`. We write a\r\n * single dedicated file so we never disturb the user's own hook files and\r\n * uninstall is a clean, scoped operation.\r\n */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return join(this.getConfigDir(ctx), \"hooks\", \"agent-connector.json\");\r\n }\r\n\r\n // ── MCP server install / uninstall ───────────────────────────────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for copilot-cli\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /** Render a normalized ServerDef into Copilot CLI's native mcpServers entry. */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): CopilotLocalServer | CopilotHttpServer {\r\n const transport: Transport = server.transport;\r\n const tools = this.renderTools(server);\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n const entry: CopilotLocalServer = {\r\n type: \"local\",\r\n command: resolveEnvRefsDeep(command),\r\n tools,\r\n };\r\n if (args.length > 0) entry.args = args.map((a) => resolveEnvRefsDeep(a));\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n if (server.cwd) entry.cwd = resolveEnvRefsDeep(server.cwd);\r\n return entry;\r\n }\r\n\r\n // remote (Streamable http / legacy sse) — Copilot registers a URL keyed by\r\n // its `type`. AC's canonical \"sse\" → \"sse\"; everything else → \"http\".\r\n const entry: CopilotHttpServer = {\r\n type: server.transport === \"sse\" ? \"sse\" : \"http\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n tools,\r\n };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /**\r\n * Render env/header values. Copilot CLI is not documented to support native\r\n * `${env:VAR}` interpolation in mcp-config.json, so refs resolve to literals\r\n * at install time (the safe default for a no-native-interp host).\r\n */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n return resolveEnvRefsDeep({ ...env });\r\n }\r\n\r\n /** Render the tool allow-list. Copilot CLI expects `tools` on every entry; default [\"*\"]. */\r\n private renderTools(server: ServerDef): string[] {\r\n const include = server.tools?.include;\r\n return include && include.length > 0 ? [...include] : [\"*\"];\r\n }\r\n\r\n // ── Hook install / uninstall ─────────────────────────────────────────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST];\r\n const hooksDisabled = override?.hooks === false;\r\n // `hooks: false` disables only the NORMALIZED events; nativeHooks is a\r\n // sibling, copilot-cli-scoped declaration that installs regardless.\r\n const normalizedEvents = hooksDisabled ? [] : connector.hookEvents;\r\n const nativeHooks = override?.nativeHooks ?? {};\r\n const nativeEvents = Object.keys(nativeHooks);\r\n\r\n if (normalizedEvents.length === 0 && nativeEvents.length === 0) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: hooksDisabled\r\n ? \"hooks disabled for copilot-cli\"\r\n : \"connector declares no hooks\",\r\n },\r\n ];\r\n }\r\n\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const file = this.readJson<CopilotHooksFile>(hooksPath) ?? {};\r\n const __skip = this.malformedHookRootSkip(hooksPath, (file as Record<string, unknown>).hooks);\r\n if (__skip) return [__skip];\r\n const hooks = (file.hooks ??= {});\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of normalizedEvents) {\r\n // CAPABILITY FILTER: an event Copilot CLI's runtime does not deliver (e.g.\r\n // PostCompact — postCompact unset on `capabilities`) must NOT be written as\r\n // a dead hook the host never fires; report a graceful warn/skip instead.\r\n if (this.capabilities[EVENT_CAPABILITY[event]] !== true) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: hooksPath,\r\n detail: `${event} unsupported on copilot-cli — skipped`,\r\n });\r\n continue;\r\n }\r\n // Key the hooks file by the CLI's camelCase WIRE name — a PascalCase key\r\n // is silently dropped by the loader (see EVENT_WIRE_KEY). The home-bin\r\n // command keeps the PascalCase `event` token: that is the AC-internal\r\n // router event passed to parseEvent/formatReply, NOT a hooks-file key.\r\n const copilotEvent = EVENT_WIRE_KEY[event];\r\n if (copilotEvent === undefined) {\r\n // Defensive: a capability-flagged event with no wire key would write a\r\n // dead PascalCase key. Treat it like an unsupported event (warn-skip).\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: hooksPath,\r\n detail: `${event} unsupported on copilot-cli — skipped`,\r\n });\r\n continue;\r\n }\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);\r\n const matcher = connector.hooks[event]?.matcher;\r\n // OMIT the matcher key when there is none — Copilot CLI rejects an empty\r\n // string (matcher cannot be empty) and discards the whole hooks file.\r\n const entry: CopilotHookEntry = {\r\n ...(matcher ? { matcher } : {}),\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n\r\n const bucket = (hooks[copilotEvent] ??= []);\r\n const existingIdx = bucket.findIndex((e) => this.entryHasOurCommand(e, ctx));\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: `hooks.${copilotEvent} already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hooksPath,\r\n detail: `hooks.${copilotEvent}`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hooksPath,\r\n detail: `hooks.${copilotEvent}`,\r\n });\r\n }\r\n mutated = true;\r\n }\r\n\r\n // NATIVE passthrough events: copilot-cli-native event-name keys (e.g.\r\n // ErrorOccurred) filed VERBATIM into the hooks map — no EVENT_MAP, since they\r\n // ARE Copilot events. The command keeps the native token; matched by EXACT\r\n // command so a native key coinciding with a normalized one never clobbers it.\r\n for (const nativeEvent of nativeEvents) {\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, nativeEvent, connector.id);\r\n const matcher = nativeHooks[nativeEvent]?.matcher;\r\n // OMIT the matcher key when there is none (empty string fails validation).\r\n const entry: CopilotHookEntry = {\r\n ...(matcher ? { matcher } : {}),\r\n hooks: [{ type: \"command\", command }],\r\n };\r\n const bucket = (hooks[nativeEvent] ??= []);\r\n const existingIdx = bucket.findIndex((e) => e.hooks?.[0]?.command === command);\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: `hooks.${nativeEvent} (native) already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({\r\n platform: this.id,\r\n action: \"update\",\r\n path: hooksPath,\r\n detail: `hooks.${nativeEvent} (native)`,\r\n });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({\r\n platform: this.id,\r\n action: \"create\",\r\n path: hooksPath,\r\n detail: `hooks.${nativeEvent} (native)`,\r\n });\r\n }\r\n mutated = true;\r\n }\r\n\r\n if (mutated) {\r\n file.version = COPILOT_HOOKS_VERSION;\r\n this.writeJson(hooksPath, file, ctx.dryRun);\r\n }\r\n return changes;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const file = this.readJson<CopilotHooksFile>(hooksPath);\r\n const hooks = file?.hooks;\r\n if (!file || !hooks) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: \"no hooks section present\",\r\n },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const copilotEvent of Object.keys(hooks)) {\r\n const bucket = hooks[copilotEvent];\r\n if (!Array.isArray(bucket)) continue;\r\n\r\n // Strip our hook command from each entry; drop entries left empty so we\r\n // never remove another connector's (or the user's own) hook commands.\r\n const next: CopilotHookEntry[] = [];\r\n let removed = 0;\r\n for (const e of bucket) {\r\n const innerBefore = e.hooks?.length ?? 0;\r\n const inner = (e.hooks ?? []).filter((h) => !this.isOurCommand(h.command, ctx));\r\n removed += innerBefore - inner.length;\r\n // Preserve the entry's original matcher verbatim, but never synthesize an\r\n // empty string — Copilot CLI rejects `matcher: \"\"` (whole file discarded).\r\n if (inner.length > 0) next.push({ ...(e.matcher ? { matcher: e.matcher } : {}), hooks: inner });\r\n }\r\n\r\n if (removed > 0) {\r\n if (next.length > 0) hooks[copilotEvent] = next;\r\n else delete hooks[copilotEvent];\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path: hooksPath,\r\n detail: `hooks.${copilotEvent} (${removed})`,\r\n });\r\n mutated = true;\r\n }\r\n }\r\n\r\n if (mutated) {\r\n // When our uninstall empties the hooks map, delete this connector's\r\n // dedicated file rather than leaving an inert `{version:1,hooks:{}}` stub.\r\n // The file is owned solely by AC (see getHookConfigPath), so removing it on\r\n // full teardown is safe and keeps ~/.copilot/hooks clean.\r\n if (Object.keys(hooks).length === 0) {\r\n this.removeManagedFile(hooksPath, ctx.dryRun);\r\n } else {\r\n this.writeJson(hooksPath, file, ctx.dryRun);\r\n }\r\n }\r\n if (changes.length === 0) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path: hooksPath,\r\n detail: \"no matching hook entries\",\r\n });\r\n }\r\n return changes;\r\n }\r\n\r\n private entryHasOurCommand(entry: CopilotHookEntry, ctx: InstallContext): boolean {\r\n return (entry.hooks ?? []).some((h) => this.isOurCommand(h.command, ctx));\r\n }\r\n\r\n /** True when a hook command references our home binary AND this connector id\r\n * (anchored so a shared-prefix id can't collide — see isHomeBinHookCommand). */\r\n private isOurCommand(command: string | undefined, ctx: InstallContext): boolean {\r\n return isHomeBinHookCommand(command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Content surfaces: skills / subagents ─────────────────────────────────\r\n // CONTENT-ONLY: pure native-file writers. No runtime dispatch, no home-bin\r\n // pointer, no telemetry wrap. Each method is idempotent (byte-identical →\r\n // skip) via BaseAdapter.writeContentFile and reversible via removeContentFile.\r\n // Honors platforms[\"copilot-cli\"] per-surface false to skip. Commands are\r\n // unsupported here — they inherit the BaseAdapter skip/warn default.\r\n //\r\n // Path scoping: user scope lives under ~/.copilot (getConfigDir); project\r\n // scope lives under the shared <projectDir>/.github tree (the same files\r\n // vscode-copilot / jetbrains-copilot would write). We write identical content\r\n // and on uninstall remove only the files this connector wrote.\r\n\r\n /** Root dir for content surfaces: ~/.copilot (user) or <projectDir>/.github (project). */\r\n private contentDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".github\")\r\n : this.getConfigDir(ctx);\r\n }\r\n\r\n private skillsDir(ctx: InstallContext): string {\r\n return join(this.contentDir(ctx), \"skills\");\r\n }\r\n private agentsDir(ctx: InstallContext): string {\r\n return join(this.contentDir(ctx), \"agents\");\r\n }\r\n\r\n /** Native skill dir: <contentDir>/skills/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), name);\r\n }\r\n /** Native subagent file path: <contentDir>/agents/<name>.agent.md. */\r\n private subagentPath(ctx: InstallContext, name: string): string {\r\n return join(this.agentsDir(ctx), `${name}.agent.md`);\r\n }\r\n\r\n // ── Skills ────────────────────────────────────────────────────────────────\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for copilot-cli\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Subagents ───────────────────────────────────────────────────────────────\r\n\r\n override installSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.subagents === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"subagents disabled for copilot-cli\" }];\r\n }\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.writeContentFile(\r\n this.subagentPath(ctx, agent.name),\r\n renderSubagentMd(agent),\r\n ctx.dryRun,\r\n ),\r\n );\r\n }\r\n\r\n override uninstallSubagents(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.subagents.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no subagents\" }];\r\n }\r\n return connector.subagents.map((agent) =>\r\n this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun),\r\n );\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const mcpPath = this.getServerConfigPath(ctx);\r\n const hooksPath = this.getHookConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = ctx.connector.hookEvents;\r\n const checks: HealthCheck[] = [\r\n {\r\n name: `${this.name}: mcp-config.json present`,\r\n check: () =>\r\n existsSync(mcpPath)\r\n ? { status: \"OK\", detail: mcpPath }\r\n : { status: \"FAIL\", detail: `not found: ${mcpPath}` },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const file = this.readJson<CopilotHooksFile>(hooksPath);\r\n if (!file) return { status: \"FAIL\", detail: `cannot read ${hooksPath}` };\r\n const hooks = file.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) =>\r\n (e.hooks ?? []).some((h) =>\r\n isHomeBinHookCommand(h.command, homeBin, connectorId),\r\n ),\r\n ),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${hooksPath}` };\r\n },\r\n },\r\n ];\r\n\r\n // Content-surface checks: only assert presence of the surfaces this\r\n // connector declares (skip silently for surfaces it never asked for).\r\n // Commands are unsupported on Copilot CLI, so no command check.\r\n for (const skill of ctx.connector.skills) {\r\n const p = join(this.skillDir(ctx, skill.name), \"SKILL.md\");\r\n checks.push({\r\n name: `${this.name}: skill ${skill.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n for (const agent of ctx.connector.subagents) {\r\n const p = this.subagentPath(ctx, agent.name);\r\n checks.push({\r\n name: `${this.name}: subagent ${agent.name} present`,\r\n check: () =>\r\n existsSync(p) ? { status: \"OK\", detail: p } : { status: \"FAIL\", detail: `not found: ${p}` },\r\n });\r\n }\r\n return checks;\r\n }\r\n\r\n // ── Runtime: parse Copilot CLI stdin JSON → normalized event ─────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as CopilotWireInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = extractSessionId(input);\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n switch (event) {\r\n case \"PreToolUse\": {\r\n // Live 1.0.63 wire: camelCase `toolName` + `toolArgs` (a JSON-STRING).\r\n // snake_case `tool_name`/`tool_input` are tolerant fallbacks.\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: toolNameOf(input),\r\n toolInput: toolInputOf(input),\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUse\": {\r\n // PostToolUse fires success-only. The live 1.0.63 wire carries the\r\n // result under camelCase `toolResult.textResultForLlm`; the snake_case\r\n // `tool_result.text_result_for_llm` form is a tolerant fallback. Failures\r\n // arrive via the separate PostToolUseFailure event, so isError is never\r\n // set here.\r\n const toolOutput =\r\n input.toolResult?.textResultForLlm ?? input.tool_result?.text_result_for_llm;\r\n const ev: PostToolUseEvent = {\r\n ...base,\r\n toolName: toolNameOf(input),\r\n toolInput: toolInputOf(input),\r\n ...(typeof toolOutput === \"string\" ? { toolOutput } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SessionStart\": {\r\n const ev: SessionStartEvent = {\r\n ...base,\r\n source: normalizeSessionSource(input.source),\r\n };\r\n return ev;\r\n }\r\n case \"SessionEnd\": {\r\n const ev: SessionEndEvent = {\r\n ...base,\r\n ...(typeof input.reason === \"string\" ? { reason: input.reason } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"UserPromptSubmit\": {\r\n const ev: UserPromptSubmitEvent = {\r\n ...base,\r\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"PreCompact\": {\r\n const ev: PreCompactEvent = {\r\n ...base,\r\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\r\n ? { trigger: input.trigger }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"PostCompact\": {\r\n // Copilot CLI does not fire PostCompact natively (postCompact unset →\r\n // warn-skip at install). Parsed defensively, mirroring PreCompact, so a\r\n // manual/mis-routed dispatch normalizes instead of hitting the guard.\r\n const ev: PostCompactEvent = {\r\n ...base,\r\n ...(input.trigger === \"auto\" || input.trigger === \"manual\"\r\n ? { trigger: input.trigger }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n case \"Stop\": {\r\n // The host signals completion via stop_reason:\"end_turn\", not the\r\n // stopHookActive loop-guard boolean, so nothing maps onto StopEvent\r\n // beyond base (hooks-reference.md:449-457).\r\n const ev: StopEvent = { ...base };\r\n return ev;\r\n }\r\n case \"Notification\": {\r\n const ev: NotificationEvent = {\r\n ...base,\r\n message: typeof input.message === \"string\" ? input.message : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"PermissionRequest\": {\r\n // permissionRequest uses the PreToolUse shape (tool_name + tool_input\r\n // only); the host emits no permission_suggestions payload\r\n // (hooks-reference.md:218,627-649).\r\n const ev: PermissionRequestEvent = {\r\n ...base,\r\n toolName: toolNameOf(input),\r\n toolInput: toolInputOf(input),\r\n };\r\n return ev;\r\n }\r\n case \"PostToolUseFailure\": {\r\n // Host payload is the tool-event shape + `error`; it carries no\r\n // tool_use_id / is_interrupt / duration_ms correlation fields. (Demoted\r\n // on capabilities — below the live-fire bar — but parsed consistently\r\n // with the live camelCase tool dialect.)\r\n const ev: PostToolUseFailureEvent = {\r\n ...base,\r\n toolName: toolNameOf(input),\r\n toolInput: toolInputOf(input),\r\n error: typeof input.error === \"string\" ? input.error : \"\",\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStart\": {\r\n // The host's subagent identity field is `agent_name`; there is no\r\n // agent_id or agent_type (agent_display_name is a human label, not a\r\n // type), so agentType stays unset (hooks-reference.md:467-476).\r\n const ev: SubagentStartEvent = {\r\n ...base,\r\n ...(typeof input.agent_name === \"string\" ? { agentId: input.agent_name } : {}),\r\n };\r\n return ev;\r\n }\r\n case \"SubagentStop\": {\r\n // VS Code dialect: identity is `agent_name`, the subagent transcript is\r\n // the BASE `transcript_path` (not agent_transcript_path), and there is\r\n // no agent_type / last_assistant_message / stop_hook_active — the final\r\n // response is reachable only via transcript_path, and completion is\r\n // signalled by stop_reason (hooks-reference.md:497-507).\r\n const ev: SubagentStopEvent = {\r\n ...base,\r\n ...(typeof input.agent_name === \"string\" ? { agentId: input.agent_name } : {}),\r\n ...(typeof input.transcript_path === \"string\"\r\n ? { agentTranscriptPath: input.transcript_path }\r\n : {}),\r\n };\r\n return ev;\r\n }\r\n default: {\r\n // Exhaustive guard — every HookEventName is handled above.\r\n const _never: never = event;\r\n throw new Error(`unsupported copilot-cli hook event: ${String(_never)}`);\r\n }\r\n }\r\n }\r\n\r\n // ── Runtime: normalized response → Copilot CLI native hook reply ─────────\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const hookEventName = event;\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // PermissionRequest replies use the Claude-compatible nested\r\n // decision{behavior} envelope and are the ONE event where an EXPLICIT\r\n // \"allow\" is an ACTIVE grant (it suppresses the permission dialog) rather\r\n // than passthrough:\r\n // allow → decision{behavior:\"allow\"} (+updatedInput when set);\r\n // the host still enforces its own deny rules.\r\n // modify → an allow grant carrying updatedInput.\r\n // deny → decision{behavior:\"deny\", message}.\r\n // ask/context/void → NO decision output: fall through to the native\r\n // dialog (the dialog IS the ask).\r\n if (event === \"PermissionRequest\") {\r\n if (response.decision === \"deny\") {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n decision: {\r\n behavior: \"deny\",\r\n message: response.reason ?? \"Blocked by hook\",\r\n },\r\n },\r\n });\r\n }\r\n if (\r\n response.decision === \"allow\" ||\r\n (response.decision === \"modify\" && response.updatedInput)\r\n ) {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n decision: {\r\n behavior: \"allow\",\r\n ...(response.updatedInput\r\n ? { updatedInput: response.updatedInput }\r\n : {}),\r\n },\r\n },\r\n });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // PostToolUseFailure (recovery guidance beside the error) and SubagentStart\r\n // (context prepended to the SUBAGENT's conversation — creation is not\r\n // blockable on Copilot CLI) are observe/context-only: \"context\" emits\r\n // additionalContext, and a \"deny\" DEGRADES to the same shape carrying the\r\n // reason. Everything else passes through.\r\n if (event === \"PostToolUseFailure\" || event === \"SubagentStart\") {\r\n const context =\r\n decision === \"context\"\r\n ? response.additionalContext\r\n : decision === \"deny\"\r\n ? response.reason ?? response.additionalContext\r\n : undefined;\r\n if (context) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, additionalContext: context },\r\n });\r\n }\r\n return { exitCode: 0 };\r\n }\r\n\r\n // PreToolUse — the ONLY event whose reply the installed CLI 1.0.63 actually\r\n // reads, and it reads the permission decision FLAT at the TOP LEVEL of the\r\n // hook's stdout JSON (VERIFIED in app.js):\r\n // a => ({ permissionDecision: a?.permissionDecision,\r\n // permissionDecisionReason: a?.permissionDecisionReason,\r\n // modifiedArgs: a?.modifiedArgs })\r\n // There is NO `hookSpecificOutput` wrapper in 1.0.63 (the token appears zero\r\n // times in the bundle), so a nested reply leaves `a.permissionDecision`\r\n // undefined and the deny is SILENTLY IGNORED — the tool runs anyway. We\r\n // therefore emit the flat keys the host honors:\r\n // deny → { permissionDecision:\"deny\", permissionDecisionReason }\r\n // → host prints `Denied by preToolUse hook: <reason>`\r\n // ask → { permissionDecision:\"ask\", permissionDecisionReason }\r\n // modify → { modifiedArgs } (the host accepts a string or an object and\r\n // stringifies an object itself; we pass updatedInput as-is)\r\n // context/additionalContext → NO-OP on 1.0.63 (the bundle has no\r\n // additionalContext mechanism), so it degrades to exit 0.\r\n if (event === \"PreToolUse\") {\r\n if (decision === \"deny\") {\r\n return this.stdout({\r\n permissionDecision: \"deny\",\r\n permissionDecisionReason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n if (decision === \"ask\") {\r\n return this.stdout({\r\n permissionDecision: \"ask\",\r\n permissionDecisionReason: response.reason ?? \"Confirmation required by hook\",\r\n });\r\n }\r\n if (decision === \"modify\" && response.updatedInput) {\r\n return this.stdout({ modifiedArgs: response.updatedInput });\r\n }\r\n // allow / context / void → pass through (no honored reply on 1.0.63).\r\n return { exitCode: 0 };\r\n }\r\n\r\n // deny → block the action with a reason (exit 0; JSON carries the decision).\r\n // SubagentStop is the Stop-semantics exception: like Claude, the block is\r\n // the TOP-LEVEL {\"decision\":\"block\",\"reason\"} — it keeps the subagent\r\n // running with `reason` as its next instruction (the host \"can block and\r\n // force continuation\").\r\n //\r\n // NOTE: for every OTHER event, the installed CLI 1.0.63 attaches NO reply\r\n // reader (the bundle's args getter is `a => {}`), so what we emit below is a\r\n // no-op on this version; it is retained for forward-compatibility with a\r\n // CLI that does consume these decisions.\r\n if (decision === \"deny\") {\r\n if (event === \"SubagentStop\") {\r\n return this.stdout({\r\n decision: \"block\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"deny\",\r\n permissionDecisionReason: response.reason ?? \"Blocked by hook\",\r\n },\r\n });\r\n }\r\n\r\n // ask → prompt the user to confirm.\r\n if (decision === \"ask\") {\r\n return this.stdout({\r\n hookSpecificOutput: {\r\n hookEventName,\r\n permissionDecision: \"ask\",\r\n permissionDecisionReason: response.reason ?? \"Confirmation required by hook\",\r\n },\r\n });\r\n }\r\n\r\n // modify is a PreToolUse-only capability and is handled in the flat-reply\r\n // PreToolUse branch above; for every other event it falls through to allow\r\n // (output rewrite is unsupported on Copilot CLI).\r\n\r\n // context → inject soft guidance (also the SessionStart context path).\r\n if (decision === \"context\" && response.additionalContext) {\r\n return this.stdout({\r\n hookSpecificOutput: { hookEventName, additionalContext: response.additionalContext },\r\n });\r\n }\r\n\r\n // allow / void / unsupported-degradation → pass through with exit 0.\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/**\r\n * Extract a stable session id from a Copilot CLI wire payload.\r\n * Priority mirrors the Claude wire protocol: transcript UUID > sessionId > \"\".\r\n * The live 1.0.63 wire sends `sessionId` (camelCase); `session_id` is kept as a\r\n * tolerant fallback.\r\n */\r\nfunction extractSessionId(input: CopilotWireInput): string {\r\n if (typeof input.transcript_path === \"string\") {\r\n const m = input.transcript_path.match(/([a-f0-9-]{36})\\.jsonl$/);\r\n if (m && m[1]) return m[1];\r\n }\r\n if (typeof input.sessionId === \"string\" && input.sessionId !== \"\") {\r\n return input.sessionId;\r\n }\r\n if (typeof input.session_id === \"string\" && input.session_id !== \"\") {\r\n return input.session_id;\r\n }\r\n return \"\";\r\n}\r\n\r\n/**\r\n * Resolve the tool name from a tool-event payload — camelCase `toolName` (the\r\n * live 1.0.63 wire) with a snake_case `tool_name` fallback.\r\n */\r\nfunction toolNameOf(input: CopilotWireInput): string {\r\n if (typeof input.toolName === \"string\") return input.toolName;\r\n if (typeof input.tool_name === \"string\") return input.tool_name;\r\n return \"\";\r\n}\r\n\r\n/**\r\n * Resolve the tool input object from a tool-event payload. The live 1.0.63 wire\r\n * sends `toolArgs` as a JSON-STRING (`JSON.stringify(t.toolArgs,null,2)`), so we\r\n * parse it into an object; an already-object `toolArgs` is accepted as-is, and\r\n * the snake_case `tool_input` (already an object) is the fallback.\r\n */\r\nfunction toolInputOf(input: CopilotWireInput): Record<string, unknown> {\r\n const raw = input.toolArgs;\r\n if (typeof raw === \"string\") {\r\n try {\r\n const parsed = JSON.parse(raw);\r\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\r\n return parsed as Record<string, unknown>;\r\n }\r\n } catch {\r\n // Non-JSON string → fall through to the other shapes / empty object.\r\n }\r\n } else if (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\r\n return raw as Record<string, unknown>;\r\n }\r\n if (input.tool_input && typeof input.tool_input === \"object\") {\r\n return input.tool_input;\r\n }\r\n return {};\r\n}\r\n\r\nexport const adapter = new CopilotCliAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/crush/index.ts"],"sourcesContent":["/**\r\n * adapters/crush — Crush (Charm) platform adapter for agent-connector.\r\n *\r\n * Crush is a json-stdio host. A single JSON config file holds BOTH the MCP\r\n * server registrations and the hook registrations, and agent-connector MERGES\r\n * into it (never replaces it — unrelated keys are preserved verbatim):\r\n * - User scope: ~/.config/crush/crush.json\r\n * (Windows: %LOCALAPPDATA%\\crush\\crush.json)\r\n * - Project scope: ./.crush.json (preferred) or ./crush.json\r\n *\r\n * MCP servers live under the ROOT KEY \"mcp\" (NOT \"mcpServers\"). A stdio entry is\r\n * { type:\"stdio\", command, args, env, timeout:120, disabled:false }.\r\n * Hooks live under the top-level \"hooks\" key. Crush supports PreToolUse ONLY;\r\n * the hook reads the host's JSON on stdin and writes a JSON object on stdout\r\n * carrying a `decision` field. A deny is `{ decision:\"deny\", reason }` on\r\n * stdout (exit 0); an allow is an empty stdout (exit 0). Crush cannot rewrite\r\n * tool args/output nor inject session context — every non-deny decision (and\r\n * every non-PreToolUse event) degrades to a silent allow.\r\n *\r\n * SECURITY NOTE: Crush expands `$(...)` command substitution in its config file\r\n * at load time. We therefore NEVER emit shell-substitution syntax into the\r\n * command/args/env we write — every value is a literal path/string built by the\r\n * spawn helpers (which quote, not interpolate). Connector ids are kebab-case and\r\n * the home-bin path is an absolute literal, so nothing we write can be coerced\r\n * into a `$(...)` substitution.\r\n */\r\n\r\nimport { existsSync, statSync } from \"node:fs\";\r\nimport { dirname, join } from \"node:path\";\r\n\r\nimport { BaseAdapter } from \"../base.js\";\r\nimport type { Adapter, HookReply, InstallContext, NormalizedEvent } from \"../spi.js\";\r\nimport type {\r\n ChangeRecord,\r\n DetectedPlatform,\r\n HealthCheck,\r\n HookEventName,\r\n HookParadigm,\r\n HookResponse,\r\n PlatformCapabilities,\r\n PlatformId,\r\n PreToolUseEvent,\r\n ServerDef,\r\n Transport,\r\n} from \"../../core/types.js\";\r\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\r\nimport { localAppData, xdgConfigHome } from \"../../core/host-paths.js\";\r\nimport {\r\n buildHomeBinHookCommand,\r\n buildWrappedStdio,\r\n isHomeBinHookCommand,\r\n} from \"../../core/spawn.js\";\r\nimport { renderSkillMd } from \"../claude-code/render.js\";\r\n\r\nconst HOST: PlatformId = \"crush\";\r\n/** Crush registers MCP servers under \"mcp\" — NOT the \"mcpServers\" of Claude/Gemini. */\r\nconst MCP_ROOT_KEY = \"mcp\";\r\n/** Default per-server timeout (seconds) Crush expects on a stdio entry. */\r\nconst STDIO_TIMEOUT_SECONDS = 120;\r\n\r\n/**\r\n * Crush hook events agent-connector can register. Crush honors PreToolUse ONLY,\r\n * and only its deny decision is meaningful; every other normalized event has no\r\n * Crush equivalent and is skipped at install time.\r\n */\r\nconst CRUSH_HOOK_EVENTS = [\"PreToolUse\"] as const;\r\ntype CrushHookEventName = (typeof CRUSH_HOOK_EVENTS)[number];\r\n\r\n/**\r\n * Newer E1 canonical events with NO Crush analog (no permission-dialog,\r\n * tool-failure, or subagent lifecycle hook). Declared hooks for these warn-skip\r\n * (action `warn`, exit-1) — the established degradation convention. EVERY OTHER\r\n * unsupported event (the legacy SessionStart / Stop / … that previously vanished)\r\n * now reports a VISIBLE `skip` instead — see {@link CrushAdapter.declinedHookEvents}.\r\n * Nothing Crush cannot fire is ever silently dropped.\r\n */\r\nconst WARN_SKIP_EVENTS: ReadonlySet<HookEventName> = new Set([\r\n \"PermissionRequest\",\r\n \"PostToolUseFailure\",\r\n \"SubagentStart\",\r\n \"SubagentStop\",\r\n]);\r\n\r\n// ─────────────────────────────────────────────────────────────────────────\r\n// Native config shapes (only the parts we touch; rest preserved verbatim)\r\n// ─────────────────────────────────────────────────────────────────────────\r\n\r\n/** One hook registration entry as Crush stores it under top-level \"hooks\". */\r\ninterface CrushHookEntry {\r\n matcher?: string;\r\n command: string;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Crush's crush.json shape — only the keys we read/merge. */\r\ninterface CrushConfigFile {\r\n mcp?: Record<string, unknown>;\r\n hooks?: Record<string, CrushHookEntry[]>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/** Native stdio MCP server entry under \"mcp\". */\r\ninterface CrushStdioServer {\r\n type: \"stdio\";\r\n command: string;\r\n args: string[];\r\n env?: Record<string, string>;\r\n timeout: number;\r\n disabled: boolean;\r\n}\r\n\r\n/** Native HTTP MCP server entry under \"mcp\". */\r\ninterface CrushHttpServer {\r\n type: \"http\";\r\n url: string;\r\n headers?: Record<string, string>;\r\n timeout: number;\r\n disabled: boolean;\r\n}\r\n\r\n/** Raw Crush hook stdin payload (Claude-style: PascalCase event, snake_case fields). */\r\ninterface CrushHookInput {\r\n tool_name?: string;\r\n tool_input?: Record<string, unknown>;\r\n session_id?: string;\r\n cwd?: string;\r\n hook_event_name?: string;\r\n connector?: unknown;\r\n}\r\n\r\nexport class CrushAdapter extends BaseAdapter implements Adapter {\r\n readonly id: PlatformId = HOST;\r\n readonly name = \"Crush\";\r\n readonly paradigm: HookParadigm = \"json-stdio\";\r\n\r\n readonly capabilities: PlatformCapabilities = {\r\n // Memory surface: AGENTS.md-first managed block via the BaseAdapter default\r\n // (memoryTargets: project <projectDir>/AGENTS.md; user scope where documented).\r\n supportsMemory: true,\r\n // PreToolUse deny is the only hook decision Crush honors.\r\n preToolUse: true,\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 // Newer events: Crush honors PreToolUse ONLY — no permission-dialog,\r\n // tool-failure, or subagent lifecycle events exist, so permissionRequest /\r\n // postToolUseFailure / subagentStart / subagentStop stay unset — install\r\n // reports the standard skip-warn for them.\r\n // Crush is deny-only: it cannot rewrite tool args/output nor inject context.\r\n canModifyArgs: false,\r\n canModifyOutput: false,\r\n canInjectSessionContext: false,\r\n transports: [\"stdio\", \"http\"],\r\n // Content surface: Crush auto-discovers Agent-Skills dirs (dir-per-skill\r\n // SKILL.md). Paths are HARD-CODED — no crush.json entry needed\r\n // (charmbracelet/crush internal/config/load.go GlobalSkillsDirs +\r\n // ProjectSkillsDir, fetched 2026-06-16):\r\n // project → <projectDir>/.crush/skills/<name>/SKILL.md (crush-native, in\r\n // projectSkillSubdirs; preferred over .agents/.claude/.cursor)\r\n // user → ~/.config/crush/skills/<name>/SKILL.md (home.Config()/crush/\r\n // skills; XDG_CONFIG_HOME or ~/.config).\r\n supportsSkills: true,\r\n };\r\n\r\n // ── Detection ────────────────────────────────────────────────────────────\r\n\r\n detectInstalled(projectDir: string): DetectedPlatform {\r\n const userDir = this.userConfigDir();\r\n const userConfig = join(userDir, \"crush.json\");\r\n const projectDot = join(projectDir, \".crush.json\");\r\n const projectBare = join(projectDir, \"crush.json\");\r\n const userInstalled = existsSync(userDir) || existsSync(userConfig);\r\n const projInstalled = existsSync(projectDot) || existsSync(projectBare);\r\n const installed = userInstalled || projInstalled;\r\n // Report the scope/path that actually matched so a project-only install is\r\n // not misreported as a (non-existent) user install.\r\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\r\n const projectPath = existsSync(projectBare) && !existsSync(projectDot) ? projectBare : projectDot;\r\n const configPath = scope === \"project\" ? projectPath : userConfig;\r\n return {\r\n id: this.id,\r\n name: this.name,\r\n installed,\r\n paradigm: this.paradigm,\r\n capabilities: this.capabilities,\r\n configPath,\r\n scope,\r\n reason: installed\r\n ? scope === \"project\"\r\n ? `found project Crush config at ${configPath}`\r\n : `found Crush config under ${userDir}`\r\n : `no Crush config at ${userConfig}`,\r\n confidence: installed ? \"high\" : \"low\",\r\n };\r\n }\r\n\r\n // ── Native paths ─────────────────────────────────────────────────────────\r\n\r\n getConfigDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\" ? ctx.projectDir : this.userConfigDir();\r\n }\r\n\r\n /** MCP servers and hooks share ONE crush.json file. */\r\n getServerConfigPath(ctx: InstallContext): string {\r\n if (ctx.scope === \"project\") {\r\n // Prefer ./.crush.json, but MERGE into an existing ./crush.json if that is\r\n // the file already present (so we don't fork the user's config in two).\r\n const dot = join(ctx.projectDir, \".crush.json\");\r\n const bare = join(ctx.projectDir, \"crush.json\");\r\n if (existsSync(bare) && !existsSync(dot)) return bare;\r\n return dot;\r\n }\r\n return join(this.userConfigDir(), \"crush.json\");\r\n }\r\n\r\n /** Hooks live in the SAME crush.json under the top-level \"hooks\" key. */\r\n getHookConfigPath(ctx: InstallContext): string {\r\n return this.getServerConfigPath(ctx);\r\n }\r\n\r\n /**\r\n * Crush's user config dir, mirroring its own `home.Config()` resolution\r\n * (charmbracelet/crush internal/home/home.go: `cmp.Or(XDG_CONFIG_HOME, ~/.config)`):\r\n * - Windows: %LOCALAPPDATA%\\crush (Local; falls back to AppData/Local)\r\n * - macOS / Linux: $XDG_CONFIG_HOME/crush or ~/.config/crush\r\n * (crush honors $XDG_CONFIG_HOME on Linux/macOS; the `xdgConfigHome()` helper\r\n * is byte-identical to ~/.config when XDG is unset.)\r\n */\r\n private userConfigDir(): string {\r\n if (process.platform === \"win32\") {\r\n return join(localAppData(), \"crush\");\r\n }\r\n return join(xdgConfigHome(), \"crush\");\r\n }\r\n\r\n // ── MCP server install / uninstall (crush.json → mcp.<id>) ───────────────\r\n\r\n installServer(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n const override = connector.platforms[HOST]?.server;\r\n if (!connector.server || override === false) {\r\n return [\r\n {\r\n platform: this.id,\r\n action: \"skip\",\r\n detail: connector.server\r\n ? \"server registration disabled for crush\"\r\n : \"connector declares no MCP server\",\r\n },\r\n ];\r\n }\r\n\r\n // Shallow-merge any per-platform server override into the base ServerDef.\r\n const server: ServerDef =\r\n override && typeof override === \"object\"\r\n ? { ...connector.server, ...override }\r\n : connector.server;\r\n\r\n const serverPath = this.getServerConfigPath(ctx);\r\n const entry = this.renderServerEntry(ctx, server);\r\n\r\n return [\r\n this.upsertServerInJson(serverPath, MCP_ROOT_KEY, connector.id, entry, ctx.dryRun),\r\n ];\r\n }\r\n\r\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\r\n const serverPath = this.getServerConfigPath(ctx);\r\n return [\r\n this.removeServerFromJson(serverPath, MCP_ROOT_KEY, ctx.connector.id, ctx.dryRun),\r\n ];\r\n }\r\n\r\n /**\r\n * Render a normalized ServerDef into Crush's native \"mcp\" entry. Crush has no\r\n * documented native `${env:VAR}` interpolation — and it expands `$(...)` at\r\n * load time — so refs are resolved to LITERALS at install time (the safe\r\n * posture for a host without native interpolation, matching Codex/Kimi).\r\n */\r\n private renderServerEntry(\r\n ctx: InstallContext,\r\n server: ServerDef,\r\n ): CrushStdioServer | CrushHttpServer {\r\n const transport: Transport = server.transport;\r\n const disabled = server.enabled === false;\r\n\r\n if (transport === \"stdio\") {\r\n let command = server.command ?? \"\";\r\n let args = [...(server.args ?? [])];\r\n\r\n // Transparent telemetry wrapping: route the real command through\r\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\r\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\r\n\r\n // Resolve to literals — never emit `$(...)` Crush would expand at load.\r\n command = resolveEnvRefsDeep(command);\r\n args = resolveEnvRefsDeep(args);\r\n\r\n const entry: CrushStdioServer = {\r\n type: \"stdio\",\r\n command,\r\n args,\r\n timeout: STDIO_TIMEOUT_SECONDS,\r\n disabled,\r\n };\r\n const env = this.renderEnv(server.env);\r\n if (env) entry.env = env;\r\n return entry;\r\n }\r\n\r\n // http (and any other remote transport we surface) — Crush registers a URL.\r\n const entry: CrushHttpServer = {\r\n type: \"http\",\r\n url: resolveEnvRefsDeep(server.url ?? \"\"),\r\n timeout: STDIO_TIMEOUT_SECONDS,\r\n disabled,\r\n };\r\n const headers = this.renderEnv(server.headers);\r\n if (headers) entry.headers = headers;\r\n return entry;\r\n }\r\n\r\n /** Resolve env/header values to literals (no native interpolation on Crush). */\r\n private renderEnv(\r\n env: Record<string, string> | undefined,\r\n ): Record<string, string> | undefined {\r\n if (!env || Object.keys(env).length === 0) return undefined;\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(resolveEnvRefsDeep({ ...env }))) {\r\n out[k] = String(v);\r\n }\r\n return out;\r\n }\r\n\r\n // ── Hook install / uninstall (crush.json → hooks.PreToolUse[]) ───────────\r\n\r\n installHooks(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.hooks === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"hooks disabled for crush\" }];\r\n }\r\n\r\n const events = this.effectiveHookEvents(ctx);\r\n const declined = this.declinedHookEvents(ctx);\r\n const path = this.getHookConfigPath(ctx);\r\n\r\n if (events.length === 0 && declined.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", path, detail: \"no hooks declared\" }];\r\n }\r\n\r\n const cfg = this.readJson<CrushConfigFile>(path) ?? {};\r\n const __skip = this.malformedHookRootSkip(path, (cfg as Record<string, unknown>).hooks);\r\n if (__skip) return [__skip];\r\n const hooks = (cfg.hooks ??= {});\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n // EVERY declared event Crush cannot fire is reported, never silently dropped.\r\n // The 4 newer E1 events keep the established `warn` (exit-1); the legacy\r\n // events (SessionStart, Stop, …) — silently dropped before this fix — surface\r\n // as a VISIBLE `skip` so the install exit code is unchanged for them.\r\n for (const { event, action } of declined) {\r\n changes.push({\r\n platform: this.id,\r\n action,\r\n path,\r\n detail: `${event} has no Crush hook equivalent — skipped`,\r\n });\r\n }\r\n\r\n for (const event of events) {\r\n const command = buildHomeBinHookCommand(ctx.homeBinPath, HOST, event, connector.id);\r\n const matcher = connector.hooks[event]?.matcher ?? \"\";\r\n const entry: CrushHookEntry = { matcher, command };\r\n\r\n const bucket = (hooks[event] ??= []);\r\n const existingIdx = bucket.findIndex((e) => this.isOurHook(e, ctx));\r\n\r\n if (existingIdx >= 0) {\r\n if (JSON.stringify(bucket[existingIdx]) === JSON.stringify(entry)) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: `hooks.${event} already registered`,\r\n });\r\n continue;\r\n }\r\n bucket[existingIdx] = entry;\r\n changes.push({ platform: this.id, action: \"update\", path, detail: `hooks.${event}` });\r\n } else {\r\n bucket.push(entry);\r\n changes.push({ platform: this.id, action: \"create\", path, detail: `hooks.${event}` });\r\n }\r\n mutated = true;\r\n }\r\n\r\n if (mutated) this.writeJson(path, cfg, ctx.dryRun);\r\n return changes;\r\n }\r\n\r\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\r\n const path = this.getHookConfigPath(ctx);\r\n const cfg = this.readJson<CrushConfigFile>(path);\r\n const hooks = cfg?.hooks;\r\n if (!cfg || !hooks) {\r\n return [\r\n { platform: this.id, action: \"skip\", path, detail: \"no hooks section present\" },\r\n ];\r\n }\r\n\r\n const changes: ChangeRecord[] = [];\r\n let mutated = false;\r\n\r\n for (const event of Object.keys(hooks)) {\r\n const bucket = hooks[event];\r\n if (!Array.isArray(bucket)) continue;\r\n\r\n // Keep every entry that is NOT ours (anchored match) so a shared-prefix\r\n // connector id — or the user's own hook — is never stripped.\r\n const kept = bucket.filter((e) => !this.isOurHook(e, ctx));\r\n const removed = bucket.length - kept.length;\r\n if (removed > 0) {\r\n if (kept.length > 0) hooks[event] = kept;\r\n else delete hooks[event];\r\n changes.push({\r\n platform: this.id,\r\n action: \"remove\",\r\n path,\r\n detail: `hooks.${event} (${removed})`,\r\n });\r\n mutated = true;\r\n }\r\n }\r\n\r\n if (mutated) this.writeJson(path, cfg, ctx.dryRun);\r\n if (changes.length === 0) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"skip\",\r\n path,\r\n detail: \"no matching hook entries\",\r\n });\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Internal helpers ─────────────────────────────────────────────────────\r\n\r\n /** Which canonical hook events to register for Crush, honoring overrides. */\r\n private effectiveHookEvents(ctx: InstallContext): CrushHookEventName[] {\r\n if (ctx.connector.platforms[HOST]?.hooks === false) return [];\r\n return CRUSH_HOOK_EVENTS.filter((e) => ctx.connector.hookEvents.includes(e));\r\n }\r\n\r\n /**\r\n * Every declared event Crush has NO native equivalent for, tagged with the\r\n * severity install reports it at — so a host that cannot fire an event is\r\n * NEVER silent. The 4 newer E1 events ({@link WARN_SKIP_EVENTS}) keep `warn`\r\n * (exit-1, the established convention); every other unsupported event (the\r\n * legacy SessionStart / Stop / … that used to vanish) reports a visible\r\n * `skip` (counted, but exit-0-preserving).\r\n */\r\n private declinedHookEvents(\r\n ctx: InstallContext,\r\n ): { event: HookEventName; action: \"warn\" | \"skip\" }[] {\r\n if (ctx.connector.platforms[HOST]?.hooks === false) return [];\r\n return ctx.connector.hookEvents\r\n .filter((e) => !(CRUSH_HOOK_EVENTS as readonly string[]).includes(e))\r\n .map((event) => ({\r\n event,\r\n action: WARN_SKIP_EVENTS.has(event) ? (\"warn\" as const) : (\"skip\" as const),\r\n }));\r\n }\r\n\r\n /**\r\n * Does this hook entry belong to this connector? Anchored on the home-bin +\r\n * connector-id token (isHomeBinHookCommand) so uninstalling a shared-prefix\r\n * connector id never strips a sibling's hook.\r\n */\r\n private isOurHook(entry: CrushHookEntry | undefined, ctx: InstallContext): boolean {\r\n if (!entry || typeof entry !== \"object\") return false;\r\n return isHomeBinHookCommand(entry.command, ctx.homeBinPath, ctx.connector.id);\r\n }\r\n\r\n // ── Content surface: skills (dir-per-skill SKILL.md) ─────────────────────\r\n // CONTENT-ONLY: pure native-file writers, mirroring goose/openclaw. No\r\n // runtime dispatch, no home-bin pointer, no telemetry wrap. Each write is\r\n // idempotent (byte-identical → skip) via writeContentFile and reversible via\r\n // removeContentFile + removeDirIfEmpty (never rm -rf a shared dir). Honors\r\n // platforms[\"crush\"].skills === false. No crush.json entry is written — Crush\r\n // auto-discovers these hard-coded dirs (load.go GlobalSkillsDirs/ProjectSkillsDir).\r\n //\r\n // Native locations (Agent-Skills spec — dir-per-skill SKILL.md + resources):\r\n // project → <projectDir>/.crush/skills/<name>/SKILL.md\r\n // user → ~/.config/crush/skills/<name>/SKILL.md\r\n\r\n /** The skills ROOT dir for ctx.scope (parent of every per-skill dir). */\r\n private skillsDir(ctx: InstallContext): string {\r\n return ctx.scope === \"project\"\r\n ? join(ctx.projectDir, \".crush\", \"skills\")\r\n : join(this.userConfigDir(), \"skills\");\r\n }\r\n\r\n /** Per-skill directory: <skillsDir>/<name>. */\r\n private skillDir(ctx: InstallContext, name: string): string {\r\n return join(this.skillsDir(ctx), name);\r\n }\r\n\r\n override installSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.platforms[HOST]?.skills === false) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"skills disabled for crush\" }];\r\n }\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Guard a skills path occupied by a regular FILE (would throw ENOTDIR on\r\n // mkdir): skip-warn rather than crash (the cline `.clinerules`-is-a-file\r\n // precedent). The per-skill dir or any parent up to the scope root counts.\r\n const blockingFile = firstFileOnPath(this.skillsDir(ctx), dir);\r\n if (blockingFile !== null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n path: blockingFile,\r\n detail: `${blockingFile} is a file, not a directory; skill \"${skill.name}\" skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(\r\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\r\n );\r\n // Bundle any resource files beside SKILL.md (relative path → contents).\r\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\r\n // (config-time validation already rejects these, but never trust input).\r\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) {\r\n changes.push({\r\n platform: this.id,\r\n action: \"warn\",\r\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\r\n });\r\n continue;\r\n }\r\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\r\n }\r\n }\r\n return changes;\r\n }\r\n\r\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\r\n const { connector } = ctx;\r\n if (connector.skills.length === 0) {\r\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\r\n }\r\n const changes: ChangeRecord[] = [];\r\n for (const skill of connector.skills) {\r\n const dir = this.skillDir(ctx, skill.name);\r\n // Remove only the files we wrote (SKILL.md + declared resources), then the\r\n // skill dir itself when we own its full contents.\r\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\r\n for (const rel of Object.keys(skill.resources ?? {})) {\r\n const target = this.resolveWithin(dir, rel);\r\n if (target === null) continue; // never delete outside the skill dir\r\n changes.push(this.removeContentFile(target, ctx.dryRun));\r\n }\r\n // Only remove the skill dir when WE own its full contents — never rm -rf a\r\n // dir that still holds user-added / sibling-tool / shared files.\r\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\r\n }\r\n return changes;\r\n }\r\n\r\n // ── Diagnostics ──────────────────────────────────────────────────────────\r\n\r\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\r\n const configPath = this.getServerConfigPath(ctx);\r\n const connectorId = ctx.connector.id;\r\n const homeBin = ctx.homeBinPath;\r\n const hookEvents = this.effectiveHookEvents(ctx);\r\n return [\r\n {\r\n name: `${this.name}: crush.json present`,\r\n check: () =>\r\n existsSync(configPath)\r\n ? { status: \"OK\", detail: configPath }\r\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\r\n },\r\n {\r\n name: `${this.name}: mcp.${connectorId} registered`,\r\n check: () => {\r\n if (!ctx.connector.server) return { status: \"OK\", detail: \"no MCP server declared\" };\r\n const cfg = this.readJson<CrushConfigFile>(configPath);\r\n const bucket = cfg?.[MCP_ROOT_KEY];\r\n const present =\r\n typeof bucket === \"object\" && bucket !== null && connectorId in bucket;\r\n return present\r\n ? { status: \"OK\", detail: `mcp.${connectorId}` }\r\n : { status: \"FAIL\", detail: `mcp.${connectorId} not found in ${configPath}` };\r\n },\r\n },\r\n {\r\n name: `${this.name}: hook command registered`,\r\n check: () => {\r\n if (hookEvents.length === 0) {\r\n return { status: \"OK\", detail: \"no hooks declared\" };\r\n }\r\n const cfg = this.readJson<CrushConfigFile>(configPath);\r\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\r\n const hooks = cfg.hooks ?? {};\r\n const registered = Object.values(hooks).some((entries) =>\r\n (entries ?? []).some((e) => isHomeBinHookCommand(e?.command, homeBin, connectorId)),\r\n );\r\n return registered\r\n ? { status: \"OK\", detail: \"hook command present\" }\r\n : { status: \"FAIL\", detail: `no hook for ${connectorId} in ${configPath}` };\r\n },\r\n },\r\n ];\r\n }\r\n\r\n // ── Runtime: parse Crush stdin JSON → normalized event ───────────────────\r\n\r\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\r\n const input = (raw ?? {}) as CrushHookInput;\r\n const connectorId = typeof input.connector === \"string\" ? input.connector : \"\";\r\n const sessionId = typeof input.session_id === \"string\" ? input.session_id : \"\";\r\n const projectDir = typeof input.cwd === \"string\" ? input.cwd : undefined;\r\n\r\n const base = {\r\n hostPlatform: HOST,\r\n connectorId,\r\n sessionId,\r\n raw,\r\n ...(projectDir !== undefined ? { projectDir } : {}),\r\n } as const;\r\n\r\n if (event === \"PreToolUse\") {\r\n const ev: PreToolUseEvent = {\r\n ...base,\r\n toolName: input.tool_name ?? \"\",\r\n toolInput: input.tool_input ?? {},\r\n };\r\n return ev;\r\n }\r\n\r\n // Crush only ever delivers PreToolUse. Surface anything else loudly rather\r\n // than silently mis-parsing a payload the host cannot actually produce.\r\n throw new Error(`unsupported crush hook event: ${String(event)}`);\r\n }\r\n\r\n // ── Runtime: normalized response → Crush native hook reply ───────────────\r\n\r\n formatReply(event: HookEventName, response: HookResponse): HookReply {\r\n const decision = response.decision ?? \"allow\";\r\n\r\n // deny → Crush blocks the pending tool call via a stdout JSON object with a\r\n // `decision:\"deny\"` (+ reason). Only PreToolUse is honored; every other\r\n // event/decision degrades to a silent allow.\r\n if (decision === \"deny\" && event === \"PreToolUse\") {\r\n return this.stdout({\r\n decision: \"deny\",\r\n reason: response.reason ?? \"Blocked by hook\",\r\n });\r\n }\r\n\r\n // allow / modify / context / ask / unsupported → pass through (exit 0, empty\r\n // stdout). Crush cannot rewrite args/output or inject context, so those are\r\n // dropped fail-open here (the deny path above is the only fail-safe lever).\r\n return { exitCode: 0 };\r\n }\r\n\r\n private stdout(payload: unknown): HookReply {\r\n return { exitCode: 0, stdout: JSON.stringify(payload) };\r\n }\r\n}\r\n\r\n/**\r\n * Walk every path segment from `root` down to (and including) `leaf` and return\r\n * the FIRST one that exists as a regular file (which would make a mkdir for the\r\n * skill dir throw ENOTDIR), else null. Used to skip-warn instead of crashing\r\n * when a user has a `.crush/skills` file where we need a directory.\r\n */\r\nfunction firstFileOnPath(root: string, leaf: string): string | null {\r\n const segments: string[] = [];\r\n let cur = leaf;\r\n while (cur.length >= root.length) {\r\n segments.unshift(cur);\r\n if (cur === root) break;\r\n const parent = dirname(cur);\r\n if (parent === cur) break;\r\n cur = parent;\r\n }\r\n for (const p of segments) {\r\n if (!existsSync(p)) return null; // nothing planted from here down\r\n try {\r\n if (statSync(p).isFile()) return p;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nexport const adapter = new CrushAdapter();\r\nexport default adapter;\r\n"],"mappings":"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","names":["entry"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/usage/readers/crush.ts"],"sourcesContent":["/**\r\n * usage/readers/crush — Crush CLI usage reader (SQLite, cost-only).\r\n *\r\n * Faithful port of tokscale sessions/crush.rs. Crush persists usage in a single\r\n * SQLite database at ~/.cache/crush/crush.db (the host root, incl. the\r\n * AGENT_CONNECTOR_CRUSH_DIR override, is resolved by paths.ts hostRoots(\"crush\")).\r\n *\r\n * Crush stores reliable session-level COST but NOT a stable per-message token\r\n * breakdown. Tokscale v1 therefore preserves cost and assistant-message counts\r\n * without fabricating token precision:\r\n * - assistant messages (across the whole session tree) are grouped by LOCAL day;\r\n * - the root session's cost is allocated across those day buckets in proportion\r\n * to each bucket's assistant-message count (the last bucket absorbs the\r\n * rounding remainder so the per-day costs sum back to the stored total);\r\n * - all five token dimensions stay 0;\r\n * - confidence is \"host-estimated\" (cost is real, tokens are not reported).\r\n * A costed root session with no assistant messages emits a single zero-count,\r\n * cost-bearing record at its updated_at (else created_at) timestamp.\r\n *\r\n * Two SELECTs:\r\n * 1. root sessions — SELECT id, cost, created_at, updated_at FROM sessions\r\n * WHERE parent_session_id IS NULL\r\n * AND (COALESCE(message_count,0) > 0 OR COALESCE(cost,0) > 0)\r\n * ORDER BY created_at ASC;\r\n * 2. assistant message timestamps, attributed to their ROOT session via a\r\n * RECURSIVE CTE over parent_session_id (so a descendant session's assistant\r\n * messages count toward its root), filtered to role = 'assistant'.\r\n *\r\n * Timestamps tolerate both second-precision (auto ×1000) and millisecond-precision\r\n * values; non-positive timestamps are dropped. Model/provider are the constants\r\n * \"session-total\" / \"crush\". Session id = \"<db_path>:<root_session_id>\". No\r\n * project/workspace in the schema.\r\n *\r\n * Fail-open: db missing/locked/unreadable → openSqlite returns null → []; no root\r\n * sessions → []; a bad row is skipped, never thrown.\r\n */\r\n\r\nimport type { TokenBreakdown, UsageReader, UsageRecord } from \"../types.js\";\r\nimport { emptyTokens } from \"../aggregate.js\";\r\nimport { firstExistingRoot } from \"../paths.js\";\r\nimport { openSqlite } from \"../sqlite.js\";\r\n\r\nconst PLATFORM_ID = \"crush\" as const;\r\nconst CRUSH_MODEL_ID = \"session-total\";\r\nconst CRUSH_PROVIDER_ID = \"crush\";\r\n\r\n/** Root session row (cost lives here; tokens are not trustworthy per-message). */\r\ninterface CrushSession {\r\n id: string;\r\n cost: number;\r\n createdAt: number;\r\n updatedAt: number;\r\n}\r\n\r\n/** One local-day bucket of assistant messages within a root session. */\r\ninterface DayBucket {\r\n timestampMs: number;\r\n messageCount: number;\r\n}\r\n\r\nconst ROOT_SESSIONS_QUERY = `\r\n SELECT id, cost, created_at, updated_at\r\n FROM sessions\r\n WHERE parent_session_id IS NULL\r\n AND (COALESCE(message_count, 0) > 0 OR COALESCE(cost, 0) > 0)\r\n ORDER BY created_at ASC\r\n`;\r\n\r\nconst ASSISTANT_BUCKETS_QUERY = `\r\n WITH RECURSIVE session_tree(root_session_id, session_id) AS (\r\n SELECT id, id\r\n FROM sessions\r\n WHERE parent_session_id IS NULL\r\n\r\n UNION ALL\r\n\r\n SELECT st.root_session_id, s.id\r\n FROM sessions s\r\n JOIN session_tree st ON s.parent_session_id = st.session_id\r\n )\r\n SELECT st.root_session_id, m.created_at\r\n FROM session_tree st\r\n JOIN messages m ON m.session_id = st.session_id\r\n WHERE m.role = 'assistant'\r\n ORDER BY st.root_session_id ASC, m.created_at ASC\r\n`;\r\n\r\n/** Coerce an unknown SQLite cell to a finite number, or null when absent/garbage. */\r\nfunction toNumberOrNull(v: unknown): number | null {\r\n if (v === null || v === undefined) return null;\r\n const n = typeof v === \"number\" ? v : typeof v === \"string\" ? Number(v) : NaN;\r\n return Number.isFinite(n) ? n : null;\r\n}\r\n\r\n/** Coerce an unknown SQLite cell to an integer (0 on absence/garbage). */\r\nfunction toInt(v: unknown): number {\r\n const n = toNumberOrNull(v);\r\n return n === null ? 0 : Math.trunc(n);\r\n}\r\n\r\n/**\r\n * Normalize a raw Crush timestamp to epoch ms (port of normalize_crush_timestamp_ms):\r\n * non-positive → null; values >= 100_000_000_000 are already ms; smaller values are\r\n * seconds and are multiplied by 1000.\r\n */\r\nfunction normalizeCrushTimestampMs(raw: number): number | null {\r\n if (raw <= 0) return null;\r\n if (raw >= 100_000_000_000) return raw;\r\n return raw * 1000;\r\n}\r\n\r\n/**\r\n * Local-day key \"YYYY-MM-DD\" for an epoch-ms timestamp (port of local_day_key):\r\n * uses the host LOCAL timezone (matches chrono::Local), so buckets follow the\r\n * machine's calendar day. Returns null when the timestamp is not a valid date.\r\n */\r\nfunction localDayKey(timestampMs: number): string | null {\r\n const d = new Date(timestampMs);\r\n if (Number.isNaN(d.getTime())) return null;\r\n const year = d.getFullYear();\r\n const month = d.getMonth() + 1;\r\n const day = d.getDate();\r\n return `${year.toString().padStart(4, \"0\")}-${month\r\n .toString()\r\n .padStart(2, \"0\")}-${day.toString().padStart(2, \"0\")}`;\r\n}\r\n\r\n/**\r\n * Fallback session timestamp (port of fallback_session_timestamp_ms): updated_at,\r\n * else created_at, each run through the s↔ms normalization.\r\n */\r\nfunction fallbackSessionTimestampMs(updatedAt: number, createdAt: number): number | null {\r\n return normalizeCrushTimestampMs(updatedAt) ?? normalizeCrushTimestampMs(createdAt);\r\n}\r\n\r\n/** Load root sessions carrying a message count or cost (port of load_root_sessions). */\r\nfunction loadRootSessions(db: { all(sql: string): Array<Record<string, unknown>> }): CrushSession[] {\r\n const out: CrushSession[] = [];\r\n for (const row of db.all(ROOT_SESSIONS_QUERY)) {\r\n const id = row.id;\r\n if (typeof id !== \"string\") continue;\r\n out.push({\r\n id,\r\n cost: toNumberOrNull(row.cost) ?? 0,\r\n createdAt: toInt(row.created_at),\r\n updatedAt: toInt(row.updated_at),\r\n });\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Bucket assistant messages by root session + local day (port of\r\n * load_assistant_buckets). The recursive CTE attributes every descendant\r\n * session's assistant messages to its root; within a root the per-day bucket\r\n * keeps the MIN timestamp and counts the messages, and buckets are emitted in\r\n * ascending day order (BTreeMap → insertion-ordered Map keyed by day).\r\n */\r\nfunction loadAssistantBuckets(\r\n db: { all(sql: string): Array<Record<string, unknown>> },\r\n): Map<string, DayBucket[]> {\r\n // root session id → (local day → bucket), days kept sorted ascending.\r\n const sessionDays = new Map<string, Map<string, DayBucket>>();\r\n\r\n for (const row of db.all(ASSISTANT_BUCKETS_QUERY)) {\r\n const sessionId = row.root_session_id;\r\n if (typeof sessionId !== \"string\") continue;\r\n\r\n const createdAt = toInt(row.created_at);\r\n const timestampMs = normalizeCrushTimestampMs(createdAt);\r\n if (timestampMs === null) continue;\r\n const day = localDayKey(timestampMs);\r\n if (day === null) continue;\r\n\r\n let dayMap = sessionDays.get(sessionId);\r\n if (dayMap === undefined) {\r\n dayMap = new Map<string, DayBucket>();\r\n sessionDays.set(sessionId, dayMap);\r\n }\r\n const existing = dayMap.get(day);\r\n if (existing === undefined) {\r\n dayMap.set(day, { timestampMs, messageCount: 1 });\r\n } else {\r\n existing.timestampMs = Math.min(existing.timestampMs, timestampMs);\r\n existing.messageCount += 1;\r\n }\r\n }\r\n\r\n const out = new Map<string, DayBucket[]>();\r\n for (const [sessionId, dayMap] of sessionDays) {\r\n // Sort by day key ascending to mirror the Rust BTreeMap iteration order.\r\n const sortedDays = [...dayMap.keys()].sort();\r\n out.set(\r\n sessionId,\r\n sortedDays.map((day) => dayMap.get(day) as DayBucket),\r\n );\r\n }\r\n return out;\r\n}\r\n\r\n/** The Crush CLI usage reader singleton. */\r\nconst crushReader: UsageReader = {\r\n platformId: PLATFORM_ID,\r\n kind: \"local\",\r\n async read({ sinceMs }: { sinceMs?: number }): Promise<UsageRecord[]> {\r\n const dbPath = firstExistingRoot(PLATFORM_ID);\r\n if (dbPath === undefined) return []; // no crush.db → fail-open\r\n\r\n const db = await openSqlite(dbPath);\r\n if (db === null) return []; // missing / locked / unreadable / non-sqlite → fail-open\r\n\r\n try {\r\n const rootSessions = loadRootSessions(db);\r\n if (rootSessions.length === 0) return [];\r\n\r\n const assistantBuckets = loadAssistantBuckets(db);\r\n const records: UsageRecord[] = [];\r\n\r\n for (const session of rootSessions) {\r\n const sessionKey = `${dbPath}:${session.id}`;\r\n const dayBuckets = assistantBuckets.get(session.id);\r\n\r\n if (dayBuckets !== undefined && dayBuckets.length > 0) {\r\n const totalAssistantMessages = dayBuckets.reduce(\r\n (sum, bucket) => sum + bucket.messageCount,\r\n 0,\r\n );\r\n const safeCost = Math.max(0, session.cost);\r\n let allocatedCost = 0;\r\n\r\n for (let index = 0; index < dayBuckets.length; index++) {\r\n const bucket = dayBuckets[index] as DayBucket;\r\n // Last bucket absorbs the remainder so per-day costs sum to the total.\r\n const bucketCost =\r\n index + 1 === dayBuckets.length\r\n ? Math.max(0, safeCost - allocatedCost)\r\n : totalAssistantMessages > 0\r\n ? (safeCost * bucket.messageCount) / totalAssistantMessages\r\n : 0;\r\n allocatedCost += bucketCost;\r\n\r\n const tokens: TokenBreakdown = emptyTokens(); // tokens stay all-zero\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: CRUSH_MODEL_ID,\r\n providerId: CRUSH_PROVIDER_ID,\r\n sessionId: sessionKey,\r\n tokens,\r\n cost: bucketCost,\r\n ts: bucket.timestampMs,\r\n messageCount: Math.max(0, bucket.messageCount),\r\n confidence: \"host-estimated\",\r\n };\r\n if (sinceMs === undefined || record.ts >= sinceMs) records.push(record);\r\n }\r\n\r\n continue;\r\n }\r\n\r\n // No assistant messages: emit a single cost-only record (when costed).\r\n if (session.cost <= 0) continue;\r\n\r\n const ts = fallbackSessionTimestampMs(session.updatedAt, session.createdAt);\r\n if (ts === null) continue;\r\n\r\n const record: UsageRecord = {\r\n platformId: PLATFORM_ID,\r\n modelId: CRUSH_MODEL_ID,\r\n providerId: CRUSH_PROVIDER_ID,\r\n sessionId: sessionKey,\r\n tokens: emptyTokens(),\r\n cost: Math.max(0, session.cost),\r\n ts,\r\n messageCount: 0,\r\n confidence: \"host-estimated\",\r\n };\r\n if (sinceMs === undefined || record.ts >= sinceMs) records.push(record);\r\n }\r\n\r\n // Sort by timestamp asc, then session id (port of the Rust final sort).\r\n records.sort((a, b) => a.ts - b.ts || a.sessionId.localeCompare(b.sessionId));\r\n return records;\r\n } finally {\r\n db.close();\r\n }\r\n },\r\n};\r\n\r\nexport default crushReader;\r\n"],"mappings":"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","names":["record"]}