@ken-jo/agent-connector 0.4.94 → 0.4.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -202
- package/NOTICE +6 -6
- package/README.md +605 -485
- package/dist/{action-VW5SACXY.js → action-IPFDPOZU.js} +16 -16
- package/dist/action-IPFDPOZU.js.map +1 -0
- package/dist/{amazon-q-ETMAGY3D.js → amazon-q-IT44LEWV.js} +6 -6
- package/dist/amazon-q-IT44LEWV.js.map +1 -0
- package/dist/{amp-BQZOL5GR.js → amp-CEWOSWQO.js} +4 -4
- package/dist/amp-CEWOSWQO.js.map +1 -0
- package/dist/{amp-67BZONGF.js → amp-P35FRU2Z.js} +7 -7
- package/dist/amp-P35FRU2Z.js.map +1 -0
- package/dist/{antigravity-EA4L225L.js → antigravity-LHFEFNMC.js} +5 -5
- package/dist/antigravity-LHFEFNMC.js.map +1 -0
- package/dist/antigravity-TWIWXTQH.js +18 -0
- package/dist/{antigravity-cli-B2HXZFYQ.js → antigravity-cli-MOKFSWOU.js} +5 -5
- package/dist/antigravity-cli-MOKFSWOU.js.map +1 -0
- package/dist/{antigravity-cli-OM6SC3PE.js → antigravity-cli-MPE27LPI.js} +9 -9
- package/dist/antigravity-cli-MPE27LPI.js.map +1 -0
- package/dist/audit-3E5JAOP2.js +311 -0
- package/dist/audit-3E5JAOP2.js.map +1 -0
- package/dist/{chunk-KSPMSRVU.js → chunk-3EP5V2F4.js} +3 -3
- package/dist/chunk-3EP5V2F4.js.map +1 -0
- package/dist/{chunk-MGTDQLJJ.js → chunk-3FINQ2ZF.js} +4 -4
- package/dist/chunk-3FINQ2ZF.js.map +1 -0
- package/dist/{chunk-KHO5WNTP.js → chunk-4GH2KA7V.js} +1 -1
- package/dist/chunk-4GH2KA7V.js.map +1 -0
- package/dist/{chunk-2Y2MZRLP.js → chunk-7C2IPIIB.js} +3 -3
- package/dist/chunk-7C2IPIIB.js.map +1 -0
- package/dist/{chunk-NLE2J5P4.js → chunk-7HHW23AC.js} +29 -29
- package/dist/chunk-7HHW23AC.js.map +1 -0
- package/dist/{chunk-XD7MY6HB.js → chunk-7PSNXUFK.js} +1 -1
- package/dist/chunk-7PSNXUFK.js.map +1 -0
- package/dist/{chunk-KJLFGQQM.js → chunk-7ZFK2WEO.js} +8 -6
- package/dist/chunk-7ZFK2WEO.js.map +1 -0
- package/dist/{chunk-XC5SJ35F.js → chunk-C63AAXK7.js} +5 -5
- package/dist/chunk-C63AAXK7.js.map +1 -0
- package/dist/{chunk-UM5S7DKK.js → chunk-C747DYNT.js} +1 -1
- package/dist/chunk-C747DYNT.js.map +1 -0
- package/dist/{chunk-EHP7WISI.js → chunk-FHYR5Q5T.js} +19 -14
- package/dist/chunk-FHYR5Q5T.js.map +1 -0
- package/dist/{chunk-I5TO42QC.js → chunk-GCPEPUD7.js} +8 -8
- package/dist/chunk-GCPEPUD7.js.map +1 -0
- package/dist/{chunk-ZMAJ2XFD.js → chunk-GZZCHJDP.js} +2 -2
- package/dist/chunk-GZZCHJDP.js.map +1 -0
- package/dist/{chunk-UMQ6NBQR.js → chunk-ILWHLGIC.js} +2 -2
- package/dist/chunk-ILWHLGIC.js.map +1 -0
- package/dist/{chunk-IVAWP6HH.js → chunk-IOJDZJ72.js} +2 -2
- package/dist/chunk-IOJDZJ72.js.map +1 -0
- package/dist/{chunk-NBYKLUF6.js → chunk-KFPSJRHR.js} +2 -2
- package/dist/chunk-KFPSJRHR.js.map +1 -0
- package/dist/{chunk-O7SDRBVB.js → chunk-KM2YXO6V.js} +1 -1
- package/dist/chunk-KM2YXO6V.js.map +1 -0
- package/dist/{chunk-P23RVIUY.js → chunk-L2C5UMKW.js} +4 -4
- package/dist/chunk-L2C5UMKW.js.map +1 -0
- package/dist/{chunk-OP4KBXUO.js → chunk-LFWYYWLV.js} +1 -1
- package/dist/chunk-LFWYYWLV.js.map +1 -0
- package/dist/{chunk-B5W3RHTI.js → chunk-LQ55E7QI.js} +7 -7
- package/dist/chunk-LQ55E7QI.js.map +1 -0
- package/dist/{chunk-TTKSYJRY.js → chunk-MLU7NDAZ.js} +12 -12
- package/dist/chunk-MLU7NDAZ.js.map +1 -0
- package/dist/{chunk-TQRLMA2C.js → chunk-N4ENLZEU.js} +22 -19
- package/dist/chunk-N4ENLZEU.js.map +1 -0
- package/dist/{chunk-SILKSP2Y.js → chunk-NDMNMAKP.js} +43 -43
- package/dist/chunk-NDMNMAKP.js.map +1 -0
- package/dist/{chunk-H53QZQJI.js → chunk-PSGTHJQU.js} +1 -1
- package/dist/chunk-PSGTHJQU.js.map +1 -0
- package/dist/{chunk-FBJQGRET.js → chunk-Q4ISQHUQ.js} +1 -1
- package/dist/chunk-Q4ISQHUQ.js.map +1 -0
- package/dist/{chunk-6TF7MHG5.js → chunk-Q7PR2WGT.js} +2 -2
- package/dist/chunk-Q7PR2WGT.js.map +1 -0
- package/dist/{chunk-GZGLCCEE.js → chunk-QYOUQCC6.js} +1 -1
- package/dist/chunk-QYOUQCC6.js.map +1 -0
- package/dist/{chunk-Z2MRSS2I.js → chunk-SLJI5BOM.js} +2 -2
- package/dist/chunk-SLJI5BOM.js.map +1 -0
- package/dist/{chunk-JKKU2WKG.js → chunk-TGZFDXNH.js} +4 -4
- package/dist/chunk-TGZFDXNH.js.map +1 -0
- package/dist/{chunk-FJCUKXGX.js → chunk-THI4PI4H.js} +2 -2
- package/dist/chunk-THI4PI4H.js.map +1 -0
- package/dist/{chunk-MQPNU2CY.js → chunk-UZZTWWGG.js} +1 -1
- package/dist/chunk-UZZTWWGG.js.map +1 -0
- package/dist/{chunk-2RIFORQS.js → chunk-WPTNMYYM.js} +2 -2
- package/dist/chunk-WPTNMYYM.js.map +1 -0
- package/dist/{chunk-OPXAP3AC.js → chunk-XN7S44U4.js} +1 -1
- package/dist/chunk-XN7S44U4.js.map +1 -0
- package/dist/{chunk-6WZODR3Z.js → chunk-XVEJAFKW.js} +5 -5
- package/dist/chunk-XVEJAFKW.js.map +1 -0
- package/dist/{chunk-BEFVJ2OE.js → chunk-XXLCMSD3.js} +1 -1
- package/dist/chunk-XXLCMSD3.js.map +1 -0
- package/dist/{chunk-YT43MUSJ.js → chunk-XZWMJLIT.js} +1 -1
- package/dist/chunk-XZWMJLIT.js.map +1 -0
- package/dist/{chunk-4NHXLXSD.js → chunk-Y3K6KJMS.js} +2 -2
- package/dist/chunk-Y3K6KJMS.js.map +1 -0
- package/dist/{chunk-RK6VBLXP.js → chunk-ZSOS2KOR.js} +1 -1
- package/dist/chunk-ZSOS2KOR.js.map +1 -0
- package/dist/{claude-code-NMRKTAP7.js → claude-code-FS42PXZO.js} +4 -4
- package/dist/claude-code-FS42PXZO.js.map +1 -0
- package/dist/{claude-code-DUZNOAS2.js → claude-code-QBWDT5RT.js} +11 -11
- package/dist/claude-code-QBWDT5RT.js.map +1 -0
- package/dist/cli/sdk.js +9 -8
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{cline-25KW25IN.js → cline-JKEAYWVR.js} +6 -6
- package/dist/cline-JKEAYWVR.js.map +1 -0
- package/dist/{codebuddy-ZNYQACMF.js → codebuddy-EB6EYPC3.js} +7 -7
- package/dist/codebuddy-EB6EYPC3.js.map +1 -0
- package/dist/{codebuff-VR6JUDIY.js → codebuff-5RSXFU6G.js} +6 -6
- package/dist/codebuff-5RSXFU6G.js.map +1 -0
- package/dist/{codebuff-ETYT5UBL.js → codebuff-6KWBW6CT.js} +4 -4
- package/dist/codebuff-6KWBW6CT.js.map +1 -0
- package/dist/{codex-CPNKTXG3.js → codex-ART46CDD.js} +9 -9
- package/dist/codex-ART46CDD.js.map +1 -0
- package/dist/{codex-YAORPCOD.js → codex-DKBAAUCO.js} +4 -4
- package/dist/codex-DKBAAUCO.js.map +1 -0
- package/dist/{continue-WU2IB5KS.js → continue-JHZCMSPH.js} +8 -8
- package/dist/continue-JHZCMSPH.js.map +1 -0
- package/dist/{copilot-cli-MZTAWMPW.js → copilot-cli-CLBJACQ5.js} +4 -4
- package/dist/copilot-cli-CLBJACQ5.js.map +1 -0
- package/dist/{copilot-cli-PAP377OM.js → copilot-cli-QVAU5CFM.js} +7 -7
- package/dist/copilot-cli-QVAU5CFM.js.map +1 -0
- package/dist/{crush-3AVQA4YU.js → crush-7TORBYUL.js} +7 -7
- package/dist/crush-7TORBYUL.js.map +1 -0
- package/dist/{crush-QBXLNN7T.js → crush-A5C5USRL.js} +4 -4
- package/dist/crush-A5C5USRL.js.map +1 -0
- package/dist/{cursor-DIW43TRZ.js → cursor-D5SWDVSW.js} +3 -3
- package/dist/cursor-D5SWDVSW.js.map +1 -0
- package/dist/cursor-W7I4QUFO.js +22 -0
- package/dist/{define-connector-LQFJL_at.d.ts → define-connector-DTWb46UB.d.ts} +1 -1
- package/dist/{detect-P5OA5UYM.js → detect-JHSA3G3U.js} +4 -4
- package/dist/detect-JHSA3G3U.js.map +1 -0
- package/dist/{devin-QMEM3OFA.js → devin-SSR7S4WX.js} +7 -7
- package/dist/devin-SSR7S4WX.js.map +1 -0
- package/dist/{doctor-2O2Q6MIB.js → doctor-CQJ7TZPA.js} +26 -26
- package/dist/doctor-CQJ7TZPA.js.map +1 -0
- package/dist/{droid-MOKV53J4.js → droid-45PJ7JGF.js} +7 -7
- package/dist/droid-45PJ7JGF.js.map +1 -0
- package/dist/{droid-U3ZBLQFO.js → droid-PFQ2PZSF.js} +4 -4
- package/dist/droid-PFQ2PZSF.js.map +1 -0
- package/dist/{gemini-cli-47ZHCOZR.js → gemini-cli-24PFDK7D.js} +8 -8
- package/dist/gemini-cli-24PFDK7D.js.map +1 -0
- package/dist/{gemini-cli-NMVVBO3K.js → gemini-cli-Y6RJMU24.js} +4 -4
- package/dist/gemini-cli-Y6RJMU24.js.map +1 -0
- package/dist/{goose-64V46Y2W.js → goose-K2HKUTBK.js} +4 -4
- package/dist/goose-K2HKUTBK.js.map +1 -0
- package/dist/{goose-4EI5MYTI.js → goose-U5D3LCPC.js} +9 -9
- package/dist/goose-U5D3LCPC.js.map +1 -0
- package/dist/{grok-cli-QYQPDSVB.js → grok-cli-L2MHAPBY.js} +7 -7
- package/dist/grok-cli-L2MHAPBY.js.map +1 -0
- package/dist/{hermes-F75OSK3Y.js → hermes-2S5JUTTJ.js} +7 -7
- package/dist/hermes-2S5JUTTJ.js.map +1 -0
- package/dist/{hermes-F3EGTK6N.js → hermes-MF3NX4ZY.js} +4 -4
- package/dist/hermes-MF3NX4ZY.js.map +1 -0
- package/dist/{hook-4N3BMKIK.js → hook-VHICXHXL.js} +16 -16
- package/dist/hook-VHICXHXL.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/{install-NPN77N3S.js → install-WLENR4KF.js} +192 -28
- package/dist/install-WLENR4KF.js.map +1 -0
- package/dist/{introspect-D4fEGuhN.d.ts → introspect-dNekqPDY.d.ts} +1 -1
- package/dist/{jetbrains-copilot-ZGIWI6YV.js → jetbrains-copilot-Z4FC3RM3.js} +6 -6
- package/dist/jetbrains-copilot-Z4FC3RM3.js.map +1 -0
- package/dist/{junie-DIFN2P22.js → junie-ULGJJIHB.js} +6 -6
- package/dist/junie-ULGJJIHB.js.map +1 -0
- package/dist/{kilo-SW7BH6EG.js → kilo-SMHPOHJ5.js} +4 -4
- package/dist/kilo-SMHPOHJ5.js.map +1 -0
- package/dist/{kilo-EMZJBEBB.js → kilo-WWIJOVWD.js} +8 -8
- package/dist/kilo-WWIJOVWD.js.map +1 -0
- package/dist/{kilo-cli-S3GXBPAE.js → kilo-cli-QKE2EQLS.js} +8 -8
- package/dist/kilo-cli-QKE2EQLS.js.map +1 -0
- package/dist/{kilo-cli-ZF272ZHN.js → kilo-cli-T2SRRRJS.js} +5 -5
- package/dist/kilo-cli-T2SRRRJS.js.map +1 -0
- package/dist/{kimi-IQJOIY6B.js → kimi-JMUFTVSF.js} +7 -7
- package/dist/kimi-JMUFTVSF.js.map +1 -0
- package/dist/{kimi-5FHEXGZD.js → kimi-MSIDEMCI.js} +4 -4
- package/dist/kimi-MSIDEMCI.js.map +1 -0
- package/dist/{kiro-WGOI5CGT.js → kiro-NYRRBIKW.js} +4 -4
- package/dist/kiro-NYRRBIKW.js.map +1 -0
- package/dist/{kiro-BYX4HEAJ.js → kiro-UWUCRKR3.js} +7 -7
- package/dist/kiro-UWUCRKR3.js.map +1 -0
- package/dist/{leaderboard-QKB36OBR.js → leaderboard-NJWKGMXP.js} +7 -7
- package/dist/leaderboard-NJWKGMXP.js.map +1 -0
- package/dist/{mimo-code-7R53QDHB.js → mimo-code-BPNYYIVM.js} +7 -7
- package/dist/mimo-code-BPNYYIVM.js.map +1 -0
- package/dist/{mistral-vibe-LHJ55FSB.js → mistral-vibe-LT3DAJ3L.js} +6 -6
- package/dist/mistral-vibe-LT3DAJ3L.js.map +1 -0
- package/dist/{mux-XM6AP35L.js → mux-7PIJ7HUQ.js} +4 -4
- package/dist/mux-7PIJ7HUQ.js.map +1 -0
- package/dist/{mux-NYCNGL43.js → mux-AHNUAY5Z.js} +6 -6
- package/dist/mux-AHNUAY5Z.js.map +1 -0
- package/dist/{nemoclaw-WSIPSE7U.js → nemoclaw-RE5RZASZ.js} +7 -7
- package/dist/nemoclaw-RE5RZASZ.js.map +1 -0
- package/dist/{omp-562NK5BJ.js → omp-D2EXEPH7.js} +7 -7
- package/dist/omp-D2EXEPH7.js.map +1 -0
- package/dist/{open-interpreter-I2CNOEXY.js → open-interpreter-SSGBEUKX.js} +6 -6
- package/dist/open-interpreter-SSGBEUKX.js.map +1 -0
- package/dist/openclaw-IJ33K45M.js +17 -0
- package/dist/{openclaw-5EQFGOWL.js → openclaw-KAFTNLR2.js} +4 -4
- package/dist/openclaw-KAFTNLR2.js.map +1 -0
- package/dist/{opencode-YS4WX2E2.js → opencode-YKUKZY3J.js} +7 -7
- package/dist/opencode-YKUKZY3J.js.map +1 -0
- package/dist/{opencode-QGP4ALQY.js → opencode-Z65JYILL.js} +4 -4
- package/dist/opencode-Z65JYILL.js.map +1 -0
- package/dist/{openhands-QMYIKNOS.js → openhands-SEZMCUC3.js} +7 -7
- package/dist/openhands-SEZMCUC3.js.map +1 -0
- package/dist/{package-Q6DV5247.js → package-JECVQEFT.js} +15 -15
- package/dist/package-JECVQEFT.js.map +1 -0
- package/dist/{pi-GGDQREVG.js → pi-DLKCPOJE.js} +4 -4
- package/dist/pi-DLKCPOJE.js.map +1 -0
- package/dist/{pi-644FIN3A.js → pi-YPYV2ULH.js} +6 -6
- package/dist/pi-YPYV2ULH.js.map +1 -0
- package/dist/{qwen-code-GQILBJ5B.js → qwen-code-26CSFDX6.js} +4 -4
- package/dist/qwen-code-26CSFDX6.js.map +1 -0
- package/dist/{qwen-code-STDMX7EM.js → qwen-code-ZHCLCYMC.js} +9 -9
- package/dist/qwen-code-ZHCLCYMC.js.map +1 -0
- package/dist/{roo-code-DLPIHHQ6.js → roo-code-7F4HYB34.js} +6 -6
- package/dist/roo-code-7F4HYB34.js.map +1 -0
- package/dist/{roo-code-SUWI4BBN.js → roo-code-HSAE2CJU.js} +4 -4
- package/dist/roo-code-HSAE2CJU.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +14 -14
- package/dist/sdk/index.d.ts +4 -4
- package/dist/sdk/index.js +6 -6
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/test.d.ts +2 -2
- package/dist/sdk/test.js +11 -11
- package/dist/{serve-MJTTXDX4.js → serve-LYSBH43G.js} +16 -16
- package/dist/serve-LYSBH43G.js.map +1 -0
- package/dist/{status-Q3UY4NZU.js → status-PHKKGEW5.js} +11 -11
- package/dist/status-PHKKGEW5.js.map +1 -0
- package/dist/{statusline-QF4G724E.js → statusline-ZDD4OZ4F.js} +15 -15
- package/dist/statusline-ZDD4OZ4F.js.map +1 -0
- package/dist/{synthetic-6FRA7P5Q.js → synthetic-LUT6V6SD.js} +4 -4
- package/dist/synthetic-LUT6V6SD.js.map +1 -0
- package/dist/{telemetry-JHVRDDNK.js → telemetry-HDGMI2BK.js} +10 -10
- package/dist/telemetry-HDGMI2BK.js.map +1 -0
- package/dist/{trae-CPPYASR2.js → trae-SIQI3C66.js} +4 -4
- package/dist/trae-SIQI3C66.js.map +1 -0
- package/dist/{trae-UOYD6JZZ.js → trae-Y67HSDOW.js} +6 -6
- package/dist/trae-Y67HSDOW.js.map +1 -0
- package/dist/{types-BhcUEIJJ.d.ts → types-JyhpJHJa.d.ts} +4 -2
- package/dist/{uninstall-H2SOV3AP.js → uninstall-RWO2GM2I.js} +21 -21
- package/dist/uninstall-RWO2GM2I.js.map +1 -0
- package/dist/{upgrade-UCSG7L5U.js → upgrade-YJCXNUEE.js} +21 -21
- package/dist/upgrade-YJCXNUEE.js.map +1 -0
- package/dist/{usage-4EGIJ4TM.js → usage-IWO4RQN6.js} +4 -4
- package/dist/usage-IWO4RQN6.js.map +1 -0
- package/dist/{usage-event-GRQE7XWT.js → usage-event-AFFKCLHE.js} +15 -15
- package/dist/usage-event-AFFKCLHE.js.map +1 -0
- package/dist/{vscode-copilot-NIMH2DGU.js → vscode-copilot-5CI5B3X6.js} +7 -7
- package/dist/vscode-copilot-5CI5B3X6.js.map +1 -0
- package/dist/{warp-NRBBJQR2.js → warp-5YBSP7FP.js} +4 -4
- package/dist/warp-5YBSP7FP.js.map +1 -0
- package/dist/{warp-QYJGKURM.js → warp-INAKVGBV.js} +6 -6
- package/dist/warp-INAKVGBV.js.map +1 -0
- package/dist/{windsurf-75LLZ37C.js → windsurf-4HM57QAY.js} +6 -6
- package/dist/windsurf-4HM57QAY.js.map +1 -0
- package/dist/{zed-XJFOOKCO.js → zed-XXCNYOIN.js} +4 -4
- package/dist/zed-XXCNYOIN.js.map +1 -0
- package/dist/{zed-HKAZXEJW.js → zed-YN5X37PQ.js} +7 -7
- package/dist/zed-YN5X37PQ.js.map +1 -0
- package/package.json +92 -88
- package/dist/action-VW5SACXY.js.map +0 -1
- package/dist/amazon-q-ETMAGY3D.js.map +0 -1
- package/dist/amp-67BZONGF.js.map +0 -1
- package/dist/amp-BQZOL5GR.js.map +0 -1
- package/dist/antigravity-EA4L225L.js.map +0 -1
- package/dist/antigravity-VXLDLTO7.js +0 -18
- package/dist/antigravity-cli-B2HXZFYQ.js.map +0 -1
- package/dist/antigravity-cli-OM6SC3PE.js.map +0 -1
- package/dist/chunk-2RIFORQS.js.map +0 -1
- package/dist/chunk-2Y2MZRLP.js.map +0 -1
- package/dist/chunk-4NHXLXSD.js.map +0 -1
- package/dist/chunk-6TF7MHG5.js.map +0 -1
- package/dist/chunk-6WZODR3Z.js.map +0 -1
- package/dist/chunk-B5W3RHTI.js.map +0 -1
- package/dist/chunk-BEFVJ2OE.js.map +0 -1
- package/dist/chunk-EHP7WISI.js.map +0 -1
- package/dist/chunk-FBJQGRET.js.map +0 -1
- package/dist/chunk-FJCUKXGX.js.map +0 -1
- package/dist/chunk-GZGLCCEE.js.map +0 -1
- package/dist/chunk-H53QZQJI.js.map +0 -1
- package/dist/chunk-I5TO42QC.js.map +0 -1
- package/dist/chunk-IVAWP6HH.js.map +0 -1
- package/dist/chunk-JKKU2WKG.js.map +0 -1
- package/dist/chunk-KHO5WNTP.js.map +0 -1
- package/dist/chunk-KJLFGQQM.js.map +0 -1
- package/dist/chunk-KSPMSRVU.js.map +0 -1
- package/dist/chunk-MGTDQLJJ.js.map +0 -1
- package/dist/chunk-MQPNU2CY.js.map +0 -1
- package/dist/chunk-NBYKLUF6.js.map +0 -1
- package/dist/chunk-NLE2J5P4.js.map +0 -1
- package/dist/chunk-O7SDRBVB.js.map +0 -1
- package/dist/chunk-OP4KBXUO.js.map +0 -1
- package/dist/chunk-OPXAP3AC.js.map +0 -1
- package/dist/chunk-P23RVIUY.js.map +0 -1
- package/dist/chunk-RK6VBLXP.js.map +0 -1
- package/dist/chunk-SILKSP2Y.js.map +0 -1
- package/dist/chunk-TQRLMA2C.js.map +0 -1
- package/dist/chunk-TTKSYJRY.js.map +0 -1
- package/dist/chunk-UM5S7DKK.js.map +0 -1
- package/dist/chunk-UMQ6NBQR.js.map +0 -1
- package/dist/chunk-XC5SJ35F.js.map +0 -1
- package/dist/chunk-XD7MY6HB.js.map +0 -1
- package/dist/chunk-YT43MUSJ.js.map +0 -1
- package/dist/chunk-Z2MRSS2I.js.map +0 -1
- package/dist/chunk-ZMAJ2XFD.js.map +0 -1
- package/dist/claude-code-DUZNOAS2.js.map +0 -1
- package/dist/claude-code-NMRKTAP7.js.map +0 -1
- package/dist/cline-25KW25IN.js.map +0 -1
- package/dist/codebuddy-ZNYQACMF.js.map +0 -1
- package/dist/codebuff-ETYT5UBL.js.map +0 -1
- package/dist/codebuff-VR6JUDIY.js.map +0 -1
- package/dist/codex-CPNKTXG3.js.map +0 -1
- package/dist/codex-YAORPCOD.js.map +0 -1
- package/dist/continue-WU2IB5KS.js.map +0 -1
- package/dist/copilot-cli-MZTAWMPW.js.map +0 -1
- package/dist/copilot-cli-PAP377OM.js.map +0 -1
- package/dist/crush-3AVQA4YU.js.map +0 -1
- package/dist/crush-QBXLNN7T.js.map +0 -1
- package/dist/cursor-DIW43TRZ.js.map +0 -1
- package/dist/cursor-GSLUET3V.js +0 -22
- package/dist/detect-P5OA5UYM.js.map +0 -1
- package/dist/devin-QMEM3OFA.js.map +0 -1
- package/dist/doctor-2O2Q6MIB.js.map +0 -1
- package/dist/droid-MOKV53J4.js.map +0 -1
- package/dist/droid-U3ZBLQFO.js.map +0 -1
- package/dist/gemini-cli-47ZHCOZR.js.map +0 -1
- package/dist/gemini-cli-NMVVBO3K.js.map +0 -1
- package/dist/goose-4EI5MYTI.js.map +0 -1
- package/dist/goose-64V46Y2W.js.map +0 -1
- package/dist/grok-cli-QYQPDSVB.js.map +0 -1
- package/dist/hermes-F3EGTK6N.js.map +0 -1
- package/dist/hermes-F75OSK3Y.js.map +0 -1
- package/dist/hook-4N3BMKIK.js.map +0 -1
- package/dist/install-NPN77N3S.js.map +0 -1
- package/dist/jetbrains-copilot-ZGIWI6YV.js.map +0 -1
- package/dist/junie-DIFN2P22.js.map +0 -1
- package/dist/kilo-EMZJBEBB.js.map +0 -1
- package/dist/kilo-SW7BH6EG.js.map +0 -1
- package/dist/kilo-cli-S3GXBPAE.js.map +0 -1
- package/dist/kilo-cli-ZF272ZHN.js.map +0 -1
- package/dist/kimi-5FHEXGZD.js.map +0 -1
- package/dist/kimi-IQJOIY6B.js.map +0 -1
- package/dist/kiro-BYX4HEAJ.js.map +0 -1
- package/dist/kiro-WGOI5CGT.js.map +0 -1
- package/dist/leaderboard-QKB36OBR.js.map +0 -1
- package/dist/mimo-code-7R53QDHB.js.map +0 -1
- package/dist/mistral-vibe-LHJ55FSB.js.map +0 -1
- package/dist/mux-NYCNGL43.js.map +0 -1
- package/dist/mux-XM6AP35L.js.map +0 -1
- package/dist/nemoclaw-WSIPSE7U.js.map +0 -1
- package/dist/omp-562NK5BJ.js.map +0 -1
- package/dist/open-interpreter-I2CNOEXY.js.map +0 -1
- package/dist/openclaw-5EQFGOWL.js.map +0 -1
- package/dist/openclaw-7EYBQS4R.js +0 -17
- package/dist/opencode-QGP4ALQY.js.map +0 -1
- package/dist/opencode-YS4WX2E2.js.map +0 -1
- package/dist/openhands-QMYIKNOS.js.map +0 -1
- package/dist/package-Q6DV5247.js.map +0 -1
- package/dist/pi-644FIN3A.js.map +0 -1
- package/dist/pi-GGDQREVG.js.map +0 -1
- package/dist/qwen-code-GQILBJ5B.js.map +0 -1
- package/dist/qwen-code-STDMX7EM.js.map +0 -1
- package/dist/roo-code-DLPIHHQ6.js.map +0 -1
- package/dist/roo-code-SUWI4BBN.js.map +0 -1
- package/dist/serve-MJTTXDX4.js.map +0 -1
- package/dist/status-Q3UY4NZU.js.map +0 -1
- package/dist/statusline-QF4G724E.js.map +0 -1
- package/dist/synthetic-6FRA7P5Q.js.map +0 -1
- package/dist/telemetry-JHVRDDNK.js.map +0 -1
- package/dist/trae-CPPYASR2.js.map +0 -1
- package/dist/trae-UOYD6JZZ.js.map +0 -1
- package/dist/uninstall-H2SOV3AP.js.map +0 -1
- package/dist/upgrade-UCSG7L5U.js.map +0 -1
- package/dist/usage-4EGIJ4TM.js.map +0 -1
- package/dist/usage-event-GRQE7XWT.js.map +0 -1
- package/dist/vscode-copilot-NIMH2DGU.js.map +0 -1
- package/dist/warp-NRBBJQR2.js.map +0 -1
- package/dist/warp-QYJGKURM.js.map +0 -1
- package/dist/windsurf-75LLZ37C.js.map +0 -1
- package/dist/zed-HKAZXEJW.js.map +0 -1
- package/dist/zed-XJFOOKCO.js.map +0 -1
- /package/dist/{antigravity-VXLDLTO7.js.map → antigravity-TWIWXTQH.js.map} +0 -0
- /package/dist/{cursor-GSLUET3V.js.map → cursor-W7I4QUFO.js.map} +0 -0
- /package/dist/{openclaw-7EYBQS4R.js.map → openclaw-IJ33K45M.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/telemetry-accessor.ts","../src/telemetry/measure.ts","../src/runtime/hook-entrypoint.ts"],"sourcesContent":["/**\n * runtime/telemetry-accessor — build the handler-facing {@link TelemetryAccessor}.\n *\n * Every runtime entrypoint (hook / statusline / action) stamps `ctx.telemetry`\n * (or `evt.telemetry`) with the accessor built here so a connector handler can\n * read ITS OWN recorded usage on demand (`await ctx.telemetry?.()`), without the\n * handler reaching into the telemetry store directly.\n *\n * The accessor is LAZY (it opens the store only when called) and DEFENSIVE:\n * - AGENT_CONNECTOR_TELEMETRY=0 → resolves zeros without touching disk;\n * - any read error → resolves zeros (NEVER throws into a handler — the hook\n * runtime is fail-open, the statusline fail-safe, and even the action\n * surface must not have a usage read crash an otherwise-fine run).\n *\n * COST: each accessor call, or the first read of a lazy `ctx.usage`, reads the\n * connector's telemetry file in full (the NDJSON store has no since/limit bound)\n * and linear-scans it. Cheap for normal stores, but a handler on a HOT path\n * should call it only when it needs usage.\n */\n\nimport type { TelemetryAccessor, TelemetryUsageSummary } from \"../core/types.js\";\nimport { openStore } from \"../telemetry/store.js\";\n\n/** The empty summary returned when telemetry is off or a read fails. */\nconst ZERO: TelemetryUsageSummary = {\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n calls: 0,\n};\n\n/**\n * Synchronously sum every stored row's inputTokens/outputTokens for the\n * connector and count rows (`calls`). Honors the AGENT_CONNECTOR_TELEMETRY=0\n * kill switch (zeros, no disk touch) and swallows any error (zeros).\n */\nexport function readTelemetryUsageSummary(connectorId: string): TelemetryUsageSummary {\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return { ...ZERO };\n try {\n const store = openStore({});\n try {\n let inputTokens = 0;\n let outputTokens = 0;\n let calls = 0;\n // COERCE each addend to a finite number: query() tolerates malformed\n // LINES but does not validate field shapes, so a legacy/hand-edited row\n // with a missing (→ NaN) or string (→ concat) token field must not\n // poison the summary's numeric contract. A non-finite token reads as 0.\n for (const row of store.query({ connectorId })) {\n inputTokens += Number.isFinite(row.inputTokens) ? row.inputTokens : 0;\n outputTokens += Number.isFinite(row.outputTokens) ? row.outputTokens : 0;\n calls += 1;\n }\n return {\n inputTokens,\n outputTokens,\n totalTokens: inputTokens + outputTokens,\n calls,\n };\n } finally {\n store.close();\n }\n } catch {\n // Never throw into a handler — a usage read failure resolves to zeros.\n return { ...ZERO };\n }\n}\n\n/**\n * Build a {@link TelemetryAccessor} bound to `connectorId`. The returned async\n * function uses {@link readTelemetryUsageSummary}; it never throws.\n */\nexport function buildTelemetryAccessor(connectorId: string): TelemetryAccessor {\n return async (): Promise<TelemetryUsageSummary> => {\n return readTelemetryUsageSummary(connectorId);\n };\n}\n\n/**\n * Install a cached lazy `usage` property on a statusline context. This preserves\n * synchronous `ctx.usage` reads for render functions without forcing every\n * status refresh to scan the telemetry store when the render never uses usage.\n */\nexport function attachLazyTelemetryUsage(\n ctx: { usage?: TelemetryUsageSummary },\n connectorId: string,\n read: (connectorId: string) => TelemetryUsageSummary = readTelemetryUsageSummary,\n): void {\n let cached: TelemetryUsageSummary | undefined;\n let hasCached = false;\n\n Object.defineProperty(ctx, \"usage\", {\n configurable: true,\n enumerable: true,\n get() {\n if (!hasCached) {\n cached = read(connectorId);\n hasCached = true;\n }\n return cached;\n },\n set(value: TelemetryUsageSummary | undefined) {\n cached = value;\n hasCached = true;\n },\n });\n}\n","/**\n * telemetry/measure — pure measurement helpers over a single MCP tool call.\n *\n * The framework tokenizes its OWN bytes: the tool-call arguments going in, the\n * tool result coming out, and the fixed tool-definition schemas. These helpers\n * turn those into {@link TokenCount}s using the default {@link Tokenizer}.\n *\n * Honesty rules:\n * • Binary modalities (image/audio/resource blobs) are NOT tokenized — base64\n * would massively over-count. Each gets a flat per-modality estimate, and\n * because that estimate is itself an approximation the combined confidence\n * is pulled down to at least \"tokenizer-approx\".\n * • When combining several counts, the reported confidence is the WORST\n * (least-confident) of the inputs — never report \"exact\" for a mixed bag.\n *\n * Everything here is pure: no IO, no mutation of inputs.\n */\n\nimport { getTokenizer } from \"./tokenizer.js\";\nimport { worstConfidence } from \"./types.js\";\nimport type {\n ConfidenceSource,\n ModelFamily,\n TokenCount,\n Tokenizer,\n} from \"./types.js\";\n\n/** Per-call measurement: tokens in, tokens out, and combined confidence. */\nexport interface ToolCallMeasurement {\n inputTokens: number;\n outputTokens: number;\n source: ConfidenceSource;\n}\n\n/**\n * Flat token estimates for non-text content blocks. These mirror common host\n * accounting (an image tile is roughly ~85 tokens at low detail); they are\n * deliberately coarse — we never tokenize the underlying base64 payload.\n */\nconst MODALITY_ESTIMATE: Record<string, number> = {\n image: 85,\n audio: 85,\n resource: 85,\n};\n/** Fallback estimate for an unknown non-text block type. */\nconst UNKNOWN_BLOCK_ESTIMATE = 85;\n\n// Confidence ordering + the worst-of comparison live in ./types (the single\n// source of truth, imported as `worstConfidence` above). Re-exported here so\n// callers that already import `worstConfidence` from this module keep working\n// as a new ConfidenceSource value (e.g. tokenizer-calibrated) slots into the\n// shared ordering.\nexport { worstConfidence };\n\n/** Reduce a list of sources to the single worst (least-confident) one. */\nfunction combineSources(sources: readonly ConfidenceSource[]): ConfidenceSource {\n // Default to the most-confident floor so an empty list combines to the only\n // other source it is paired with; in practice callers always pass ≥1 source.\n let worst: ConfidenceSource = \"host-native\";\n for (const s of sources) worst = worstConfidence(worst, s);\n return worst;\n}\n\n// ── Structural shapes we read from a tool result (kept local + narrow) ───────\n\n/** A text content block: `{ type: \"text\", text: \"...\" }`. */\ninterface TextBlock {\n type: \"text\";\n text?: unknown;\n}\n/** Any content block; only text blocks expose a `text` field we tokenize. */\ninterface ContentBlock {\n type?: unknown;\n text?: unknown;\n}\n/** The portion of an MCP tool result we measure. */\ninterface ToolResultLike {\n content?: unknown;\n structuredContent?: unknown;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null;\n}\n\nfunction isTextBlock(block: ContentBlock): block is TextBlock {\n return block.type === \"text\";\n}\n\n/**\n * Measure one tool call.\n *\n * Input = canonical JSON of `args`.\n * Output = tokens over the TEXTUAL parts of `result`:\n * • If `result.content` is an array of blocks, text blocks are tokenized via\n * their `text` field and non-text blocks add a flat per-modality estimate\n * (no base64 tokenization).\n * • `result.structuredContent`, when present, is tokenized as canonical JSON.\n * • Anything else (string / arbitrary object) is tokenized as canonical JSON.\n *\n * The reported `source` is the worst of the input and output sources.\n */\nexport function measureToolCall(\n args: unknown,\n result: unknown,\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): ToolCallMeasurement {\n const input = tok.countValue(args, family);\n const output = measureResult(result, family, tok);\n return {\n inputTokens: input.tokens,\n outputTokens: output.tokens,\n source: worstConfidence(input.source, output.source),\n };\n}\n\n/** Tokenize the textual + structured parts of a tool result. */\nfunction measureResult(\n result: unknown,\n family: ModelFamily,\n tok: Tokenizer,\n): TokenCount {\n if (!isObject(result)) {\n // Plain string or scalar result — count its canonical form directly.\n return tok.countValue(result, family);\n }\n\n const res = result as ToolResultLike;\n const sources: ConfidenceSource[] = [];\n let tokens = 0;\n\n if (Array.isArray(res.content)) {\n for (const raw of res.content) {\n if (!isObject(raw)) continue;\n const block = raw as ContentBlock;\n if (isTextBlock(block)) {\n const text = typeof block.text === \"string\" ? block.text : \"\";\n const c = tok.count(text, family);\n tokens += c.tokens;\n sources.push(c.source);\n } else {\n // image / audio / resource / unknown → flat estimate, no base64 BPE.\n const key = typeof block.type === \"string\" ? block.type : \"\";\n tokens += MODALITY_ESTIMATE[key] ?? UNKNOWN_BLOCK_ESTIMATE;\n // A flat estimate is an approximation, never \"exact\".\n sources.push(\"tokenizer-approx\");\n }\n }\n } else if (res.content !== undefined) {\n // content present but not an array (e.g. a bare string) → count it whole.\n const c = tok.countValue(res.content, family);\n tokens += c.tokens;\n sources.push(c.source);\n }\n\n if (res.structuredContent !== undefined) {\n const c = tok.countValue(res.structuredContent, family);\n tokens += c.tokens;\n sources.push(c.source);\n }\n\n // Nothing measurable found → count the whole object as a fallback so we never\n // silently report zero for a non-empty result.\n if (sources.length === 0) {\n return tok.countValue(result, family);\n }\n\n return { tokens, source: combineSources(sources) };\n}\n\n/**\n * Measure the fixed tool-definition overhead: tokenize the JSON of the entire\n * `tools/list` array once. `tools` is the raw array of tool descriptors as the\n * server advertises them (shape is host/SDK-specific, so it is treated as\n * opaque JSON here).\n */\nexport function measureToolDefs(\n tools: readonly unknown[],\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): TokenCount {\n return tok.countValue(tools, family);\n}\n\n/**\n * Measure one RUNTIME hook dispatch (the new `hook` developer-axis surface).\n *\n * Input = canonical JSON of the inbound normalized event payload the handler\n * reads (the host-provided fields: toolInput / prompt / raw / etc.).\n * Output = canonical JSON of what the handler RETURNS that becomes context or a\n * decision the host consumes (additionalContext / updatedInput / reason\n * and the rest of the normalized {@link HookResponse}).\n *\n * Uses the SAME tokenizer + worst-of confidence combination as the proxy. Pure:\n * no IO, no mutation. The two values are tokenized whole (a hook payload has no\n * binary content blocks to special-case, unlike a tool result).\n */\nexport function measureHook(\n eventPayload: unknown,\n handlerReturn: unknown,\n family: ModelFamily,\n tok: Tokenizer = getTokenizer(),\n): ToolCallMeasurement {\n const input = tok.countValue(eventPayload, family);\n const output = tok.countValue(handlerReturn, family);\n return {\n inputTokens: input.tokens,\n outputTokens: output.tokens,\n source: worstConfidence(input.source, output.source),\n };\n}\n","/**\n * runtime/hook-entrypoint — the universal json-stdio hook dispatcher.\n *\n * Every json-stdio host points its hook config at the single stable home binary\n * (`<homeBin> hook <platformId> <event> --connector <id>`, built by\n * core/spawn.buildHomeBinHookCommand). The CLI reads stdin and calls\n * {@link runHook} with the parsed flags + the raw stdin string. This module:\n *\n * 1. Loads the registered connector (live handlers) and the host adapter.\n * 2. Parses the host's stdin payload into a normalized event via the adapter.\n * 3. Finds the connector's handler for the event, honoring the tool matcher.\n * 4. Runs the handler, normalizes its return, and formats the adapter's native\n * reply.\n *\n * Fail-open is the safety contract: ANY error degrades to exit 0 (\"allow\") so a\n * framework or handler bug can never wedge a host's tool call — EXCEPT when the\n * event is PreToolUse or PermissionRequest and the handler explicitly denied,\n * in which case the deny (a deliberate security decision) is preserved rather\n * than swallowed.\n */\n\nimport type {\n HookEventName,\n HookResponse,\n NativeHookDef,\n NativeHookEvent,\n PlatformId,\n ResolvedConnector,\n} from \"../core/types.js\";\nimport { log } from \"../core/logger.js\";\nimport {\n loadRegisteredConnector,\n readRegisteredMeta,\n} from \"../core/load-connector.js\";\nimport { loadAdapter, REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\nimport { buildTelemetryAccessor } from \"./telemetry-accessor.js\";\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\nimport { projectIdentity } from \"../core/paths.js\";\nimport { measureHook } from \"../telemetry/measure.js\";\nimport { openStore, newRecordId } from \"../telemetry/store.js\";\nimport { getTokenizer, inferModelFamily } from \"../telemetry/tokenizer.js\";\n\n/** Flags + stdin the CLI hands to {@link runHook}. */\nexport interface RunHookOptions {\n /** Host platform id from the hook command (`hook <platformId> ...`). */\n platformId: string;\n /** Canonical lifecycle event name from the hook command. */\n event: HookEventName;\n /** Connector id from `--connector <id>`. */\n connectorId: string;\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\n stdin: string;\n}\n\n/** Process-level result the CLI translates into exit code + stdout/stderr. */\nexport interface RunHookResult {\n exitCode: number;\n stdout?: string;\n stderr?: string;\n}\n\n/** A pass-through \"allow\" reply with exit 0 and no payload. */\nconst ALLOW: RunHookResult = { exitCode: 0 };\n\n/**\n * The string a connector matcher filters on, when the event carries one:\n * tool events (PreToolUse / PostToolUse / PermissionRequest /\n * PostToolUseFailure) expose `toolName`; subagent events expose `agentType`.\n * Returns undefined when the event has neither (no filtering) — including a\n * SubagentStop arriving WITHOUT agent_type, which some hosts fail to populate;\n * filtering out such an event would silently drop a real stop, so we run the\n * handler instead (fail-open).\n */\nexport function eventMatcherSubject(evt: NormalizedEvent): string | undefined {\n const name = (evt as { toolName?: unknown }).toolName;\n if (typeof name === \"string\") return name;\n const agentType = (evt as { agentType?: unknown }).agentType;\n if (typeof agentType === \"string\") return agentType;\n return undefined;\n}\n\n/**\n * Tolerantly parse the host's stdin payload. Empty / whitespace-only input is a\n * legitimate \"no payload\" signal from some hosts and resolves to `{}`. Malformed\n * JSON also degrades to `{}` rather than throwing — fail-open is the contract.\n */\nfunction parseStdin(stdin: string): unknown {\n const trimmed = stdin.trim();\n if (trimmed === \"\") return {};\n try {\n return JSON.parse(trimmed);\n } catch {\n return {};\n }\n}\n\n/**\n * Compile the connector's matcher (a regex string) for a tool event. A missing\n * or empty matcher matches everything (returns null → no filtering). An invalid\n * regex degrades to \"match everything\" so a typo never blocks tool calls.\n */\nexport function compileMatcher(matcher: string | undefined): RegExp | null {\n if (!matcher || matcher === \"\") return null;\n try {\n return new RegExp(matcher);\n } catch {\n return null;\n }\n}\n\n/**\n * Normalize a handler return into a concrete HookResponse. A void/non-object\n * return becomes `{}` — NO decision — which every adapter formats as\n * pass-through allow (`response.decision ?? \"allow\"`). Deliberately NOT\n * `{decision:\"allow\"}`: on PermissionRequest an explicit \"allow\" is an ACTIVE\n * grant that suppresses the host's permission dialog, and a handler that\n * returns nothing must fall through to the dialog, never silently grant.\n */\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\n if (value == null || typeof value !== \"object\") return {};\n return value;\n}\n\n/**\n * Resolve the host platform to stamp on a hook telemetry row. Mirrors the serve\n * `--host` plumbing: the install-target platform id is baked into the hook\n * command (`hook <platformId> <event> …`) so `opts.platformId` is authoritative,\n * but an explicit env override (AGENT_CONNECTOR_HOST) wins when it names a known\n * platform. Falls back to the event's adapter-stamped hostPlatform, then\n * \"unknown\" — never throws.\n */\nfunction resolveHookHostPlatform(\n platformId: string,\n evt: NormalizedEvent | NativeHookEvent,\n): PlatformId {\n const override = process.env.AGENT_CONNECTOR_HOST;\n if (\n override !== undefined &&\n REGISTERED_PLATFORM_IDS.has(override as PlatformId)\n ) {\n return override as PlatformId;\n }\n if (REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)) {\n return platformId as PlatformId;\n }\n const fromEvent = (evt as { hostPlatform?: unknown }).hostPlatform;\n if (\n typeof fromEvent === \"string\" &&\n REGISTERED_PLATFORM_IDS.has(fromEvent as PlatformId)\n ) {\n return fromEvent as PlatformId;\n }\n return \"unknown\";\n}\n\n/**\n * Record one RUNTIME hook dispatch as a `hook` developer-axis telemetry row.\n *\n * input = the inbound event payload the handler reads (normalized event, or\n * the {@link NativeHookEvent} envelope for native passthrough hooks);\n * output = what the handler returned (a normalized {@link HookResponse}, or the\n * verbatim native reply object). Tokenized with the SAME tokenizer the\n * proxy uses. For a native hook `opts.event` is the host-native name.\n *\n * MUST be fail-open (the hook runtime is fail-open by contract): any error here\n * is swallowed so a measurement bug can NEVER break a host's hook. Honors the\n * AGENT_CONNECTOR_TELEMETRY=0 kill switch (skips entirely; the store's append is\n * already a no-op under it, but we also skip the tokenize work).\n */\nfunction recordHookTelemetry(\n opts: { platformId: string; event: string; connectorId: string },\n connector: ResolvedConnector,\n evt: NormalizedEvent | NativeHookEvent,\n response: unknown,\n): void {\n if (process.env.AGENT_CONNECTOR_TELEMETRY === \"0\") return;\n try {\n const family = inferModelFamily(\"\", connector.telemetry.modelFamilyHint);\n const measurement = measureHook(evt, response, family, getTokenizer());\n\n const sessionId =\n typeof (evt as { sessionId?: unknown }).sessionId === \"string\"\n ? ((evt as { sessionId: string }).sessionId)\n : \"\";\n const evtProjectDir = (evt as { projectDir?: unknown }).projectDir;\n const projectDir =\n typeof evtProjectDir === \"string\" && evtProjectDir !== \"\"\n ? evtProjectDir\n : process.cwd();\n const id = projectIdentity(projectDir);\n\n const store = openStore({});\n try {\n store.append({\n id: newRecordId(0),\n ts: Date.now(),\n connectorId: opts.connectorId,\n toolName: opts.event, // for a hook row the per-item name IS the event\n scope: \"hook\",\n surfaceKind: \"hook\",\n hostPlatform: resolveHookHostPlatform(opts.platformId, evt),\n sessionId,\n projectKey: id.key,\n projectDir: id.dir,\n inputTokens: measurement.inputTokens,\n outputTokens: measurement.outputTokens,\n confidenceSource: measurement.source,\n isError: false,\n });\n } finally {\n store.close();\n }\n } catch {\n // Fail-open: a telemetry error must NEVER break the hook runtime.\n }\n}\n\n/**\n * Dispatch one host hook invocation. Always resolves (never rejects): every\n * failure path returns a concrete {@link RunHookResult} so the CLI can exit\n * cleanly. See the module header for the fail-open contract.\n */\nexport async function runHook(opts: RunHookOptions): Promise<RunHookResult> {\n const { platformId, event, connectorId, stdin } = opts;\n\n try {\n const connector = await loadRegisteredConnector(connectorId);\n const adapter = await loadAdapter(platformId);\n\n // mcp-only hosts (and any adapter without runtime dispatch) cannot parse a\n // hook payload — nothing to do, allow.\n if (!adapter || !adapter.parseEvent || !adapter.formatReply) {\n return ALLOW;\n }\n\n const raw = parseStdin(stdin);\n const evt = adapter.parseEvent(event, raw);\n // The hook command carries the authoritative connector id; stamp it on the\n // event so handlers see the connector they were dispatched for.\n evt.connectorId = connectorId;\n // Unify the hook event with HostCtx: the adapter's parseEvent has no\n // capabilities/scope/telemetry at parse time (they are runtime-only), so the\n // runtime ALWAYS backfills them before the handler runs. capabilities comes\n // from the adapter; scope is recovered from the registered metadata (sync);\n // telemetry is the lazy per-connector usage accessor. The SAME three\n // assignments run on the error path (failOpenOrPreserveDeny) for symmetry.\n evt.capabilities = adapter.capabilities;\n evt.scope = readRegisteredMeta(connectorId)?.scope;\n evt.telemetry = buildTelemetryAccessor(connectorId);\n\n const definition = connector.hooks[event];\n if (!definition || typeof definition.handler !== \"function\") {\n // The connector declares no handler for this event — allow.\n return ALLOW;\n }\n\n // Per-host handler override: when dispatching for host X, `hosts[X].handler`\n // wins over the top-level handler; a host not listed (or a per-host entry\n // that is somehow not a function) falls back to the top-level handler.\n // Selection NEVER throws — fail-open is preserved.\n // NOTE: keyed on the RAW install-baked platformId (the host this entrypoint\n // was registered for), NOT resolveHookHostPlatform's AGENT_CONNECTOR_HOST\n // override — that override only re-labels the telemetry row. The error path\n // (failOpenOrPreserveDeny) resolves the IDENTICAL handler the same way, so a\n // per-host deny is preserved consistently.\n const perHost = definition.hosts?.[platformId as PlatformId]?.handler;\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\n\n // Matcher: if set and the event's subject (tool name, or agent type for\n // Subagent* events) does not match, allow (the hook is simply not\n // interested in this tool/agent). The matcher is the definition's — a\n // per-host handler shares the top-level matcher.\n const subject = eventMatcherSubject(evt);\n if (subject !== undefined) {\n const re = compileMatcher(definition.matcher);\n if (re && !re.test(subject)) return ALLOW;\n }\n\n // Run the resolved handler. Its return is normalized (void → allow) and\n // handed to the adapter to render the host-native reply.\n const handlerResult = await handler(\n // The adapter parsed `evt` as exactly this event's payload type; the\n // HooksConfig handler for the same event expects that payload.\n evt as never,\n );\n const response = normalizeResponse(handlerResult);\n\n // Developer-axis telemetry for the RUNTIME `hook` surface. Fail-open: never\n // lets a measurement error break the hook (handled inside the helper).\n recordHookTelemetry(opts, connector, evt, response);\n\n const reply = adapter.formatReply(event, response);\n return {\n exitCode: reply.exitCode,\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\n ...(reply.stderr !== undefined ? { stderr: reply.stderr } : {}),\n };\n } catch (err) {\n // Fail-open: a framework/handler error must not wedge the host's tool call.\n // The ONE exception is a PreToolUse / PermissionRequest deny — a deliberate\n // block we honor even if a later step threw. We re-derive that decision\n // defensively from the adapter so a throw AFTER the deny still surfaces it.\n const message = err instanceof Error ? err.message : String(err);\n log.error(`hook ${platformId}/${event} (${connectorId}) failed:`, message);\n return failOpenOrPreserveDeny(opts, message);\n }\n}\n\n/**\n * Events whose explicit deny survives the fail-open error path. Both are\n * permission gates — a deny there is a deliberate security decision that must\n * never be silently downgraded to allow. (Stop-class denies are persistence\n * conveniences, not security boundaries, so they stay fail-open.)\n */\nconst DENY_PRESERVE_EVENTS: ReadonlySet<HookEventName> = new Set([\n \"PreToolUse\",\n \"PermissionRequest\",\n]);\n\n/**\n * Error path. Returns fail-open allow, UNLESS this is a deny-preserving event\n * (PreToolUse / PermissionRequest) whose handler explicitly denied — in which\n * case the deny is reconstructed and preserved (a security decision is never\n * silently downgraded to allow).\n *\n * Best-effort: if anything in the deny reconstruction itself throws, we fall\n * back to allow (we never escalate an error into a spurious block).\n */\nasync function failOpenOrPreserveDeny(\n opts: RunHookOptions,\n errorMessage: string,\n): Promise<RunHookResult> {\n if (!DENY_PRESERVE_EVENTS.has(opts.event)) return ALLOW;\n try {\n const connector = await loadRegisteredConnector(opts.connectorId);\n const adapter = await loadAdapter(opts.platformId);\n if (!adapter?.parseEvent || !adapter.formatReply) return ALLOW;\n\n const definition = connector.hooks[opts.event];\n if (!definition || typeof definition.handler !== \"function\") return ALLOW;\n\n // Reconstruct the deny from the SAME handler the main path would have run —\n // the per-host override if one is declared for this host, else the top-level\n // handler. Otherwise a per-host deny could be silently downgraded on the\n // error path.\n const perHost = definition.hosts?.[opts.platformId as PlatformId]?.handler;\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\n\n const raw = parseStdin(opts.stdin);\n const evt = adapter.parseEvent(opts.event, raw);\n evt.connectorId = opts.connectorId;\n // Backfill the HostCtx fields here too (symmetry with the main path), so a\n // per-host deny reconstructed on the error path still carries capabilities/\n // scope/telemetry — a handler that branches on them denies identically.\n evt.capabilities = adapter.capabilities;\n evt.scope = readRegisteredMeta(opts.connectorId)?.scope;\n evt.telemetry = buildTelemetryAccessor(opts.connectorId);\n\n const subject = eventMatcherSubject(evt);\n if (subject !== undefined) {\n const re = compileMatcher(definition.matcher);\n if (re && !re.test(subject)) return ALLOW;\n }\n\n const response = normalizeResponse(await handler(evt as never));\n if (response.decision !== \"deny\") return ALLOW;\n\n const reply = adapter.formatReply(opts.event, response);\n return {\n exitCode: reply.exitCode,\n ...(reply.stdout !== undefined ? { stdout: reply.stdout } : {}),\n ...(reply.stderr !== undefined\n ? { stderr: reply.stderr }\n : { stderr: errorMessage }),\n };\n } catch {\n // Reconstruction failed — never escalate an internal error into a block.\n return ALLOW;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Native (passthrough) hooks — host events OUTSIDE the normalized union\n// ─────────────────────────────────────────────────────────────────────────\n\n/** Flags + stdin for {@link runNativeHook}. `event` is HOST-NATIVE (non-union). */\nexport interface RunNativeHookOptions {\n /** Host platform id from the hook command (`hook <platformId> ...`). */\n platformId: string;\n /** Host-native event name, verbatim (e.g. \"TaskCreated\"). */\n event: string;\n /** Connector id from `--connector <id>`. */\n connectorId: string;\n /** Raw stdin payload (host-native JSON). Empty string is tolerated → `{}`. */\n stdin: string;\n}\n\n/** The connector's native hook definition for `platformId`/`event`, if any. */\nfunction nativeHookDef(\n connector: ResolvedConnector,\n platformId: string,\n event: string,\n): NativeHookDef | undefined {\n return connector.platforms[platformId as PlatformId]?.nativeHooks?.[event];\n}\n\n/**\n * True when the registered connector declares `event` under\n * `platforms[platformId].nativeHooks` with a live handler. Used by the hook CLI\n * to decide whether a non-union event name is accepted (declared → dispatch via\n * {@link runNativeHook}) or rejected with the strict unknown-event error.\n * Fail-safe: any load error reads as \"not declared\".\n */\nexport async function isNativeHookDeclared(\n platformId: string,\n event: string,\n connectorId: string,\n): Promise<boolean> {\n try {\n const connector = await loadRegisteredConnector(connectorId);\n return typeof nativeHookDef(connector, platformId, event)?.handler === \"function\";\n } catch {\n return false;\n }\n}\n\n/**\n * Dispatch one NATIVE (passthrough) hook invocation. No normalized parse, no\n * HookResponse mapping, no runtime matcher evaluation (the host's own matcher\n * already filtered — the def's matcher string was written verbatim into the\n * host config at install):\n *\n * stdin JSON → NativeHookEvent{raw} → handler → VERBATIM JSON stdout (exit 0).\n *\n * void/undefined return → exit 0 with no output. Fail-open: ANY throw degrades\n * to exit 0 with no output — exit-2 blocking semantics are not modeled in v1.\n * Telemetry: records the same scope:\"hook\" developer-axis row as normalized\n * hooks, with the NATIVE event name as the per-item name.\n */\nexport async function runNativeHook(\n opts: RunNativeHookOptions,\n): Promise<RunHookResult> {\n const { platformId, event, connectorId, stdin } = opts;\n try {\n const connector = await loadRegisteredConnector(connectorId);\n const def = nativeHookDef(connector, platformId, event);\n if (!def || typeof def.handler !== \"function\") {\n // Not declared for this platform — nothing to do, allow.\n return ALLOW;\n }\n\n const raw = parseStdin(stdin);\n // Best-effort session/project extraction from the json-stdio common shape\n // (Claude Code: session_id / cwd). Everything else stays in `raw` untouched.\n const sessionId =\n typeof (raw as { session_id?: unknown })?.session_id === \"string\"\n ? (raw as { session_id: string }).session_id\n : \"\";\n const cwd = (raw as { cwd?: unknown })?.cwd;\n const evt: NativeHookEvent = {\n event,\n hostPlatform: REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)\n ? (platformId as PlatformId)\n : \"unknown\",\n sessionId,\n ...(typeof cwd === \"string\" && cwd !== \"\" ? { projectDir: cwd } : {}),\n raw,\n };\n\n const result = await def.handler(evt);\n\n // Same scope:\"hook\" telemetry row as normalized hooks (fail-open inside).\n recordHookTelemetry(opts, connector, evt, result === undefined ? {} : result);\n\n if (result === undefined) return ALLOW; // void → exit 0, no output\n const json = JSON.stringify(result);\n // Non-JSON-serializable returns (functions/symbols) degrade to silence.\n if (json === undefined) return ALLOW;\n return { exitCode: 0, stdout: json };\n } catch (err) {\n // Fail-open, unconditionally: native hooks have no deny-preserve carve-out\n // (there is no normalized decision to reconstruct).\n const message = err instanceof Error ? err.message : String(err);\n log.error(\n `native hook ${platformId}/${event} (${connectorId}) failed:`,\n message,\n );\n return ALLOW;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/telemetry/store.ts","../src/telemetry/types.ts","../src/telemetry/tokenizer.ts"],"sourcesContent":["/**\n * telemetry/store — the default NDJSON {@link TelemetryStore} implementation.\n *\n * Local-first, dependency-free persistence. One JSON object per line; appends\n * are atomic enough for concurrent multi-host writers (a single `appendFileSync`\n * of a `<line>\\n` chunk is written in one `write(2)` for small payloads, so\n * interleaving across processes does not corrupt individual records). A\n * SQLite/WAL backend is a drop-in upgrade behind the same interface.\n *\n * Telemetry stores AGGREGATE COUNTS ONLY — raw tool arguments/results are never\n * persisted (enforced by the {@link ToolEventRecord} shape, which carries no\n * content fields).\n *\n * Global kill switch: AGENT_CONNECTOR_TELEMETRY=0 makes `append` a no-op so a\n * disabled telemetry layer can never touch disk or break a host's tool call.\n */\n\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nimport { ensureDir, telemetryPath } from \"../core/paths.js\";\nimport { worstConfidence } from \"./types.js\";\nimport type {\n QueryFilter,\n RollupRow,\n TelemetryStore,\n ToolEventRecord,\n} from \"./types.js\";\n\n/** Options for {@link openStore}. */\nexport interface OpenStoreOptions {\n /** Override the NDJSON file path. Defaults to telemetryPath(\"ndjson\"). */\n path?: string;\n}\n\n// Confidence ranking + worst-of comparison live in ./types (the single source\n// of truth) so a new ConfidenceSource value orders correctly everywhere.\n\n/** Is `process.env.AGENT_CONNECTOR_TELEMETRY` an explicit off switch? */\nfunction telemetryDisabled(): boolean {\n return process.env.AGENT_CONNECTOR_TELEMETRY === \"0\";\n}\n\n/**\n * A unique-enough record id. Combines the wall clock with a caller-supplied\n * sequence so two records written in the same millisecond still differ.\n */\nexport function newRecordId(seq: number): string {\n return `${Date.now()}-${seq}`;\n}\n\nclass NdjsonStore implements TelemetryStore {\n private readonly path: string;\n\n constructor(path: string) {\n this.path = path;\n }\n\n append(record: ToolEventRecord): void {\n if (telemetryDisabled()) return;\n ensureDir(dirname(this.path));\n appendFileSync(this.path, `${JSON.stringify(record)}\\n`, \"utf8\");\n }\n\n query(filter: QueryFilter): ToolEventRecord[] {\n if (!existsSync(this.path)) return [];\n\n let text: string;\n try {\n text = readFileSync(this.path, \"utf8\");\n } catch {\n return [];\n }\n\n const out: ToolEventRecord[] = [];\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed === \"\") continue;\n\n let record: ToolEventRecord;\n try {\n record = JSON.parse(trimmed) as ToolEventRecord;\n } catch {\n continue; // skip malformed lines\n }\n\n if (matches(record, filter)) out.push(record);\n }\n return out;\n }\n\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[] {\n const groups = new Map<string, RollupRow>();\n\n for (const record of this.query(filter)) {\n const key = groupKey(record, by);\n const existing = groups.get(key);\n if (existing === undefined) {\n groups.set(key, {\n key,\n calls: 1,\n inputTokens: record.inputTokens,\n outputTokens: record.outputTokens,\n totalTokens: record.inputTokens + record.outputTokens,\n confidence: record.confidenceSource,\n lastTs: record.ts,\n });\n } else {\n existing.calls += 1;\n existing.inputTokens += record.inputTokens;\n existing.outputTokens += record.outputTokens;\n existing.totalTokens += record.inputTokens + record.outputTokens;\n existing.confidence = worstConfidence(\n existing.confidence,\n record.confidenceSource,\n );\n if (record.ts > existing.lastTs) existing.lastTs = record.ts;\n }\n }\n\n return [...groups.values()];\n }\n\n close(): void {\n /* no-op for the NDJSON backend */\n }\n}\n\n/** Does a record satisfy every set field of the filter? */\nfunction matches(record: ToolEventRecord, filter: QueryFilter): boolean {\n if (\n filter.connectorId !== undefined &&\n record.connectorId !== filter.connectorId\n ) {\n return false;\n }\n if (\n filter.projectKey !== undefined &&\n record.projectKey !== filter.projectKey\n ) {\n return false;\n }\n if (filter.sessionId !== undefined && record.sessionId !== filter.sessionId) {\n return false;\n }\n if (filter.toolName !== undefined && record.toolName !== filter.toolName) {\n return false;\n }\n if (filter.sinceMs !== undefined && record.ts < filter.sinceMs) {\n return false;\n }\n return true;\n}\n\n/** Resolve the grouping key value for a record under the requested dimension. */\nfunction groupKey(\n record: ToolEventRecord,\n by: \"tool\" | \"session\" | \"project\",\n): string {\n switch (by) {\n case \"tool\":\n return record.toolName;\n case \"session\":\n return record.sessionId;\n case \"project\":\n return record.projectDir;\n }\n}\n\n/**\n * Open the default local telemetry store. NDJSON backend; no native deps. The\n * returned store is safe to use even when telemetry is disabled — `append`\n * becomes a no-op and reads of a missing file return empty results.\n */\nexport function openStore(opts?: OpenStoreOptions): TelemetryStore {\n const path = opts?.path ?? telemetryPath(\"ndjson\");\n return new NdjsonStore(path);\n}\n","/**\n * telemetry/types — runtime telemetry contracts.\n *\n * The framework measures the MCP server's OWN bytes (tool args in, tool result\n * out, tool-definition schemas) and tokenizes them locally — the only signal\n * identical across every host (no host reports per-tool usage to a server).\n * Records are aggregate counts only; raw arguments/results are NEVER stored.\n */\n\nimport type { PlatformId } from \"../core/types.js\";\n\n/** Provenance of a token count — surfaced so \"estimate\" is never read as \"exact\". */\nexport type ConfidenceSource =\n | \"tokenizer-exact\" // real BPE for a matching model family\n | \"tokenizer-calibrated\" // approx adjusted by a sampled Anthropic count_tokens factor\n | \"tokenizer-approx\" // real BPE used as a documented approximation (e.g. Anthropic)\n | \"heuristic\" // chars/4 fallback\n | \"host-native\"; // a host actually reported usage (e.g. Gemini AfterModel)\n\n/**\n * Confidence ranking, least-trustworthy (0) → most-trustworthy. The single\n * source of truth for ordering {@link ConfidenceSource} values; every\n * worst-of comparison across the telemetry subsystem (store rollup, both\n * leaderboards, report, measure) reads through {@link rankOf} /\n * {@link worstConfidence} so a new value orders correctly everywhere.\n *\n * heuristic < tokenizer-approx < tokenizer-calibrated < tokenizer-exact < host-native\n *\n * `tokenizer-calibrated` sits between the raw approximation and an exact\n * family-matched BPE count: it is an approximation nudged toward truth by a\n * sampled real count_tokens factor, so it is more trustworthy than bare approx\n * but still not the exact encoding for the target family.\n */\nexport const CONFIDENCE_RANK: Record<ConfidenceSource, number> = {\n heuristic: 0,\n \"tokenizer-approx\": 1,\n \"tokenizer-calibrated\": 2,\n \"tokenizer-exact\": 3,\n \"host-native\": 4,\n};\n\n/** Trust rank of a single {@link ConfidenceSource} (higher = more trustworthy). */\nexport function rankOf(c: ConfidenceSource): number {\n return CONFIDENCE_RANK[c];\n}\n\n/** Return whichever source is the worse (least-confident) of the two. */\nexport function worstConfidence(\n a: ConfidenceSource,\n b: ConfidenceSource,\n): ConfidenceSource {\n return CONFIDENCE_RANK[b] < CONFIDENCE_RANK[a] ? b : a;\n}\n\nexport type ModelFamily = \"openai\" | \"anthropic\" | \"generic\";\n\nexport interface TokenCount {\n tokens: number;\n source: ConfidenceSource;\n}\n\n/** Pluggable tokenizer. Default impl uses gpt-tokenizer; fallback is chars/4. */\nexport interface Tokenizer {\n /** Count tokens for a string under the given model family. */\n count(text: string, family: ModelFamily): TokenCount;\n /** Count tokens for an arbitrary JSON value (serialized canonically first). */\n countValue(value: unknown, family: ModelFamily): TokenCount;\n}\n\n/**\n * What a record measures. DISTINCT origins that must never be summed:\n * • `call` — one per-MCP `tools/call` round-trip (serve-proxy bytes).\n * • `tool_defs` — the one-time `tools/list` schema overhead (serve-proxy).\n * • `model_turn` — a WHOLE-CONVERSATION host-native turn reported by the host\n * (e.g. Gemini/Antigravity AfterModel `usageMetadata`). This is NOT per-MCP:\n * it covers the entire model turn, so the MCP leaderboard EXCLUDES it (just\n * as it treats `tool_defs` specially) and it is surfaced as its own labeled\n * section rather than added to the per-MCP `call` totals or to the\n * usage-reader host-scan numbers.\n * • `hook` — one RUNTIME hook dispatch through the home-bin `hook`\n * entrypoint (src/runtime/hook-entrypoint). The framework tokenizes the\n * inbound normalized event payload the handler reads (input) and what the\n * handler returns that becomes context/decision (output). The per-item\n * `toolName` is the hook EVENT name (e.g. \"SessionStart\"). This is the\n * developer-axis \"hook\" surface — measured live, like `call`.\n */\nexport type EventScope = \"call\" | \"tool_defs\" | \"model_turn\" | \"hook\";\n\n/**\n * Which of the FIVE developer-axis surfaces a record (or footprint) belongs to.\n * `server`/`hook` are RUNTIME-measured surfaces that produce {@link ToolEventRecord}\n * store rows; `command`/`skill`/`subagent` are STATIC footprints computed from the\n * registered connector (the host loads them as context — we never intercept them,\n * so they are NEVER written as usage rows). OPTIONAL on the record so rows written\n * before this field existed (every legacy serve-proxy row) read as `server`.\n */\nexport type SurfaceKind = \"server\" | \"hook\" | \"command\" | \"skill\" | \"subagent\";\n\n/**\n * The install scope a wrapped server was deployed under, narrowed to the two\n * dimensions that matter for slicing telemetry: a `user`-global install vs a\n * `project`-local one. The framework's broader {@link InstallScope}\n * (`system|user|project|profile|managed`) is mapped down to this at wrap time —\n * everything that is not project-local reads as `user`. Optional on the record\n * so rows written before this field existed are treated as \"unknown\".\n */\nexport type TelemetryInstallScope = \"user\" | \"project\";\n\n/**\n * How the real MCP server underneath the proxy was launched. `npx`/`bunx`/`uvx`\n * are ephemeral package runners; `node`/`bun`/`deno` are interpreters running a\n * local script; `binary` is a resolved executable on PATH; `http` marks a remote\n * server reached over the network (no local launch); `unknown` is the honest\n * fallback. Optional on the record so older rows read as \"unknown\".\n */\nexport type LaunchMethod =\n | \"npx\"\n | \"bunx\"\n | \"uvx\"\n | \"node\"\n | \"binary\"\n | \"http\"\n | \"unknown\";\n\n/**\n * One telemetry row. Aggregate counts only — no content. `projectKey` is the\n * hashed stable project identity (git remote || normalized abs path); `projectDir`\n * is kept human-readable for reports but is the same partition.\n */\nexport interface ToolEventRecord {\n /** Monotonic-ish unique id (e.g. `${ts}-${seq}`). */\n id: string;\n /** Epoch milliseconds. */\n ts: number;\n connectorId: string;\n toolName: string;\n scope: EventScope;\n hostPlatform: PlatformId;\n sessionId: string;\n projectKey: string;\n projectDir: string;\n inputTokens: number;\n outputTokens: number;\n confidenceSource: ConfidenceSource;\n isError: boolean;\n /**\n * The (narrowed) install scope the wrapped server was deployed under — a\n * slicing dimension for global(user) vs project usage. OPTIONAL: rows written\n * before this field existed lack it and must be read as \"unknown\".\n */\n installScope?: TelemetryInstallScope;\n /**\n * How the real server was launched (npx/bunx/uvx/node/binary/http) — the\n * \"launch-method\" slicing dimension. OPTIONAL: older rows lack it → \"unknown\".\n */\n launchMethod?: LaunchMethod;\n /**\n * Which developer-axis surface produced this row. OPTIONAL and\n * backward-compatible: rows written before this field existed (every legacy\n * serve-proxy `call`/`tool_defs` row) lack it and MUST be read as `server`.\n * Runtime rows stamp it explicitly (`server` for the proxy, `hook` for the\n * hook runtime); the static command/skill/subagent surfaces never produce\n * store rows (they are reported as footprints — see surface-footprint.ts).\n */\n surfaceKind?: SurfaceKind;\n}\n\nexport interface QueryFilter {\n connectorId?: string;\n projectKey?: string;\n sessionId?: string;\n toolName?: string;\n /** Lower bound epoch ms (inclusive). */\n sinceMs?: number;\n}\n\n/** A grouped rollup row for reports. */\nexport interface RollupRow {\n /** The grouping key value (tool name, session id, or project dir). */\n key: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n /** Worst (least-confident) source seen in the group, for honest labeling. */\n confidence: ConfidenceSource;\n lastTs: number;\n}\n\n/**\n * Local-first telemetry store. The default NDJSON implementation appends one\n * line per record (append is atomic enough for concurrent multi-host writers);\n * a SQLite/WAL implementation is a drop-in upgrade behind this same interface.\n */\nexport interface TelemetryStore {\n append(record: ToolEventRecord): void;\n query(filter: QueryFilter): ToolEventRecord[];\n /** Group + aggregate by tool | session | project. */\n rollup(by: \"tool\" | \"session\" | \"project\", filter: QueryFilter): RollupRow[];\n close(): void;\n}\n","/**\n * telemetry/tokenizer — the default {@link Tokenizer} implementation.\n *\n * Token counts are the only signal that is identical across every host: no host\n * reports a server's per-tool usage back to the server, so the framework\n * tokenizes its OWN bytes locally. We use gpt-tokenizer's `o200k_base` encoding\n * (the GPT-4o / o1 BPE) for all families:\n * • openai → exact BPE for the target family → \"tokenizer-exact\"\n * • anthropic → o200k_base used as a documented stand-in → \"tokenizer-approx\"\n * • generic → o200k_base used as a generic approximation → \"tokenizer-approx\"\n *\n * gpt-tokenizer is loaded LAZILY on first use, inside a try/catch. If the module\n * (or its BPE data) fails to load we degrade to a chars/4 heuristic that never\n * throws, so telemetry can never break a host's tool call.\n *\n * Pure + side-effect-free apart from the one-time lazy encoder load. No IO.\n */\n\nimport { createRequire } from \"node:module\";\n\nimport type {\n ConfidenceSource,\n ModelFamily,\n TokenCount,\n Tokenizer,\n} from \"./types.js\";\n\n/**\n * A CommonJS `require` bound to this module's URL. gpt-tokenizer ships its\n * encoding entry points as CJS (the package `exports` `require` condition maps\n * `./encoding/*` to `./cjs/encoding/*.js`), so we resolve them synchronously via\n * createRequire — this keeps the {@link Tokenizer} surface synchronous and works\n * under ESM / NodeNext without baking an absolute path.\n */\nconst requireCjs = createRequire(import.meta.url);\n\n/** Minimal shape we use from a gpt-tokenizer encoding module. */\ninterface EncodingModule {\n encode(text: string): number[];\n}\n\n/**\n * Lazy-load state for the shared o200k_base encoder.\n * - undefined → not attempted yet\n * - null → load attempted and failed (stick to the heuristic)\n * - object → loaded encoder\n */\nlet encoderState: EncodingModule | null | undefined;\n\n/** Average characters-per-token for the chars/4 fallback heuristic. */\nconst HEURISTIC_CHARS_PER_TOKEN = 4;\n\n/**\n * Lazily import the o200k_base encoding. Returns the encoder, or null if the\n * module/data could not be loaded (in which case callers use the heuristic).\n *\n * The dynamic import resolves to `gpt-tokenizer/esm/encoding/o200k_base.js` via\n * the package `exports` map and exposes a named `encode(text) => number[]`.\n */\nfunction loadEncoder(): EncodingModule | null {\n if (encoderState !== undefined) return encoderState;\n try {\n // Synchronous require keeps count() a sync function (per the Tokenizer\n // interface). The package's `require` export condition resolves this to\n // gpt-tokenizer/cjs/encoding/o200k_base.js, which exports a named `encode`.\n const mod = requireCjs(\"gpt-tokenizer/encoding/o200k_base\") as {\n encode?: (text: string) => number[];\n };\n if (mod && typeof mod.encode === \"function\") {\n encoderState = { encode: mod.encode.bind(mod) };\n } else {\n encoderState = null;\n }\n } catch {\n encoderState = null;\n }\n return encoderState;\n}\n\n/** Heuristic chars/4 count — used when no encoder is available. Never throws. */\nfunction heuristicCount(text: string): TokenCount {\n return {\n tokens: Math.ceil(text.length / HEURISTIC_CHARS_PER_TOKEN),\n source: \"heuristic\",\n };\n}\n\n/**\n * Confidence label for a successful BPE count: exact only for the OpenAI family\n * (the encoding actually matches), approximate for everything else.\n */\nfunction bpeSource(family: ModelFamily): ConfidenceSource {\n return family === \"openai\" ? \"tokenizer-exact\" : \"tokenizer-approx\";\n}\n\n/**\n * The default tokenizer. A single shared instance is fine: the encoder is\n * stateless across calls and lazy-loaded once.\n */\nclass DefaultTokenizer implements Tokenizer {\n count(text: string, family: ModelFamily): TokenCount {\n const encoder = loadEncoder();\n if (!encoder) return heuristicCount(text);\n try {\n return { tokens: encoder.encode(text).length, source: bpeSource(family) };\n } catch {\n // A per-call encode failure (e.g. a pathological input) still degrades\n // gracefully rather than breaking the host's tool call.\n return heuristicCount(text);\n }\n }\n\n countValue(value: unknown, family: ModelFamily): TokenCount {\n return this.count(canonicalString(value), family);\n }\n}\n\n/**\n * Canonical string form of an arbitrary JSON value for counting.\n * `undefined` (and anything that JSON.stringify drops to undefined, e.g. a bare\n * function or symbol) serializes to the empty string.\n */\nfunction canonicalString(value: unknown): string {\n if (typeof value === \"string\") return value;\n const json = JSON.stringify(value);\n return json ?? \"\";\n}\n\nconst SHARED_TOKENIZER: Tokenizer = new DefaultTokenizer();\n\n/** Return the default {@link Tokenizer} (shared singleton). */\nexport function getTokenizer(): Tokenizer {\n return SHARED_TOKENIZER;\n}\n\n/**\n * Infer the {@link ModelFamily} for token counting.\n *\n * An explicit hint other than \"auto\" wins outright. Otherwise we sniff the\n * client/host name: Anthropic/Claude → anthropic, OpenAI/GPT/Codex → openai,\n * Gemini and everything unrecognized → generic.\n */\nexport function inferModelFamily(\n clientName: string,\n hint: \"auto\" | ModelFamily,\n): ModelFamily {\n if (hint !== \"auto\") return hint;\n const name = clientName.toLowerCase();\n if (name.includes(\"claude\") || name.includes(\"anthropic\")) return \"anthropic\";\n if (\n name.includes(\"codex\") ||\n name.includes(\"gpt\") ||\n name.includes(\"openai\")\n ) {\n return \"openai\";\n }\n // gemini and all others fall through to the generic approximation.\n return \"generic\";\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/openclaw/index.ts"],"sourcesContent":["/**\n * adapters/openclaw — OpenClaw (Gateway) platform adapter for agent-connector.\n *\n * OpenClaw is a **ts-plugin** host, but with a twist no other ts-plugin host\n * has: a plugin only delivers its MCP tools to the agent when it is registered\n * in TWO places at once (DUAL REGISTRATION):\n *\n * (a) plugins.entries.<id> = { enabled: true } + plugins.load.paths: [dir]\n * — so the gateway DISCOVERS and LOADS the plugin\n * module (runs register(api), wiring the hooks).\n * NOTE: the entry is { enabled } only — a per-entry\n * `module` field is rejected by `openclaw config\n * validate`, so discovery goes through the\n * plugins.load.paths dir scan instead; and\n * (b) mcp.servers.<id> — nested under a top-level \"mcp\" object, so the\n * plugin's MCP tools SURFACE to the agent.\n *\n * If only (a) is present, the plugin loads but ZERO tools reach the agent.\n * If only (b) is present, the tools are declared but the hook bridge never\n * loads. context-mode's own adapter proved this empirically against OpenClaw\n * 2026.4.22 (src/adapters/openclaw/index.ts checkPluginRegistration: \"in\n * plugins.entries but missing from mcp.servers — plugin loads but no ctx_*\n * tools reach the agent\"). So getHealthChecks() FAILS when the two are\n * inconsistent (entries XOR mcp.servers).\n *\n * Why we BRIDGE instead of importing handlers (mirrors the OpenCode adapter):\n * context-mode could load ITS OWN plugin in-process because the handler code\n * shipped with the package. agent-connector cannot: the connector's hook\n * handlers are arbitrary developer code we must not import into the gateway's\n * runtime (wrong cwd, wrong deps, version skew). So installHooks synthesizes a\n * tiny, fully self-contained ESM plugin module — { id, name, configSchema,\n * register(api) } — that imports NOTHING from agent-connector and, on each\n * hook firing, shells out to the ONE stable home binary's universal entrypoint\n * <homeBin> hook openclaw <event> --connector <id>\n * feeding the event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout. Fail-open: any bridge error → no-op. The same\n * universal json-stdio dispatcher every other host uses thus serves OpenClaw.\n *\n * Plugin module shape (grounded in context-mode build/adapters/openclaw/plugin.js):\n * export default { id, name, configSchema, register(api) }\n * register(api) wires typed lifecycle hooks via api.on(event, handler):\n * before_tool_call → block (return { block, blockReason }) / rewrite args\n * (mutate event.params in place); deny+ask both block.\n * after_tool_call → observe (capture result/output).\n * session_start → record the real session id.\n * before_prompt_build → inject context (fires PER TURN; no blocking). ONE\n * handler serves two events via separate state: SessionStart\n * additionalContext via { appendSystemContext } ONCE (the\n * verified injection point — session_start itself returns no\n * context payload) AND UserPromptSubmit additionalContext via\n * { appendContext } EVERY turn (context-injection only — a\n * block/deny decision degrades to a no-op).\n * subagent_spawned / subagent_ended → observe the subagent lifecycle\n * (SubagentStart / SubagentStop; observe-only — no\n * decision or context payload).\n * api.on() is the correct API for typed lifecycle hooks; api.registerHook() is\n * for generic command hooks. We register defensively (api.on present check).\n *\n * Config file (JSON5 / JSONC — PARSE TOLERANTLY):\n * user scope → resolveOpenClawConfigPath():\n * $OPENCLAW_CONFIG_PATH\n * else $OPENCLAW_STATE_DIR/openclaw.json\n * else ~/.openclaw/openclaw.json\n * (mirrors context-mode openclawConfigPath — the file the\n * gateway actually loads, NOT process.cwd()).\n * project scope → <projectDir>/openclaw.json (also supported).\n * openclaw.json may contain comments + trailing commas (JSON5), so we NEVER\n * use strict JSON.parse — readJson is overridden to strip comments/commas\n * first via the shared core/jsonc parseJsonc. writes are strict JSON\n * (idempotent, comment-free).\n *\n * Plugin module location (project | user):\n * user → <stateDir>/extensions/<id>/index.mjs (stateDir = dir of openclaw.json)\n * project → <projectDir>/.openclaw/extensions/<id>/index.mjs\n * Beside index.mjs we also emit an openclaw.plugin.json manifest\n * { id, name, main, enabled, configSchema }. `configSchema` is REQUIRED —\n * `openclaw config validate` rejects a manifest without it (\"plugin manifest\n * requires configSchema\"); our plugin takes no user config, so it is the empty\n * closed object schema { type: \"object\", additionalProperties: false }. The\n * plugin's DIRECTORY (not the file) is added to plugins.load.paths so the\n * gateway scans it and loads the module; the plugins.entries.<id> =\n * { enabled: true } half then activates it. (There is no per-entry \"module\"\n * field — validate rejects it.)\n *\n * The gateway hot-reloads its config on SIGUSR1 — no action needed here; the\n * next reload (or restart) picks up our entries/servers/module.\n *\n * Capabilities (per OpenClaw hook API):\n * preToolUse / postToolUse / sessionStart / userPromptSubmit / subagentStart /\n * subagentStop true; supportsNativeHooks true; transports [\"stdio\"].\n * before_tool_call mutates event.params → canModifyArgs true.\n * after_tool_call cannot rewrite the tool result → canModifyOutput false.\n * before_prompt_build injects context → canInjectSessionContext true; it has\n * no blocking ability, so userPromptSubmit is context-injection-only.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport { BaseAdapter } from \"../base.js\";\nimport type {\n Adapter,\n GeneratedPluginFile,\n HookReply,\n InstallContext,\n MemoryTarget,\n NormalizedEvent,\n} from \"../spi.js\";\nimport type {\n ChangeRecord,\n DetectedPlatform,\n HealthCheck,\n HookEventName,\n HookParadigm,\n HookResponse,\n PlatformCapabilities,\n PlatformId,\n PostToolUseEvent,\n PreToolUseEvent,\n ServerDef,\n SessionStartEvent,\n SubagentStartEvent,\n SubagentStopEvent,\n UserPromptSubmitEvent,\n} from \"../../core/types.js\";\nimport { resolveEnvRefsDeep } from \"../../core/interpolate.js\";\nimport { parseJsonc } from \"../../core/jsonc.js\";\nimport {\n buildWrappedStdio,\n} from \"../../core/spawn.js\";\nimport { renderSkillMd } from \"../claude-code/render.js\";\n\nconst HOST: PlatformId = \"openclaw\";\n\n/**\n * MCP servers live nested under the top-level \"mcp\" object: mcp.servers.<id>\n * (NOT a top-level \"mcpServers\" key). plugins.entries.<id> is the second half of\n * the dual registration.\n */\nconst MCP_ROOT_KEY = \"mcp\";\nconst MCP_SERVERS_KEY = \"servers\";\nconst PLUGINS_KEY = \"plugins\";\nconst PLUGINS_ENTRIES_KEY = \"entries\";\n/**\n * The gateway discovers a synthesized plugin module by scanning the directories\n * listed in plugins.load.paths — it does NOT take a per-entry `module` field\n * (that fails `openclaw config validate`). We add the plugin's own dir here.\n */\nconst PLUGINS_LOAD_KEY = \"load\";\nconst PLUGINS_LOAD_PATHS_KEY = \"paths\";\n/** Manifest the gateway reads inside a plugins.load.paths directory. */\nconst PLUGIN_MANIFEST_FILE = \"openclaw.plugin.json\";\n\n/**\n * Canonical → OpenClaw event name map. A connector hook event is only emitted by\n * the generated plugin when it appears here AND is declared by the connector.\n *\n * SessionStart maps to OpenClaw's session_start (to learn the real session id)\n * plus before_prompt_build (the verified context-injection point — session_start\n * itself returns no context payload upstream).\n *\n * UserPromptSubmit also maps to before_prompt_build, which fires PER TURN (every\n * prompt build) and can ONLY inject context (prependContext / appendContext /\n * *SystemContext) — it has NO blocking ability. So UserPromptSubmit here is\n * CONTEXT-INJECTION-ONLY: a connector handler returning a block/deny decision\n * DEGRADES to a no-op (the generated bridge injects nothing). The two events\n * coexist in one before_prompt_build handler via separate state (the once-only\n * SessionStart flag must never suppress the per-turn UserPromptSubmit injection).\n */\nconst EVENT_TO_OPENCLAW: Partial<Record<HookEventName, string>> = {\n PreToolUse: \"before_tool_call\",\n PostToolUse: \"after_tool_call\",\n SessionStart: \"session_start\",\n // Per-turn context injection at prompt assembly (context-only, no blocking).\n UserPromptSubmit: \"before_prompt_build\",\n // Subagent lifecycle: OpenClaw observes launch + completion (no decision\n // control on either). PermissionRequest is deliberately ABSENT — OpenClaw's\n // permission gate is the requireApproval RETURN VALUE of before_tool_call,\n // not an observable event; PostToolUseFailure is ABSENT too (a failure\n // arrives merged into after_tool_call, already surfaced as PostToolUse\n // isError). Unmapped declared events are reported as \"unsupported here\".\n SubagentStart: \"subagent_spawned\",\n SubagentStop: \"subagent_ended\",\n};\n\n/** Raw payload the generated plugin posts to the universal hook entrypoint. */\ninterface OpenClawBridgePayload {\n toolName?: string;\n toolInput?: Record<string, unknown>;\n toolOutput?: string;\n isError?: boolean;\n sessionId?: string;\n source?: string;\n projectDir?: string;\n // UserPromptSubmit (before_prompt_build): the current prompt text.\n prompt?: string;\n // subagent_spawned / subagent_ended. The generated plugin projects the host's\n // REAL native fields here (verified against openclaw/openclaw\n // src/plugins/hook-types.ts): SubagentStart agentId←e.agentId,\n // agentType←e.label (the host's only descriptor); SubagentStop\n // agentId←e.targetSessionKey (its ONLY identity). The host emits no agentType\n // and no result/output on subagent_ended, so those slots are simply omitted —\n // we never post invented field names. Empty strings are dropped.\n agentId?: string;\n agentType?: string;\n lastAssistantMessage?: string;\n}\n\n/**\n * Native MCP server entry shapes OpenClaw accepts under mcp.servers.<id>.\n *\n * A stdio sidecar carries NO `transport` field: OpenClaw's config validator\n * only accepts `transport: \"sse\" | \"streamable-http\"` for remote servers and\n * REJECTS `transport: \"stdio\"` (verified against OpenClaw 2026.6.1). A stdio\n * server is INFERRED from the presence of `command`, so we must never write a\n * transport key here.\n */\ninterface OpenClawStdioServer {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n enabled?: boolean;\n}\ninterface OpenClawRemoteServer {\n url: string;\n transport: \"sse\" | \"streamable-http\";\n headers?: Record<string, string>;\n enabled?: boolean;\n}\n\n/**\n * plugins.entries.<id> reference shape.\n *\n * `openclaw config validate` REJECTS a `module` field here (it is not part of\n * the entry schema), so the entry is `{ enabled: true }` ONLY. Discovery of the\n * synthesized module is wired separately via `plugins.load.paths` (the dir that\n * holds the module + its openclaw.plugin.json manifest).\n */\ninterface OpenClawPluginEntry {\n enabled: boolean;\n}\n\nexport class OpenClawAdapter extends BaseAdapter implements Adapter {\n readonly id: PlatformId = HOST;\n // Declared as `string` (not the literal \"OpenClaw\") so a fork adapter can\n // override it — NemoClaw extends this class (precedent: AntigravityAdapter,\n // whose name is likewise typed `string` for AntigravityCliAdapter).\n readonly name: string = \"OpenClaw\";\n readonly paradigm: HookParadigm = \"ts-plugin\";\n\n readonly capabilities: PlatformCapabilities = {\n // Memory surface: AGENTS.md-first managed block. OpenClaw's AGENTS.md\n // lives in the AGENT WORKSPACE (default ~/.openclaw/workspace), NOT the\n // user's repo — memoryTargets below maps BOTH scopes there (R2:\n // platforms force scopes; personal-assistant semantics, no project file).\n supportsMemory: true,\n preToolUse: true,\n postToolUse: true,\n // OpenClaw compaction is owned by a context engine, not wired through the\n // universal bridge here.\n preCompact: false,\n sessionStart: true,\n sessionEnd: false,\n // before_prompt_build fires per-turn and injects context (no blocking), so\n // UserPromptSubmit is supported as CONTEXT-INJECTION-ONLY: a block/deny\n // decision degrades to a no-op (it cannot stop the prompt here).\n userPromptSubmit: true,\n stop: false,\n notification: false,\n // Newer events: OpenClaw observes the full subagent lifecycle via\n // subagent_spawned / subagent_ended (observe-only — no decision control,\n // so a SubagentStop \"deny\" cannot keep the subagent running here).\n // permissionRequest / postToolUseFailure stay unset: the permission gate\n // is a RETURN VALUE of before_tool_call (not an event) and tool failures\n // arrive merged into after_tool_call. Install reports the standard\n // \"unsupported here\" for unmapped declared events.\n subagentStart: true,\n subagentStop: true,\n // before_tool_call mutates event.params in place → input rewrite supported.\n canModifyArgs: true,\n // after_tool_call observes but cannot rewrite the tool result upstream.\n canModifyOutput: false,\n // injected via before_prompt_build at prompt assembly.\n canInjectSessionContext: true,\n // stdio sidecars + remote servers. OpenClaw's validator accepts a remote\n // transport of \"sse\" | \"streamable-http\" (AC \"http\" → \"streamable-http\");\n // verified against OpenClaw 2026.6.1 + docs.openclaw.ai/gateway/configuration-reference.\n transports: [\"stdio\", \"sse\", \"http\"],\n // Content surface: OpenClaw follows the AgentSkills spec (dir-per-skill\n // SKILL.md with {name, description} frontmatter + body). User scope →\n // ~/.openclaw/skills (the `openclaw skills install --global` target);\n // project scope → the resolved <workspace>/skills (the highest-priority\n // documented skill root, the same workspace the memory surface anchors to).\n supportsSkills: true,\n // Action surface: each declared action becomes a user-invokable slash\n // command registered INSIDE the generated plugin module (api.registerCommand),\n // shelling out to the home-bin action verb with the install target baked as\n // the host token (this.id), so a fork like NemoClaw emits `action nemoclaw`.\n // supportsActions -> the installActions/uninstallActions overrides below.\n supportsActions: true,\n // Native passthrough: OpenClaw lifecycle hook names with no canonical analog\n // (declared on platforms.<id>.nativeHooks) are bridged verbatim by the\n // generated plugin via the same on(...) helper → home-bin runNativeHook\n // dispatch (mirrors omp/opencode). Independent of hooks:false.\n supportsNativeHooks: true,\n };\n\n // ── Tolerant JSON5/JSONC read (override base strict JSON.parse) ──────────\n\n /**\n * openclaw.json is officially JSON5 (comments + trailing commas allowed). A\n * strict JSON.parse false-fails a perfectly valid commented file, so we strip\n * comments and trailing commas before parsing. Writes remain strict JSON\n * (writeJson in BaseAdapter), which is itself valid JSON5 — so round-trips\n * stay safe and idempotent.\n */\n protected override readJson<T = Record<string, unknown>>(path: string): T | null {\n if (!existsSync(path)) return null;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return null;\n }\n try {\n // Shared, string-aware JSONC stripper (the local stripJsonish whose\n // trailing-comma regex corrupted in-string \",]\"/\",}\"-like values is gone).\n return parseJsonc<T>(raw);\n } catch {\n return null;\n }\n }\n\n // ── Memory surface: agent-workspace AGENTS.md (both scopes) ─────────────\n /**\n * OpenClaw loads AGENTS.md (operating instructions) from its AGENT WORKSPACE\n * every session — `agents.defaults.workspace` in openclaw.json when set,\n * else `<stateDir>/workspace` (default ~/.openclaw/workspace). That file is\n * personal-assistant state, NOT a repo file, so a \"project scope\" write does\n * not apply: BOTH scopes map to the workspace (R2: platforms force scopes),\n * with the reason string flagging the semantics.\n */\n protected override memoryTargets(ctx: InstallContext): MemoryTarget[] {\n if (this.memoryOverride(ctx)?.path) return super.memoryTargets(ctx);\n if (ctx.scope !== \"project\" && ctx.scope !== \"user\") return [];\n return [\n {\n path: join(this.resolveWorkspace(), \"AGENTS.md\"),\n reason:\n \"openclaw agent-workspace AGENTS.md (workspace-scoped operating instructions, \" +\n \"not the project repo — both scopes map here)\",\n },\n ];\n }\n\n /**\n * Resolve OpenClaw's AGENT WORKSPACE — `agents.defaults.workspace` in\n * openclaw.json when set, else `<stateDir>/workspace` (default\n * ~/.openclaw/workspace). The same root the memory surface and the\n * project-scope skills surface both anchor to, so they never diverge.\n */\n private resolveWorkspace(): string {\n const configPath = resolveOpenClawConfigPath();\n const cfg = this.readJson<{ agents?: { defaults?: { workspace?: unknown } } }>(configPath);\n const declared = cfg?.agents?.defaults?.workspace;\n return typeof declared === \"string\" && declared.trim() !== \"\"\n ? declared.startsWith(\"~\")\n ? resolve(homedir(), declared.replace(/^~[/\\\\]?/, \"\"))\n : resolve(declared)\n : join(dirname(configPath), \"workspace\");\n }\n\n // ── Detection ──────────────────────────────────────────────────────────\n\n detectInstalled(projectDir: string): DetectedPlatform {\n const userConfig = resolveOpenClawConfigPath();\n const userDir = join(homedir(), \".openclaw\");\n const projectConfig = join(projectDir, \"openclaw.json\");\n\n // Fork-before-parent at DETECTION: NemoClaw DRIVES ~/.openclaw/openclaw.json,\n // so on a NemoClaw box (its ~/.nemoclaw/ marker present) the user-scope\n // OpenClaw config is nemoclaw-OWNED — defer so the planner does not double-\n // target the shared file as two platforms (an `uninstall openclaw` would\n // otherwise strip the connector's entries out from under nemoclaw). The\n // NemoClaw adapter overrides detectInstalled, so it is unaffected by this.\n const deferToNemoClaw = existsSync(join(homedir(), \".nemoclaw\"));\n const userInstalled =\n !deferToNemoClaw && (existsSync(userConfig) || existsSync(userDir));\n const projInstalled = existsSync(projectConfig);\n const installed = userInstalled || projInstalled;\n\n const scope = projInstalled && !userInstalled ? \"project\" : \"user\";\n const configPath = scope === \"project\" ? projectConfig : userConfig;\n\n return {\n id: this.id,\n name: this.name,\n installed,\n paradigm: this.paradigm,\n capabilities: this.capabilities,\n configPath,\n scope,\n reason: installed\n ? scope === \"project\"\n ? `found project OpenClaw config at ${projectConfig}`\n : `found OpenClaw config at ${configPath}`\n : `no OpenClaw config at ${userConfig} or ${userDir}`,\n confidence: installed ? \"high\" : \"low\",\n };\n }\n\n // ── Native paths ───────────────────────────────────────────────────────\n\n getConfigDir(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? ctx.projectDir\n : dirname(resolveOpenClawConfigPath());\n }\n\n getServerConfigPath(ctx: InstallContext): string {\n return ctx.scope === \"project\"\n ? join(ctx.projectDir, \"openclaw.json\")\n : resolveOpenClawConfigPath();\n }\n\n /**\n * For ts-plugin hosts the \"hook config path\" is the generated plugin FILE.\n * OpenClaw additionally needs the plugins.entries reference written into\n * openclaw.json (handled in installHooks); this is the module on disk.\n */\n getHookConfigPath(ctx: InstallContext): string {\n return join(this.pluginDir(ctx), this.pluginFileName());\n }\n\n /** Extensions directory the synthesized plugin module is written into. */\n private pluginDir(ctx: InstallContext): string {\n const base =\n ctx.scope === \"project\"\n ? join(ctx.projectDir, \".openclaw\")\n : dirname(resolveOpenClawConfigPath());\n return join(base, \"extensions\", ctx.connector.id);\n }\n\n /** Plugin module file name (ESM so the gateway loads it without a loader). */\n private pluginFileName(): string {\n return \"index.mjs\";\n }\n\n // ── MCP server install / uninstall (mcp.servers.<id>) ───────────────────\n\n installServer(ctx: InstallContext): ChangeRecord[] {\n const server = this.effectiveServer(ctx);\n const serverPath = this.getServerConfigPath(ctx);\n\n if (!server) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: serverPath,\n detail: ctx.connector.server\n ? \"server registration disabled for openclaw\"\n : \"connector declares no MCP server\",\n },\n ];\n }\n\n const entry = this.renderServerEntry(ctx, server);\n return [this.upsertNestedServer(serverPath, ctx.connector.id, entry, ctx.dryRun)];\n }\n\n uninstallServer(ctx: InstallContext): ChangeRecord[] {\n const serverPath = this.getServerConfigPath(ctx);\n return [this.removeNestedServer(serverPath, ctx.connector.id, ctx.dryRun)];\n }\n\n /** Resolve the per-platform server override into an effective ServerDef. */\n private effectiveServer(ctx: InstallContext): ServerDef | undefined {\n const override = ctx.connector.platforms[this.id]?.server;\n if (override === false) return undefined;\n const base = ctx.connector.server;\n if (!base) return undefined;\n return override && typeof override === \"object\" ? { ...base, ...override } : base;\n }\n\n /**\n * Render a normalized ServerDef into OpenClaw's native mcp.servers.<id> entry.\n *\n * stdio → { command, args?, env? } (NO transport key — a stdio sidecar is\n * inferred from `command`; OpenClaw's validator rejects\n * transport:\"stdio\" and only accepts \"sse\"/\"streamable-http\")\n * remote → { url, transport: \"sse\"|\"streamable-http\", headers? }\n *\n * OpenClaw documents no native ${env:VAR} token, so refs resolve to literals.\n */\n private renderServerEntry(\n ctx: InstallContext,\n server: ServerDef,\n ): OpenClawStdioServer | OpenClawRemoteServer {\n if (server.transport === \"stdio\") {\n let command = server.command ?? \"\";\n let args = [...(server.args ?? [])];\n\n // Transparent telemetry wrapping: route the real command through\n // `<homeBin> serve --connector <id> -- <command> <args...>`.\n ({ command, args } = buildWrappedStdio(ctx, server, this.id, command, args));\n\n // NO `transport` key: OpenClaw infers a stdio sidecar from `command` and\n // its validator rejects transport:\"stdio\" (only \"sse\"/\"streamable-http\").\n const entry: OpenClawStdioServer = {\n command: resolveEnvRefsDeep(command),\n };\n const resolvedArgs = resolveEnvRefsDeep(args).filter((s) => s !== \"\");\n if (resolvedArgs.length > 0) entry.args = resolvedArgs;\n const env = this.renderEnv(server.env);\n if (env) entry.env = env;\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n // sse / http → OpenClaw registers a remote URL sidecar. AC's canonical\n // \"http\" (streamable HTTP) maps to OpenClaw's literal \"streamable-http\" — its\n // validator rejects a bare \"http\" transport value.\n const transport: \"sse\" | \"streamable-http\" =\n server.transport === \"http\" ? \"streamable-http\" : \"sse\";\n const entry: OpenClawRemoteServer = {\n url: resolveEnvRefsDeep(server.url ?? \"\"),\n transport,\n };\n if (server.headers && Object.keys(server.headers).length > 0) {\n entry.headers = resolveEnvRefsDeep({ ...server.headers });\n }\n if (server.enabled === false) entry.enabled = false;\n return entry;\n }\n\n /** Resolve every ${env:VAR} in env values to literals (no native token). */\n private renderEnv(\n env: Record<string, string> | undefined,\n ): Record<string, string> | undefined {\n if (!env || Object.keys(env).length === 0) return undefined;\n return resolveEnvRefsDeep({ ...env });\n }\n\n /** Upsert config.mcp.servers.<id> idempotently. */\n private upsertNestedServer(\n path: string,\n id: string,\n entry: unknown,\n dryRun: boolean,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(path)) {\n return {\n platform: this.id,\n action: \"warn\",\n path,\n detail: `existing ${path} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(path) ?? {};\n const servers = this.nestedServersBucket(cfg);\n const before = JSON.stringify(servers[id]);\n const after = JSON.stringify(entry);\n let action: ChangeRecord[\"action\"];\n if (before === undefined) action = \"create\";\n else if (before === after) action = \"skip\";\n else action = \"update\";\n if (action !== \"skip\") {\n servers[id] = entry;\n this.writeJson(path, cfg, dryRun);\n }\n return {\n platform: this.id,\n action,\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Remove config.mcp.servers.<id>. */\n private removeNestedServer(path: string, id: string, dryRun: boolean): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(path);\n const mcp = cfg?.[MCP_ROOT_KEY];\n const servers =\n mcp && typeof mcp === \"object\" && !Array.isArray(mcp)\n ? ((mcp as Record<string, unknown>)[MCP_SERVERS_KEY] as\n | Record<string, unknown>\n | undefined)\n : undefined;\n if (!cfg || !servers || !(id in servers)) {\n return {\n platform: this.id,\n action: \"skip\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id} absent`,\n };\n }\n delete servers[id];\n this.writeJson(path, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path,\n detail: `${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n\n /** Get-or-create config.mcp.servers as a mutable object. */\n private nestedServersBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) {\n mcp = {};\n cfg[MCP_ROOT_KEY] = mcp;\n }\n const mcpObj = mcp as Record<string, unknown>;\n let servers = mcpObj[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) {\n servers = {};\n mcpObj[MCP_SERVERS_KEY] = servers;\n }\n return servers as Record<string, unknown>;\n }\n\n // ── Hook install / uninstall (ts-plugin + DUAL REGISTRATION) ────────────\n\n installHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n\n // `hooks: false` disables only the CANONICAL (normalized) events; nativeHooks\n // is a sibling, openclaw-scoped declaration that SURVIVES the opt-out (mirrors\n // omp/opencode). The canonicalOff guard in buildPluginSource still suppresses\n // the canonical api.on handlers, so a hooks:false + nativeHooks connector\n // wires ONLY the native passthrough.\n const canonicalDisabled = ctx.connector.platforms[this.id]?.hooks === false;\n const hasCanonical = !canonicalDisabled && ctx.connector.hookEvents.length > 0;\n const hasNative = this.nativeHookEvents(ctx).length > 0;\n\n // Empty-events gate: the plugin module is SHARED with the action surface, so\n // a connector with actions-but-no-hooks still needs it written + registered —\n // but that is installActions' job (it calls ensurePlugin too). installHooks\n // only writes when there ARE hooks (canonical or native); its skip detail\n // stays honest about why.\n if (!hasCanonical && !hasNative) {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: pluginPath,\n detail: canonicalDisabled\n ? `hooks disabled for ${this.id}`\n : this.actionTriggers(ctx).length > 0\n ? \"no hooks (plugin written for actions)\"\n : \"connector declares no hooks\",\n },\n ];\n }\n\n return this.ensurePlugin(ctx, `openclaw plugin module (${this.hookDetail(ctx)})`);\n }\n\n // ── Action install / uninstall (slash commands in the shared plugin) ────\n\n /**\n * Each declared action becomes an `api.registerCommand(...)` inside the SAME\n * synthesized plugin module (see buildPluginSource). So installActions ensures\n * that shared module is on disk AND dual-registered: when hooks already wrote\n * the identical bytes the content-compare yields \"skip\" (no double material\n * write), and an actions-only connector gets the plugin written + registered\n * here. Honors platforms.<id>.actions === false and the empty-actions skip.\n */\n override installActions(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n if (ctx.connector.platforms[this.id]?.actions === false) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: `actions disabled for ${this.id}` },\n ];\n }\n if (this.actionTriggers(ctx).length === 0) {\n return [\n { platform: this.id, action: \"skip\", path: pluginPath, detail: \"connector declares no actions\" },\n ];\n }\n return this.ensurePlugin(ctx, \"openclaw plugin module (action commands)\");\n }\n\n /**\n * Actions live INSIDE the shared plugin module, which uninstallHooks removes\n * (file + manifest + dual-registration entry) unconditionally — it runs last\n * in the teardown pass. Removing the plugin here too would race uninstallHooks,\n * so this is a single informational skip.\n */\n override uninstallActions(ctx: InstallContext): ChangeRecord[] {\n return [\n {\n platform: this.id,\n action: \"skip\",\n path: this.getHookConfigPath(ctx),\n detail: \"action commands are embedded in the plugin module; removed by uninstallHooks\",\n },\n ];\n }\n\n /**\n * Synthesize + content-compare-write the plugin module(s) AND upsert the dual-\n * registration entry. Shared by installHooks and installActions: the generator\n * is a pure function of ctx, so whichever surface runs second sees byte-\n * identical files (and an already-present entry) and reports \"skip\" — no double\n * material write. `moduleDetail` flavors the module ChangeRecord per surface.\n */\n private ensurePlugin(ctx: InstallContext, moduleDetail: string): ChangeRecord[] {\n const configPath = this.getServerConfigPath(ctx);\n const changes: ChangeRecord[] = [];\n\n // 1. Write the synthesized plugin module(s).\n for (const file of this.synthesizePlugin(ctx)) {\n changes.push(\n this.writeManagedFile(file.path, file.contents, ctx.dryRun, moduleDetail, file.executable),\n );\n }\n\n // 2. DUAL REGISTRATION half (a): enable plugins.entries.<id> AND add the\n // plugin DIR to plugins.load.paths so the gateway discovers + loads the\n // module. (half (b), mcp.servers.<id>, is written by installServer — both\n // are required for tools to reach the agent.)\n changes.push(this.upsertPluginEntry(configPath, ctx, this.pluginDir(ctx)));\n\n return changes;\n }\n\n /**\n * Human-facing summary of which declared events the synthesized module ACTUALLY\n * wires. Only events present in EVENT_TO_OPENCLAW are mapped/wired; any declared\n * event with no OpenClaw mapping (e.g. UserPromptSubmit) is reported separately\n * as \"unsupported here\" so the detail never overstates coverage.\n */\n private hookDetail(ctx: InstallContext): string {\n const declared =\n ctx.connector.platforms[this.id]?.hooks === false\n ? []\n : ctx.connector.hookEvents;\n const mapped = declared.filter((e) => EVENT_TO_OPENCLAW[e] !== undefined);\n const unsupported = declared.filter((e) => EVENT_TO_OPENCLAW[e] === undefined);\n const native = this.nativeHookEvents(ctx);\n const parts: string[] = [];\n if (mapped.length > 0) parts.push(mapped.join(\",\"));\n if (native.length > 0) parts.push(`native: ${native.join(\",\")}`);\n const base = parts.join(\"; \") || \"no canonical hooks\";\n return unsupported.length > 0\n ? `${base}; unsupported here: ${unsupported.join(\",\")}`\n : base;\n }\n\n /**\n * OpenClaw-native passthrough events declared on platforms.<id>.nativeHooks\n * (host lifecycle hook names with no canonical HookEventName). Emitted as\n * generic on(...) bridge registrations and dispatched host-generically by the\n * home-bin's runNativeHook. Independent of hooks:false (supportsNativeHooks).\n */\n private nativeHookEvents(ctx: InstallContext): string[] {\n return Object.keys(ctx.connector.platforms[this.id]?.nativeHooks ?? {});\n }\n\n uninstallHooks(ctx: InstallContext): ChangeRecord[] {\n const pluginPath = this.getHookConfigPath(ctx);\n const configPath = this.getServerConfigPath(ctx);\n const changes: ChangeRecord[] = [];\n\n // 1. Remove the plugins.entries.<id> reference AND drop the plugin dir from\n // plugins.load.paths.\n changes.push(\n this.removePluginEntry(configPath, ctx.connector.id, this.pluginDir(ctx), ctx.dryRun),\n );\n\n // 2. Remove the plugin module + its manifest on disk.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n for (const [path, label] of [\n [pluginPath, \"openclaw plugin module\"],\n [manifestPath, \"openclaw plugin manifest\"],\n ] as const) {\n if (existsSync(path)) {\n changes.push(this.removeManagedFile(path, ctx.dryRun, label, `no ${label} present`));\n } else {\n changes.push({\n platform: this.id,\n action: \"skip\",\n path,\n detail: `no ${label} present`,\n });\n }\n }\n\n // 3. Drop the now-empty plugin dir (only if WE own its full contents).\n changes.push(this.removeDirIfEmpty(this.pluginDir(ctx), ctx.dryRun));\n\n return changes;\n }\n\n /**\n * Upsert plugins.entries.<id> = { enabled: true } AND add `pluginDir` to\n * plugins.load.paths idempotently.\n *\n * `openclaw config validate` rejects a per-entry `module` field, so the entry\n * carries ONLY `{ enabled: true }`. The gateway discovers the synthesized\n * module by scanning the directories in plugins.load.paths (the dir holding\n * index.mjs + openclaw.plugin.json), so the dir is added there.\n */\n private upsertPluginEntry(\n configPath: string,\n ctx: InstallContext,\n pluginDir: string,\n ): ChangeRecord {\n // OVERWRITE GUARD: never blank a present-but-unparseable openclaw.json.\n if (this.isPresentButUnparseable(configPath)) {\n return {\n platform: this.id,\n action: \"warn\",\n path: configPath,\n detail: `existing ${configPath} is not parseable; left untouched (back it up / fix it, then re-run)`,\n };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath) ?? {};\n const entries = this.pluginEntriesBucket(cfg);\n const id = ctx.connector.id;\n const desired: OpenClawPluginEntry = { enabled: true };\n const entryBefore = JSON.stringify(entries[id]);\n const entryAfter = JSON.stringify(desired);\n\n // plugins.load.paths: add the plugin dir if absent.\n const loadPaths = this.pluginLoadPathsBucket(cfg);\n const pathPresent = loadPaths.includes(pluginDir);\n\n const entryChanged = entryBefore !== entryAfter;\n const pathChanged = !pathPresent;\n let action: ChangeRecord[\"action\"];\n if (entryBefore === undefined) action = \"create\";\n else if (!entryChanged && !pathChanged) action = \"skip\";\n else action = \"update\";\n\n if (action !== \"skip\") {\n entries[id] = desired;\n if (!pathPresent) loadPaths.push(pluginDir);\n this.writeJson(configPath, cfg, ctx.dryRun);\n }\n return {\n platform: this.id,\n action,\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Remove plugins.entries.<id> AND drop `pluginDir` from plugins.load.paths. */\n private removePluginEntry(\n configPath: string,\n id: string,\n pluginDir: string,\n dryRun: boolean,\n ): ChangeRecord {\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n const plugins = cfg?.[PLUGINS_KEY];\n const pluginsObj =\n plugins && typeof plugins === \"object\" && !Array.isArray(plugins)\n ? (plugins as Record<string, unknown>)\n : undefined;\n const entries = pluginsObj?.[PLUGINS_ENTRIES_KEY] as\n | Record<string, unknown>\n | undefined;\n const load = pluginsObj?.[PLUGINS_LOAD_KEY] as Record<string, unknown> | undefined;\n const loadPaths =\n load && Array.isArray(load[PLUGINS_LOAD_PATHS_KEY])\n ? (load[PLUGINS_LOAD_PATHS_KEY] as unknown[])\n : undefined;\n\n const entryPresent = Boolean(entries && id in entries);\n const pathIdx = loadPaths ? loadPaths.indexOf(pluginDir) : -1;\n\n if (!cfg || (!entryPresent && pathIdx < 0)) {\n return {\n platform: this.id,\n action: \"skip\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} absent`,\n };\n }\n if (entryPresent && entries) delete entries[id];\n if (loadPaths && pathIdx >= 0) loadPaths.splice(pathIdx, 1);\n this.writeJson(configPath, cfg, dryRun);\n return {\n platform: this.id,\n action: \"remove\",\n path: configPath,\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}`,\n };\n }\n\n /** Get-or-create config.plugins.entries as a mutable object. */\n private pluginEntriesBucket(cfg: Record<string, unknown>): Record<string, unknown> {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let entries = pluginsObj[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\n entries = {};\n pluginsObj[PLUGINS_ENTRIES_KEY] = entries;\n }\n return entries as Record<string, unknown>;\n }\n\n /** Get-or-create config.plugins.load.paths as a mutable string array. */\n private pluginLoadPathsBucket(cfg: Record<string, unknown>): string[] {\n let plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) {\n plugins = {};\n cfg[PLUGINS_KEY] = plugins;\n }\n const pluginsObj = plugins as Record<string, unknown>;\n let load = pluginsObj[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) {\n load = {};\n pluginsObj[PLUGINS_LOAD_KEY] = load;\n }\n const loadObj = load as Record<string, unknown>;\n const existing = loadObj[PLUGINS_LOAD_PATHS_KEY];\n // Normalize to a fresh string[] (drop any non-string a user may have placed\n // here) and write it back so the caller mutates the live array.\n const paths = Array.isArray(existing)\n ? existing.filter((p): p is string => typeof p === \"string\")\n : [];\n loadObj[PLUGINS_LOAD_PATHS_KEY] = paths;\n return paths;\n }\n\n // ── Content surface: skills (dir-per-skill SKILL.md) ────────────────────\n // CONTENT-ONLY: pure native-file writers, mirroring codex/claude-code\n // installSkills + renderSkill. No runtime dispatch, no home-bin pointer, no\n // telemetry wrap. Each write is idempotent (byte-identical → skip) via\n // writeContentFile and reversible via removeContentFile + removeDirIfEmpty\n // (never rm -rf a shared dir). Honors platforms[\"openclaw\"].skills === false.\n //\n // Native locations (AgentSkills spec — dir-per-skill SKILL.md + resources):\n // user → ~/.openclaw/skills/<name>/SKILL.md (`openclaw skills install\n // --global` targets this dir)\n // project → <workspace>/skills/<name>/SKILL.md (the highest-priority\n // documented skill root; <workspace> is the SAME root the memory\n // surface resolves, so both content surfaces stay consistent).\n\n /**\n * Per-skill directory for ctx.scope:\n * user → ~/.openclaw/skills/<name> (`--global` install target)\n * project → <workspace>/skills/<name> (resolved agent workspace —\n * consistent with this adapter's memoryTargets resolution).\n */\n private skillDir(ctx: InstallContext, name: string): string {\n const base =\n ctx.scope === \"project\"\n ? join(this.resolveWorkspace(), \"skills\")\n : join(dirname(resolveOpenClawConfigPath()), \"skills\");\n return join(base, name);\n }\n\n override installSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.platforms[this.id]?.skills === false) {\n return [{ platform: this.id, action: \"skip\", detail: `skills disabled for ${this.id}` }];\n }\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n changes.push(\n this.writeContentFile(join(dir, \"SKILL.md\"), renderSkillMd(skill), ctx.dryRun),\n );\n // Bundle any resource files beside SKILL.md (relative path → contents).\n // Defense-in-depth: skip+warn on any key that escapes the skill dir\n // (config-time validation already rejects these, but never trust input).\n for (const [rel, contents] of Object.entries(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) {\n changes.push({\n platform: this.id,\n action: \"warn\",\n detail: `skill resource \"${rel}\" escapes the skill dir; skipped`,\n });\n continue;\n }\n changes.push(this.writeContentFile(target, contents, ctx.dryRun));\n }\n }\n return changes;\n }\n\n override uninstallSkills(ctx: InstallContext): ChangeRecord[] {\n const { connector } = ctx;\n if (connector.skills.length === 0) {\n return [{ platform: this.id, action: \"skip\", detail: \"connector declares no skills\" }];\n }\n const changes: ChangeRecord[] = [];\n for (const skill of connector.skills) {\n const dir = this.skillDir(ctx, skill.name);\n // Remove only the files we wrote (SKILL.md + declared resources), then the\n // skill dir itself when we own its full contents.\n changes.push(this.removeContentFile(join(dir, \"SKILL.md\"), ctx.dryRun));\n for (const rel of Object.keys(skill.resources ?? {})) {\n const target = this.resolveWithin(dir, rel);\n if (target === null) continue; // never delete outside the skill dir\n changes.push(this.removeContentFile(target, ctx.dryRun));\n }\n // Only remove the skill dir when WE own its full contents — never rm -rf a\n // dir that still holds user-added / sibling-tool / shared files.\n changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));\n }\n return changes;\n }\n\n // ── ts-plugin synthesis ────────────────────────────────────────────────\n\n /**\n * Build ONE self-contained ESM plugin module for OpenClaw.\n *\n * The module imports nothing from agent-connector. It embeds two constants\n * (the absolute home-bin path and the connector id) and a `bridge()` helper\n * that shells out to the universal hook entrypoint via execFileSync, feeding\n * the OpenClaw event payload on stdin and JSON.parsing the normalized\n * HookResponse back from stdout (fail-open: any error → null). Its default\n * export is the OpenClaw plugin definition { id, name, configSchema,\n * register(api) }; register wires api.on(...) handlers for the events this\n * connector actually declares.\n */\n synthesizePlugin(ctx: InstallContext): GeneratedPluginFile[] {\n const path = this.getHookConfigPath(ctx);\n const contents = this.buildPluginSource(ctx);\n // The gateway scans plugins.load.paths dirs for a manifest, so emit an\n // openclaw.plugin.json beside index.mjs pointing at the module entry.\n const manifestPath = join(this.pluginDir(ctx), PLUGIN_MANIFEST_FILE);\n const manifest = this.buildPluginManifest(ctx);\n return [\n { path, contents, executable: false },\n { path: manifestPath, contents: manifest, executable: false },\n ];\n }\n\n /**\n * Build the openclaw.plugin.json manifest the gateway reads inside a\n * plugins.load.paths directory. It names the plugin and points at the ESM\n * module entry (index.mjs) so the loader knows what to import.\n *\n * `configSchema` is REQUIRED: `openclaw config validate` (and `openclaw mcp\n * list`) REJECT a manifest without it (\"plugin manifest requires\n * configSchema\"). Our plugin takes no user config, so we emit the empty\n * closed object schema from openclaw's own minimal manifest example\n * ({ type: \"object\", additionalProperties: false }).\n */\n private buildPluginManifest(ctx: InstallContext): string {\n const manifest = {\n id: ctx.connector.id,\n name: ctx.connector.displayName || ctx.connector.id,\n main: this.pluginFileName(),\n enabled: true,\n configSchema: { type: \"object\", additionalProperties: false },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n }\n\n /** Compose the generated plugin source with plain string concatenation. */\n private buildPluginSource(ctx: InstallContext): string {\n const homeBin = JSON.stringify(ctx.homeBinPath);\n const connectorId = JSON.stringify(ctx.connector.id);\n const connectorName = JSON.stringify(ctx.connector.displayName || ctx.connector.id);\n\n // `platforms[host].hooks === false` disables the CANONICAL events. The\n // plugin module may still be synthesized for ACTIONS (installActions writes\n // it independently of hooks), so the canonical handler set collapses to []\n // here rather than letting a live before_tool_call/after_tool_call gate leak\n // into a hooks-disabled plugin. Mirrors omp/opencode. Uses this.id so the\n // NemoClaw fork checks platforms.nemoclaw.hooks.\n const canonicalOff = ctx.connector.platforms[this.id]?.hooks === false;\n const events = canonicalOff\n ? []\n : ctx.connector.hookEvents.filter(\n (e): e is HookEventName => EVENT_TO_OPENCLAW[e] !== undefined,\n );\n const has = (e: HookEventName) => events.includes(e);\n\n const header =\n \"/**\\n\" +\n \" * AUTO-GENERATED by agent-connector — DO NOT EDIT.\\n\" +\n \" *\\n\" +\n \" * Self-contained OpenClaw plugin bridge for connector \" +\n ctx.connector.id +\n \".\\n\" +\n \" * It imports nothing from agent-connector: every hook invocation shells out\\n\" +\n \" * to the stable home binary's universal entrypoint and JSON-parses the\\n\" +\n \" * normalized response. Fail-open: any bridge error degrades to a no-op.\\n\" +\n \" *\\n\" +\n \" * Registered via DUAL REGISTRATION in openclaw.json: plugins.entries.<id>\\n\" +\n \" * (loads this module) + mcp.servers.<id> (surfaces the MCP tools). The\\n\" +\n \" * gateway hot-reloads its config on SIGUSR1.\\n\" +\n \" */\\n\" +\n 'import { execFileSync, execSync } from \"node:child_process\";\\n\\n' +\n \"const HOME_BIN = \" +\n homeBin +\n \";\\n\" +\n \"const CONNECTOR_ID = \" +\n connectorId +\n \";\\n\\n\" +\n \"/**\\n\" +\n \" * Invoke the universal hook entrypoint for one canonical event.\\n\" +\n \" * @param {string} event canonical (or native passthrough) event name\\n\" +\n \" * (PreToolUse|PostToolUse|SessionStart|UserPromptSubmit|SubagentStart|SubagentStop)\\n\" +\n \" * @param {object} payload OpenClaw-shaped payload posted on stdin\\n\" +\n \" * @returns {object|null} normalized HookResponse, or null on any failure\\n\" +\n \" */\\n\" +\n \"function bridge(event, payload) {\\n\" +\n \" try {\\n\" +\n \" // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot\\n\" +\n \" // execFile a batch file, and shell+args is deprecated (DEP0190), so run\\n\" +\n \" // one quoted command line via a shell. POSIX keeps the direct execFile.\\n\" +\n ' const args = [\"hook\", ' + JSON.stringify(this.id) + ', event, \"--connector\", CONNECTOR_ID];\\n' +\n ' const opts = { input: JSON.stringify(payload), encoding: \"utf8\" };\\n' +\n \" const stdout =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), opts)\\n\" +\n \" : execFileSync(HOME_BIN, args, opts);\\n\" +\n ' const text = (stdout || \"\").trim();\\n' +\n ' if (text === \"\") return { decision: \"allow\" };\\n' +\n \" return JSON.parse(text);\\n\" +\n \" } catch {\\n\" +\n \" // Fail-open — never wedge a tool call on a bridge error.\\n\" +\n \" return null;\\n\" +\n \" }\\n\" +\n \"}\\n\\n\" +\n \"// Module-scoped context the prompt-build hook injects at session start.\\n\" +\n \"// before_prompt_build is the verified injection point (session_start itself\\n\" +\n \"// returns no context payload). The flag prevents double-injection.\\n\" +\n \"let PROJECT_DIR = process.cwd();\\n\" +\n \"let SESSION_ID = \\\"\\\";\\n\" +\n \"let pendingContext = null;\\n\" +\n \"let contextInjected = false;\\n\\n\";\n\n // register(api) body — wire only the handlers this connector declares.\n const reg: string[] = [];\n\n reg.push(\n \" register(api) {\\n\" +\n \" PROJECT_DIR = process.cwd();\\n\" +\n \" const on = (event, handler) => {\\n\" +\n ' if (typeof api?.on === \"function\") {\\n' +\n \" try { api.on(event, handler); return; } catch { /* fall through */ }\\n\" +\n \" }\\n\" +\n ' if (typeof api?.registerHook === \"function\") {\\n' +\n \" try {\\n\" +\n ' api.registerHook(event, handler, { name: CONNECTOR_ID + \":\" + event, description: \"agent-connector bridge\" });\\n' +\n \" } catch { /* best effort */ }\\n\" +\n \" }\\n\" +\n \" };\\n\",\n );\n\n if (has(\"PreToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PreToolUse → before_tool_call: block (return { block, blockReason })\\n\" +\n \" // for deny/ask; rewrite args by mutating event.params in place for modify.\\n\" +\n ' on(\"before_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const toolInput = e.params || {};\\n\" +\n \" const res = bridge(\\\"PreToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" if (!res) return undefined;\\n\" +\n \" // OpenClaw has no separate ask gate — deny and ask both block (safe).\\n\" +\n ' if (res.decision === \"deny\" || res.decision === \"ask\") {\\n' +\n ' return { block: true, blockReason: res.reason || (\"Blocked by \" + CONNECTOR_ID) };\\n' +\n \" }\\n\" +\n ' if (res.decision === \"modify\" && res.updatedInput && typeof toolInput === \"object\") {\\n' +\n \" Object.assign(toolInput, res.updatedInput);\\n\" +\n \" }\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"PostToolUse\")) {\n reg.push(\n \"\\n\" +\n \" // PostToolUse → after_tool_call: observe the completed tool call.\\n\" +\n \" // OpenClaw exposes the result (v2+) or output (older builds); it cannot\\n\" +\n \" // rewrite the tool result, so updatedOutput is intentionally dropped.\\n\" +\n ' on(\"after_tool_call\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" const rawResult = e.result !== undefined ? e.result : e.output;\\n\" +\n \" const toolOutput =\\n\" +\n ' typeof rawResult === \"string\"\\n' +\n \" ? rawResult\\n\" +\n \" : rawResult != null\\n\" +\n \" ? JSON.stringify(rawResult)\\n\" +\n \" : undefined;\\n\" +\n \" bridge(\\\"PostToolUse\\\", {\\n\" +\n ' toolName: e.toolName || \"\",\\n' +\n \" toolInput: e.params || {},\\n\" +\n \" toolOutput,\\n\" +\n \" isError: Boolean(e.error || e.isError),\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStart\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStart → subagent_spawned: observe-only (OpenClaw exposes no\\n\" +\n \" // decision or context-injection payload for subagent launches).\\n\" +\n \" // PRIMARY SOURCE (openclaw/openclaw src/plugins/hook-types.ts:720-806,\\n\" +\n \" // PluginHookSubagentSpawnedEvent = PluginHookSubagentSpawnBase): the\\n\" +\n \" // native event carries `agentId` (the agent identity) + an OPTIONAL\\n\" +\n \" // human `label` — and NO subagentId/subagentType/agent/agentType. So we\\n\" +\n \" // read the real e.agentId and surface the real e.label as the type-ish\\n\" +\n \" // descriptor; the former subagentId/subagentType/agent reads were\\n\" +\n \" // false-friends (host emits none). Empty values are omitted so the\\n\" +\n \" // dispatcher never matcher-filters on an empty descriptor (fail-open).\\n\" +\n ' on(\"subagent_spawned\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" bridge(\\\"SubagentStart\\\", {\\n\" +\n \" agentId: e.agentId || undefined,\\n\" +\n \" agentType: e.label || undefined,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SubagentStop\")) {\n reg.push(\n \"\\n\" +\n \" // SubagentStop → subagent_ended: observe-only completion. A normalized\\n\" +\n \" // 'deny' cannot keep the subagent running on OpenClaw, so the bridge\\n\" +\n \" // reply is intentionally ignored.\\n\" +\n \" // PRIMARY SOURCE (openclaw/openclaw src/plugins/hook-types.ts:808-818\\n\" +\n \" // PluginHookSubagentEndedEvent + wired-hooks-subagent.test.ts:118-126):\\n\" +\n \" // the native event's ONLY identity is `targetSessionKey` — it carries\\n\" +\n \" // NO agentId/subagentId, NO agentType/subagentType/agent, and NO\\n\" +\n \" // result/output. The old e.agentId||e.subagentId / e.agentType||… /\\n\" +\n \" // e.result||e.output reads were ALL false-friends (host emits none →\\n\" +\n \" // agentId was always undefined). We read the real e.targetSessionKey as\\n\" +\n \" // agentId and omit the type/message slots the host never provides.\\n\" +\n ' on(\"subagent_ended\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" bridge(\\\"SubagentStop\\\", {\\n\" +\n \" agentId: e.targetSessionKey || undefined,\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n if (has(\"SessionStart\")) {\n reg.push(\n \"\\n\" +\n \" // SessionStart → session_start records the real session id and fetches\\n\" +\n \" // the additionalContext to inject; before_prompt_build performs the\\n\" +\n \" // injection (the verified context-injection point upstream).\\n\" +\n ' on(\"session_start\", async (event) => {\\n' +\n \" const e = event || {};\\n\" +\n \" SESSION_ID = e.sessionId || e.sessionKey || SESSION_ID;\\n\" +\n \" const res = bridge(\\\"SessionStart\\\", {\\n\" +\n \" sessionId: SESSION_ID,\\n\" +\n ' source: e.resumedFrom ? \"resume\" : \"startup\",\\n' +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n \" contextInjected = false;\\n\" +\n \" pendingContext =\\n\" +\n ' res && typeof res.additionalContext === \"string\" && res.additionalContext\\n' +\n \" ? res.additionalContext\\n\" +\n \" : null;\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n // before_prompt_build fires on EVERY prompt build. ONE handler serves both\n // context-injecting events that target it — they COEXIST via SEPARATE state\n // so the once-only SessionStart flag never suppresses the per-turn injection:\n // SessionStart → inject pendingContext ONCE (first build), as system ctx.\n // UserPromptSubmit → bridge EVERY turn, append the returned additionalContext.\n // before_prompt_build has NO blocking ability, so a UserPromptSubmit block/deny\n // decision DEGRADES to a no-op (context-injection only — never claim a block).\n if (has(\"SessionStart\") || has(\"UserPromptSubmit\")) {\n let body = \"\";\n if (has(\"SessionStart\")) {\n body +=\n \" // SessionStart: inject the session-start context exactly once.\\n\" +\n \" if (!contextInjected && pendingContext) {\\n\" +\n \" contextInjected = true;\\n\" +\n \" out.appendSystemContext = pendingContext;\\n\" +\n \" }\\n\";\n }\n if (has(\"UserPromptSubmit\")) {\n body +=\n \" // UserPromptSubmit: per-turn context injection. before_prompt_build\\n\" +\n \" // cannot block, so a deny/block decision degrades to a no-op.\\n\" +\n \" const e = event || {};\\n\" +\n \" const ures = bridge(\\\"UserPromptSubmit\\\", {\\n\" +\n ' prompt: typeof e.prompt === \"string\" ? e.prompt : \"\",\\n' +\n \" sessionId: SESSION_ID,\\n\" +\n \" projectDir: PROJECT_DIR,\\n\" +\n \" });\\n\" +\n ' if (ures && typeof ures.additionalContext === \"string\" && ures.additionalContext) {\\n' +\n \" out.appendContext = ures.additionalContext;\\n\" +\n \" }\\n\";\n }\n reg.push(\n \"\\n\" +\n \" // before_prompt_build → coexisting SessionStart (once) + UserPromptSubmit\\n\" +\n \" // (per-turn) context injection. Context-only: it cannot block a prompt.\\n\" +\n ' on(\"before_prompt_build\", (event) => {\\n' +\n \" const out = {};\\n\" +\n body +\n \" return Object.keys(out).length > 0 ? out : undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n // NATIVE passthrough events: OpenClaw lifecycle hook names with no canonical\n // analog, declared on platforms.<id>.nativeHooks. Each registers an on(...)\n // handler that bridges the NATIVE event name verbatim to the home-bin →\n // runNativeHook dispatches it host-generically. Read straight off\n // platforms.<id>.nativeHooks (independent of hookEvents AND of canonicalOff),\n // so they install even with hooks:false. Mirrors omp/opencode.\n for (const ev of this.nativeHookEvents(ctx)) {\n const key = JSON.stringify(ev);\n reg.push(\n \"\\n\" +\n \" // nativeHooks passthrough → \" + ev + \"\\n\" +\n \" on(\" + key + \", (event) => {\\n\" +\n \" bridge(\" + key + \", { sessionId: SESSION_ID, projectDir: PROJECT_DIR, raw: event });\\n\" +\n \" return undefined;\\n\" +\n \" });\\n\",\n );\n }\n\n // Action surface: one api.registerCommand per declared action, registered in\n // the SAME register(api) body. The command shells out to the home-bin\n // `action <host>` verb with the host token baked as this.id (so a NemoClaw\n // fork emits `action nemoclaw`, NOT openclaw — exactly like the hook bridge\n // above). The win32 branch reuses the hook bridge's quoted-shell form (a .cmd\n // launcher throws EINVAL via execFileSync). Both id AND description are\n // JSON.stringify'd — descriptions are free-form connector text, so a raw \" /\n // backtick would break the generated module.\n for (const action of this.actionTriggers(ctx)) {\n const id = JSON.stringify(action.id);\n const description = JSON.stringify(action.description);\n reg.push(\n \"\\n\" +\n \" // Action /\" + action.id + \" -> run the home-bin action verb directly.\\n\" +\n \" api.registerCommand({\\n\" +\n \" name: \" + id + \",\\n\" +\n \" description: \" + description + \",\\n\" +\n \" handler: async (_ctx) => {\\n\" +\n \" try {\\n\" +\n ' const args = [\"action\", ' + JSON.stringify(this.id) + \", \" + id + ', \"--connector\", CONNECTOR_ID];\\n' +\n \" const out =\\n\" +\n ' process.platform === \"win32\"\\n' +\n \" ? execSync([HOME_BIN, ...args].map((a) => '\\\"' + a + '\\\"').join(\\\" \\\"), { encoding: \\\"utf8\\\" })\\n\" +\n ' : execFileSync(HOME_BIN, args, { encoding: \"utf8\" });\\n' +\n ' return { text: (out || \"\").trim() };\\n' +\n \" } catch (e) {\\n\" +\n \" return { text: \\\"action \\\" + \" + id + \" + \\\" failed: \\\" + (e && e.message ? e.message : \\\"error\\\") };\\n\" +\n \" }\\n\" +\n \" },\\n\" +\n \" });\\n\",\n );\n }\n\n reg.push(\" },\\n\");\n\n const definition =\n \"const plugin = {\\n\" +\n \" id: CONNECTOR_ID,\\n\" +\n \" name: \" +\n connectorName +\n \",\\n\" +\n \" configSchema: {\\n\" +\n ' type: \"object\",\\n' +\n \" properties: {\\n\" +\n \" enabled: {\\n\" +\n ' type: \"boolean\",\\n' +\n \" default: true,\\n\" +\n ' description: \"Enable or disable the \" + CONNECTOR_ID + \" plugin.\",\\n' +\n \" },\\n\" +\n \" },\\n\" +\n \" additionalProperties: false,\\n\" +\n \" },\\n\" +\n reg.join(\"\") +\n \"};\\n\\n\" +\n \"export default plugin;\\n\";\n\n return header + definition;\n }\n\n // ── Runtime: parse OUR bridge payload → normalized event ───────────────\n\n /**\n * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):\n * { toolName, toolInput, toolOutput?, isError?, sessionId, source?, projectDir }\n * so this maps straight through.\n */\n parseEvent(event: HookEventName, raw: unknown): NormalizedEvent {\n const input = (raw ?? {}) as OpenClawBridgePayload;\n const base = {\n hostPlatform: this.id,\n connectorId: \"\",\n sessionId: typeof input.sessionId === \"string\" ? input.sessionId : \"\",\n raw,\n ...(typeof input.projectDir === \"string\" ? { projectDir: input.projectDir } : {}),\n } as const;\n\n switch (event) {\n case \"PreToolUse\": {\n const ev: PreToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n };\n return ev;\n }\n case \"PostToolUse\": {\n const ev: PostToolUseEvent = {\n ...base,\n toolName: input.toolName ?? \"\",\n toolInput: input.toolInput ?? {},\n ...(typeof input.toolOutput === \"string\" ? { toolOutput: input.toolOutput } : {}),\n ...(typeof input.isError === \"boolean\" ? { isError: input.isError } : {}),\n };\n return ev;\n }\n case \"SessionStart\": {\n const source: SessionStartEvent[\"source\"] =\n input.source === \"resume\"\n ? \"resume\"\n : input.source === \"compact\"\n ? \"compact\"\n : input.source === \"clear\"\n ? \"clear\"\n : \"startup\";\n const ev: SessionStartEvent = { ...base, source };\n return ev;\n }\n case \"UserPromptSubmit\": {\n const ev: UserPromptSubmitEvent = {\n ...base,\n prompt: typeof input.prompt === \"string\" ? input.prompt : \"\",\n };\n return ev;\n }\n case \"SubagentStart\": {\n // Empty strings are dropped so the runtime matcher never filters a\n // real spawn on an unknown agent type (fail-open, per the dispatcher).\n const ev: SubagentStartEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n };\n return ev;\n }\n case \"SubagentStop\": {\n const ev: SubagentStopEvent = {\n ...base,\n ...(isNonEmptyString(input.agentId) ? { agentId: input.agentId } : {}),\n ...(isNonEmptyString(input.agentType)\n ? { agentType: input.agentType }\n : {}),\n ...(isNonEmptyString(input.lastAssistantMessage)\n ? { lastAssistantMessage: input.lastAssistantMessage }\n : {}),\n };\n return ev;\n }\n default:\n // Other canonical events are not surfaced by OpenClaw; treat as a\n // session-start-shaped no-op so the dispatcher fails open gracefully.\n return { ...base, source: \"startup\" } satisfies SessionStartEvent;\n }\n }\n\n // ── Runtime: normalized response → reply the generated bridge parses ───\n\n /**\n * Unlike json-stdio hosts (whose reply is the host's NATIVE control payload),\n * OUR generated bridge consumes this stdout directly. So the reply body is the\n * NORMALIZED HookResponse itself — the bridge JSON.parses it and reads\n * decision / updatedInput / additionalContext.\n */\n formatReply(_event: HookEventName, response: HookResponse): HookReply {\n return {\n exitCode: 0,\n stdout: JSON.stringify(response ?? { decision: \"allow\" }),\n };\n }\n\n // ── Diagnostics ────────────────────────────────────────────────────────\n\n override getHealthChecks(ctx: InstallContext): readonly HealthCheck[] {\n const configPath = this.getServerConfigPath(ctx);\n const pluginPath = this.getHookConfigPath(ctx);\n const pluginDir = this.pluginDir(ctx);\n const id = ctx.connector.id;\n // Actions are health-equivalent to hooks: both write the SAME plugin module\n // and the SAME dual-registration entry, so an actions-only connector must\n // still assert the plugin/extension is present + loaded.\n const hasHooks =\n ctx.connector.hookEvents.length > 0 ||\n this.nativeHookEvents(ctx).length > 0 ||\n this.actionTriggers(ctx).length > 0;\n const hasServer = Boolean(ctx.connector.server);\n\n return [\n {\n name: `${this.name}: openclaw.json present`,\n check: () =>\n existsSync(configPath)\n ? { status: \"OK\", detail: configPath }\n : { status: \"FAIL\", detail: `not found: ${configPath}` },\n },\n {\n // HARDEST PART — dual registration must be CONSISTENT. The plugin only\n // delivers tools when it is LOADED (plugins.entries.<id> enabled AND its\n // dir in plugins.load.paths) AND in mcp.servers. A config with one half\n // but not the other is a tool-less (or never-loaded) install, so FAIL on\n // loaded XOR mcp.servers. (We assert plugins.load.paths — NOT a per-entry\n // `module` field, which openclaw config validate rejects.)\n name: `${this.name}: dual registration (plugins.entries+load.paths + mcp.servers.${id})`,\n check: () => {\n // Only assert what the connector declares: a catalog-only connector\n // (no MCP server, no hooks) never writes either half of the dual\n // registration, so absence — even of the config file — is healthy.\n if (!hasServer && !hasHooks) {\n return { status: \"OK\", detail: \"no MCP server or hooks declared\" };\n }\n const cfg = this.readJson<Record<string, unknown>>(configPath);\n if (!cfg) return { status: \"FAIL\", detail: `cannot read ${configPath}` };\n\n const inEntries = this.hasPluginEntry(cfg, id) && this.hasLoadPath(cfg, pluginDir);\n const inMcp = this.hasMcpServer(cfg, id);\n\n // A hooks-only connector (no MCP server declared) does not need the\n // mcp.servers half — the load half alone is correct and complete.\n if (!hasServer) {\n return inEntries || !hasHooks\n ? { status: \"OK\", detail: \"hooks-only connector (no MCP server)\" }\n : {\n status: \"FAIL\",\n detail: `${id} not enabled in ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} + ${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} (plugin will not load)`,\n };\n }\n\n if (inEntries && inMcp) {\n return {\n status: \"OK\",\n detail: `${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}.${id}+${PLUGINS_KEY}.${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY} + ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}.${id}`,\n };\n }\n if (inEntries && !inMcp) {\n return {\n status: \"FAIL\",\n detail: `loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) but missing from ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} — plugin loads but no tools reach the agent`,\n };\n }\n if (!inEntries && inMcp) {\n return {\n status: \"FAIL\",\n detail: `in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY} but not loaded (missing ${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY} or ${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) — tools declared but plugin never loads`,\n };\n }\n return {\n status: \"FAIL\",\n detail: `not loaded (${PLUGINS_KEY}.${PLUGINS_ENTRIES_KEY}+${PLUGINS_LOAD_KEY}.${PLUGINS_LOAD_PATHS_KEY}) and not in ${MCP_ROOT_KEY}.${MCP_SERVERS_KEY}`,\n };\n },\n },\n {\n name: `${this.name}: plugin module present`,\n check: () => {\n if (!hasHooks) return { status: \"OK\", detail: \"no hooks declared\" };\n return existsSync(pluginPath)\n ? { status: \"OK\", detail: pluginPath }\n : { status: \"FAIL\", detail: `not found: ${pluginPath}` };\n },\n },\n ];\n }\n\n /** True when config.plugins.entries.<id> exists (and is not disabled). */\n private hasPluginEntry(cfg: Record<string, unknown>, id: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const entries = (plugins as Record<string, unknown>)[PLUGINS_ENTRIES_KEY];\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) return false;\n const entry = (entries as Record<string, unknown>)[id];\n if (entry === undefined) return false;\n // An explicit { enabled: false } means the gateway will not load it.\n return !(\n entry &&\n typeof entry === \"object\" &&\n (entry as Record<string, unknown>).enabled === false\n );\n }\n\n /** True when config.plugins.load.paths includes `pluginDir`. */\n private hasLoadPath(cfg: Record<string, unknown>, pluginDir: string): boolean {\n const plugins = cfg[PLUGINS_KEY];\n if (!plugins || typeof plugins !== \"object\" || Array.isArray(plugins)) return false;\n const load = (plugins as Record<string, unknown>)[PLUGINS_LOAD_KEY];\n if (!load || typeof load !== \"object\" || Array.isArray(load)) return false;\n const paths = (load as Record<string, unknown>)[PLUGINS_LOAD_PATHS_KEY];\n return Array.isArray(paths) && paths.includes(pluginDir);\n }\n\n /** True when config.mcp.servers.<id> exists. */\n private hasMcpServer(cfg: Record<string, unknown>, id: string): boolean {\n const mcp = cfg[MCP_ROOT_KEY];\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return false;\n const servers = (mcp as Record<string, unknown>)[MCP_SERVERS_KEY];\n if (!servers || typeof servers !== \"object\" || Array.isArray(servers)) return false;\n return id in (servers as Record<string, unknown>);\n }\n\n}\n\n/**\n * Resolve the openclaw.json the gateway actually loads (mirrors context-mode's\n * openclawConfigPath): $OPENCLAW_CONFIG_PATH, else $OPENCLAW_STATE_DIR/openclaw.json,\n * else ~/.openclaw/openclaw.json. The gateway never loads a CWD/project-local\n * openclaw.json for user scope, so this is the canonical user-scope path.\n */\nfunction resolveOpenClawConfigPath(env: NodeJS.ProcessEnv = process.env): string {\n if (env.OPENCLAW_CONFIG_PATH) return resolve(env.OPENCLAW_CONFIG_PATH);\n const stateDir = env.OPENCLAW_STATE_DIR\n ? resolve(env.OPENCLAW_STATE_DIR)\n : resolve(homedir(), \".openclaw\");\n return resolve(stateDir, \"openclaw.json\");\n}\n\n/** Narrow to a non-empty string (the bridge omits empties, but stay defensive). */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\nexport const adapter = new OpenClawAdapter();\nexport default adapter;\n"],"mappings":"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","names":["entry"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/logger.ts"],"sourcesContent":["/**\n * core/logger — minimal leveled logger. Writes human output to stderr so stdout\n * stays clean for machine-readable payloads (hook replies, --json output).\n */\n\nexport type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst ORDER: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\nfunction currentLevel(): LogLevel {\n const env = (process.env.AGENT_CONNECTOR_LOG ?? \"info\").toLowerCase();\n return (env in ORDER ? env : \"info\") as LogLevel;\n}\n\nfunction emit(level: Exclude<LogLevel, \"silent\">, args: unknown[]): void {\n if (ORDER[currentLevel()] < ORDER[level]) return;\n const prefix = { error: \"✗\", warn: \"!\", info: \"›\", debug: \"·\" }[level];\n // eslint-disable-next-line no-console\n console.error(prefix, ...args);\n}\n\nexport const log = {\n error: (...args: unknown[]) => emit(\"error\", args),\n warn: (...args: unknown[]) => emit(\"warn\", args),\n info: (...args: unknown[]) => emit(\"info\", args),\n debug: (...args: unknown[]) => emit(\"debug\", args),\n};\n"],"mappings":";;;AAOA,IAAM,QAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAAS,eAAyB;AAChC,QAAM,OAAO,QAAQ,IAAI,uBAAuB,QAAQ,YAAY;AACpE,SAAQ,OAAO,QAAQ,MAAM;AAC/B;AAEA,SAAS,KAAK,OAAoC,MAAuB;AACvE,MAAI,MAAM,aAAa,CAAC,IAAI,MAAM,KAAK,EAAG;AAC1C,QAAM,SAAS,EAAE,OAAO,UAAK,MAAM,KAAK,MAAM,UAAK,OAAO,OAAI,EAAE,KAAK;AAErE,UAAQ,MAAM,QAAQ,GAAG,IAAI;AAC/B;AAEO,IAAM,MAAM;AAAA,EACjB,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AAAA,EACjD,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,MAAM,IAAI,SAAoB,KAAK,QAAQ,IAAI;AAAA,EAC/C,OAAO,IAAI,SAAoB,KAAK,SAAS,IAAI;AACnD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/package-metadata.ts","../src/core/define-connector.ts","../src/core/mcp-standard.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\n\nimport type {\n McpPackageIdentity,\n ResolvedMcpPackageIdentity,\n ServerDef,\n} from \"./types.js\";\n\nconst GENERIC_PACKAGE_BASENAMES = new Set([\n \"mcp\",\n \"server\",\n \"mcp-server\",\n \"connector\",\n \"tool\",\n \"tools\",\n \"cli\",\n]);\n\nexport interface ConnectorPackageMetadata {\n /** Package-derived MCP identity used to derive host aliases and install ids. */\n mcp?: McpPackageIdentity;\n /** package.json version used as the connector version default. */\n version?: string;\n}\n\nconst PACKAGE_METADATA_CONTEXT = new AsyncLocalStorage<ConnectorPackageMetadata>();\n\nfunction kebab(raw: string): string {\n return raw\n .trim()\n .replace(/^@/, \"\")\n .replace(/[\\\\/_.\\s]+/g, \"-\")\n .replace(/[^a-zA-Z0-9-]+/g, \"-\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n}\n\nfunction scopedPackageParts(packageName: string): {\n scope?: string;\n basename: string;\n} {\n const raw = packageName.trim();\n if (raw.startsWith(\"@\")) {\n const [scope, basename] = raw.slice(1).split(\"/\");\n return { scope: scope ? kebab(scope) : undefined, basename: basename ?? \"\" };\n }\n return { basename: raw };\n}\n\nfunction cleanMcpBasename(raw: string): string {\n let name = kebab(raw);\n name = name.replace(/^mcp-server-/, \"\");\n name = name.replace(/^server-/, \"\");\n name = name.replace(/-mcp-server$/, \"\");\n name = name.replace(/-mcp$/, \"\");\n name = name.replace(/-server$/, \"\");\n return kebab(name);\n}\n\nfunction isGeneric(name: string | undefined): boolean {\n return !name || GENERIC_PACKAGE_BASENAMES.has(name);\n}\n\nfunction cleanString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction objectKeys(value: unknown): string[] {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return [];\n return Object.keys(value).filter((key) => key.trim());\n}\n\nfunction packageBinName(bin: unknown, packageName: string | undefined): string | undefined {\n if (typeof bin === \"string\") {\n return packageName ? deriveHostAliasFromPackageName(packageName) : undefined;\n }\n\n const keys = objectKeys(bin);\n if (keys.length === 0) return undefined;\n if (keys.length === 1) return keys[0];\n\n const packageAlias = packageName ? deriveHostAliasFromPackageName(packageName) : undefined;\n if (packageAlias && keys.includes(packageAlias)) return packageAlias;\n\n const unscoped = packageName?.startsWith(\"@\") ? packageName.split(\"/\")[1] : packageName;\n const cleanUnscoped = unscoped ? cleanMcpBasename(unscoped) : undefined;\n if (cleanUnscoped && keys.includes(cleanUnscoped)) return cleanUnscoped;\n\n return undefined;\n}\n\nexport function currentMcpPackageIdentity(): McpPackageIdentity | undefined {\n return currentConnectorPackageMetadata()?.mcp;\n}\n\nexport function currentConnectorPackageMetadata():\n | ConnectorPackageMetadata\n | undefined {\n return PACKAGE_METADATA_CONTEXT.getStore();\n}\n\nexport async function withConnectorPackageMetadata<T>(\n metadata: ConnectorPackageMetadata | undefined,\n run: () => Promise<T>,\n): Promise<T> {\n if (!metadata || Object.keys(metadata).length === 0) return run();\n return PACKAGE_METADATA_CONTEXT.run(metadata, run);\n}\n\nexport async function withMcpPackageIdentity<T>(\n identity: McpPackageIdentity | undefined,\n run: () => Promise<T>,\n): Promise<T> {\n if (!identity || Object.keys(identity).length === 0) return run();\n return withConnectorPackageMetadata({ mcp: identity }, run);\n}\n\nexport function findNearestPackageJson(startDir: string): string | undefined {\n let dir = resolve(startDir);\n for (;;) {\n const candidate = join(dir, \"package.json\");\n if (existsSync(candidate)) return candidate;\n\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nexport function readMcpPackageIdentityFromPackageJson(\n packageJsonPath: string,\n): McpPackageIdentity {\n return readConnectorPackageMetadataFromPackageJson(packageJsonPath).mcp ?? {};\n}\n\nexport function readConnectorPackageMetadataFromPackageJson(\n packageJsonPath: string,\n): ConnectorPackageMetadata {\n const parsed = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n name?: unknown;\n version?: unknown;\n mcpName?: unknown;\n mcp?: unknown;\n bin?: unknown;\n };\n const packageName = cleanString(parsed.name);\n const version = cleanString(parsed.version);\n const mcp =\n parsed.mcp && typeof parsed.mcp === \"object\" && !Array.isArray(parsed.mcp)\n ? (parsed.mcp as { name?: unknown; mcpName?: unknown })\n : undefined;\n const mcpName =\n cleanString(parsed.mcpName) ??\n cleanString(mcp?.mcpName) ??\n cleanString(mcp?.name);\n const bin = packageBinName(parsed.bin, packageName);\n\n const identity: McpPackageIdentity = {\n ...(packageName ? { packageName } : {}),\n ...(mcpName ? { mcpName } : {}),\n ...(bin ? { bin } : {}),\n };\n\n return {\n ...(Object.keys(identity).length > 0 ? { mcp: identity } : {}),\n ...(version ? { version } : {}),\n };\n}\n\nexport function readMcpPackageIdentityNearFile(\n filePath: string,\n): McpPackageIdentity | undefined {\n return readConnectorPackageMetadataNearFile(filePath)?.mcp;\n}\n\nexport function readConnectorPackageMetadataNearFile(\n filePath: string,\n): ConnectorPackageMetadata | undefined {\n const packageJsonPath = findNearestPackageJson(dirname(filePath));\n if (!packageJsonPath) return undefined;\n try {\n const metadata = readConnectorPackageMetadataFromPackageJson(packageJsonPath);\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Derive the host-facing MCP server key from a package name. */\nexport function deriveHostAliasFromPackageName(\n packageName: string,\n): string | undefined {\n const { scope, basename } = scopedPackageParts(packageName);\n const fromBasename = cleanMcpBasename(basename);\n if (!isGeneric(fromBasename)) return fromBasename;\n const fromScope = scope ? cleanMcpBasename(scope) : undefined;\n return isGeneric(fromScope) ? undefined : fromScope;\n}\n\n/** Derive the host-facing MCP server key from an MCP registry name. */\nexport function deriveHostAliasFromMcpName(\n mcpName: string,\n): string | undefined {\n const [namespace, name] = mcpName.trim().split(\"/\");\n const fromName = cleanMcpBasename(name ?? namespace ?? \"\");\n if (!isGeneric(fromName)) return fromName;\n\n const namespaceParts = (namespace ?? \"\").split(\".\").filter(Boolean);\n const fromNamespace = cleanMcpBasename(namespaceParts.at(-1) ?? \"\");\n return isGeneric(fromNamespace) ? undefined : fromNamespace;\n}\n\n/** Infer an npm package from common npx-style stdio server definitions. */\nexport function inferNpmPackageFromServer(\n server: ServerDef | undefined,\n): string | undefined {\n if (!server || server.transport !== \"stdio\") return undefined;\n if (typeof server.command !== \"string\" || server.command.trim() === \"\") {\n return undefined;\n }\n const runner = kebab(server.command.split(/[\\\\/]/).pop() ?? server.command);\n if (![\"npx\", \"npm\", \"pnpm\", \"yarn\", \"bun\"].includes(runner)) return undefined;\n\n const args = server.args ?? [];\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n if (arg === \"--\") continue;\n if (arg === \"exec\" || arg === \"dlx\" || arg === \"x\") continue;\n if (arg === \"-y\" || arg === \"--yes\" || arg === \"--no-install\") continue;\n if (arg === \"--package\" || arg === \"-p\") {\n i += 1;\n continue;\n }\n if (arg.startsWith(\"-\")) continue;\n return arg;\n }\n return undefined;\n}\n\nexport function resolveMcpPackageIdentity(input: {\n id?: string;\n mcp?: McpPackageIdentity;\n server?: ServerDef;\n}): ResolvedMcpPackageIdentity {\n const packageName =\n input.mcp?.packageName ?? inferNpmPackageFromServer(input.server);\n const hostAlias =\n input.mcp?.hostAlias ??\n (packageName ? deriveHostAliasFromPackageName(packageName) : undefined) ??\n (input.mcp?.mcpName\n ? deriveHostAliasFromMcpName(input.mcp.mcpName)\n : undefined) ??\n (input.mcp?.bin ? cleanMcpBasename(input.mcp.bin) : undefined);\n\n return {\n ...(packageName ? { packageName } : {}),\n ...(input.mcp?.mcpName ? { mcpName: input.mcp.mcpName } : {}),\n ...(input.mcp?.bin ? { bin: input.mcp.bin } : {}),\n ...(hostAlias ? { hostAlias } : {}),\n };\n}\n","/**\n * core/define-connector — the public write-once entry point.\n *\n * Validates a ConnectorConfig and normalizes it into a ResolvedConnector with\n * every optional field resolved to a default. Validation is intentionally\n * dependency-free (no zod) to keep the single-binary install lean.\n */\n\nimport { isAbsolute, normalize } from \"node:path\";\n\nimport type {\n ActionDef,\n CommandDef,\n ConfigPatchDef,\n ConnectorConfig,\n HookDefinition,\n HookEventName,\n HooksConfig,\n MemoryDef,\n NativeHookDef,\n PlatformId,\n PublishConfig,\n ResolvedConnector,\n SkillDef,\n StatuslineDef,\n SubagentDef,\n} from \"./types.js\";\nimport { REGISTERED_PLATFORM_IDS } from \"../adapters/registry.js\";\nimport { REGISTRY_NAMESPACE_RE } from \"./mcp-standard.js\";\nimport { CONNECTOR_ID_RE, isValidConnectorId } from \"./ids.js\";\nimport {\n currentConnectorPackageMetadata,\n resolveMcpPackageIdentity,\n} from \"./package-metadata.js\";\nimport {\n MANAGED_BLOCK_BEGIN_TOKEN,\n MANAGED_BLOCK_END_TOKEN,\n MEMORY_CONTENT_HARD_CAP_BYTES,\n} from \"./managed-block.js\";\nimport {\n CONFIG_PATCH_SEGMENT_RE,\n configPatchNamespaceViolation,\n isJsonValue,\n isValidConfigPatchKey,\n} from \"./config-patch-ledger.js\";\n\n/** kebab-case name regex shared by command/skill/subagent names. */\nconst SURFACE_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;\n\n/** Max length of a skill description (Agent Skills open standard). */\nconst SKILL_DESCRIPTION_MAX = 1024;\n\n// NOTE: canonical ordering — append new events at the END (hookEvents ordering\n// is pinned by tests and feeds install-file ordering). Exported so the\n// hook-dispatch gate + Claude-bundle event sets can be drift-tested against it.\nexport const ALL_EVENTS: HookEventName[] = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PreCompact\",\n \"Stop\",\n \"Notification\",\n \"PermissionRequest\",\n \"PostToolUseFailure\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PostCompact\",\n];\n\n/** Thrown on an invalid connector configuration. */\nexport class ConnectorConfigError extends Error {\n constructor(message: string) {\n super(`Invalid connector config: ${message}`);\n this.name = \"ConnectorConfigError\";\n }\n}\n\nfunction declaredEvents(hooks: HooksConfig | undefined): HookEventName[] {\n if (!hooks) return [];\n return ALL_EVENTS.filter((e) => typeof hooks[e]?.handler === \"function\");\n}\n\n/**\n * Validate + normalize a connector definition.\n * Returns a ResolvedConnector that adapters and the CLI consume directly.\n */\nexport function defineConnector(config: ConnectorConfig): ResolvedConnector {\n if (!config || typeof config !== \"object\") {\n throw new ConnectorConfigError(\"config must be an object\");\n }\n\n const packageMetadata = currentConnectorPackageMetadata();\n const packageIdentity = packageMetadata?.mcp;\n if (packageIdentity && Object.keys(packageIdentity).length > 0) {\n config = {\n ...config,\n mcp: {\n ...packageIdentity,\n ...config.mcp,\n },\n };\n }\n\n const mcpIdentity = resolveMcpPackageIdentity(config);\n const connectorId = config.id ?? mcpIdentity.hostAlias;\n if (!isValidConnectorId(connectorId)) {\n throw new ConnectorConfigError(\n `id must be kebab-case matching ${CONNECTOR_ID_RE} or derivable from mcp/package metadata (got ${JSON.stringify(config.id)})`,\n );\n }\n const hasCommands = Array.isArray(config.commands) && config.commands.length > 0;\n const hasSkills = Array.isArray(config.skills) && config.skills.length > 0;\n const hasSubagents = Array.isArray(config.subagents) && config.subagents.length > 0;\n const hasMemory = Array.isArray(config.memory) && config.memory.length > 0;\n // A statusline (a HUD) is a legitimate sole payload — a connector whose whole\n // job is rendering a status line. SINGULAR (one per connector), so a plain\n // presence check (object with a render handler — validated in normalizeStatusline).\n const hasStatusline = config.statusline != null;\n // Actions (each a user-invokable run handler) are a legitimate sole payload —\n // a connector whose whole job is shipping dispatchable actions.\n const hasActions = Array.isArray(config.actions) && config.actions.length > 0;\n // A platform-scoped nativeHooks declaration is a legitimate sole payload\n // (a hooks-only connector wired entirely through native passthrough events).\n const hasNativeHooks = Object.values(config.platforms ?? {}).some(\n (override) =>\n override?.nativeHooks != null && Object.keys(override.nativeHooks).length > 0,\n );\n // Likewise a platform-scoped configPatch declaration (a connector whose whole\n // job is asserting a host config key, e.g. an experimental feature flag).\n const hasConfigPatch = Object.values(config.platforms ?? {}).some(\n (override) => Array.isArray(override?.configPatch) && override.configPatch.length > 0,\n );\n if (\n !config.server &&\n !config.hooks &&\n !hasCommands &&\n !hasSkills &&\n !hasSubagents &&\n !hasMemory &&\n !hasStatusline &&\n !hasActions &&\n !hasNativeHooks &&\n !hasConfigPatch\n ) {\n throw new ConnectorConfigError(\n \"a connector must declare at least one of `server`, `hooks`, `commands`, `skills`, `subagents`, `memory`, `statusline`, `actions`, \" +\n \"or a per-platform `nativeHooks` / `configPatch` declaration\",\n );\n }\n\n if (config.server) {\n const s = config.server;\n if (s.transport === \"stdio\") {\n if (!s.command || typeof s.command !== \"string\") {\n throw new ConnectorConfigError(\n \"server.command is required for stdio transport\",\n );\n }\n } else {\n if (!s.url || typeof s.url !== \"string\") {\n throw new ConnectorConfigError(\n `server.url is required for ${s.transport} transport`,\n );\n }\n }\n }\n\n // Validate hook handlers are functions, plus any per-host override map.\n if (config.hooks) {\n for (const ev of ALL_EVENTS) {\n const def = config.hooks[ev];\n if (def != null && typeof def.handler !== \"function\") {\n throw new ConnectorConfigError(`hooks.${ev}.handler must be a function`);\n }\n // Per-host override map: every key must be a registered platform id and\n // every entry's handler a function (author-time hard error, not skip-warn).\n validateHostsMap(def?.hosts, `hooks.${ev}.hosts`, \"handler\");\n }\n }\n\n // Validate per-platform native passthrough hooks (handlers live in this same\n // config module, exactly like normalized hooks — the resolved connector keeps\n // `platforms` verbatim, so live handlers survive resolution and are recovered\n // at runtime by re-importing the module via the registry's modulePath).\n validateNativeHooks(config.platforms);\n\n // Validate per-platform declarative config patches (pure JSON — persisted\n // whole; semantics are FIXED set-if-absent/skip-warn, so only the shape is\n // validated here; the host adapter enforces its sensitive-key denylist).\n validateConfigPatches(config.platforms);\n\n const commands = normalizeCommands(config.commands);\n const skills = normalizeSkills(config.skills);\n const subagents = normalizeSubagents(config.subagents);\n const memory = normalizeMemory(config.memory);\n const statusline = normalizeStatusline(config.statusline);\n const actions = normalizeActions(config.actions);\n\n const t = config.telemetry ?? {};\n\n const resolved: ResolvedConnector = {\n id: connectorId,\n ...(Object.keys(mcpIdentity).length > 0 ? { mcp: mcpIdentity } : {}),\n displayName: config.displayName ?? connectorId,\n version: config.version ?? packageMetadata?.version ?? \"0.0.0\",\n ...(config.server ? { server: normalizeServer(config.server) } : {}),\n hooks: config.hooks ?? {},\n hookEvents: declaredEvents(config.hooks),\n telemetry: {\n enabled: t.enabled ?? true,\n modelFamilyHint: t.modelFamilyHint ?? \"auto\",\n measureToolDefs: t.measureToolDefs ?? true,\n // OPT-IN: host-native turn-usage capture is OFF unless explicitly enabled\n // in config (or forced on at install via AGENT_CONNECTOR_HOST_NATIVE=1).\n hostNativeUsage: t.hostNativeUsage ?? false,\n store: t.store ?? \"ndjson\",\n calibration: {\n anthropicCountTokens: t.calibration?.anthropicCountTokens ?? false,\n },\n },\n commands,\n skills,\n subagents,\n memory,\n ...(statusline ? { statusline } : {}),\n actions,\n platforms: config.platforms ?? {},\n targets: config.targets ?? \"auto\",\n ...(config.publish ? { publish: normalizePublish(config.publish) } : {}),\n };\n\n return resolved;\n}\n\n/** The 13 normalized event names, for the nativeHooks collision check. */\nconst NORMALIZED_EVENT_SET: ReadonlySet<string> = new Set(ALL_EVENTS);\n\n/**\n * Validate a per-host override map (`hosts?:`) declared on a hook definition or\n * on the statusline. Shared by the hook-validation loop and normalizeStatusline:\n * - the value must be a plain object keyed by platform id;\n * - every key MUST be a REGISTERED platform id (REGISTERED_PLATFORM_IDS) —\n * an unknown id is an author-time ConnectorConfigError (NOT a skip-warn:\n * a typo'd host would otherwise silently never fire);\n * - every entry's implementation field (`handler` for hooks, `render` for\n * statusline, `run` for actions) MUST be a function (it is re-imported from\n * the connector module at runtime, like the top-level handler/render/run).\n * `undefined` (no map) is valid and skipped.\n */\nfunction validateHostsMap(\n map: unknown,\n surfaceLabel: string,\n implField: \"handler\" | \"render\" | \"run\",\n): void {\n if (map == null) return;\n if (typeof map !== \"object\" || Array.isArray(map)) {\n throw new ConnectorConfigError(\n `${surfaceLabel} must be an object keyed by platform id`,\n );\n }\n for (const [platformId, entry] of Object.entries(map as Record<string, unknown>)) {\n if (!REGISTERED_PLATFORM_IDS.has(platformId as PlatformId)) {\n const valid = [...REGISTERED_PLATFORM_IDS].sort().join(\", \");\n throw new ConnectorConfigError(\n `unknown platform id \"${platformId}\" in hosts map for ${surfaceLabel}; valid ids: ${valid}`,\n );\n }\n if (\n entry == null ||\n typeof entry !== \"object\" ||\n typeof (entry as Record<string, unknown>)[implField] !== \"function\"\n ) {\n throw new ConnectorConfigError(\n `${surfaceLabel}.${platformId}.${implField} must be a function`,\n );\n }\n }\n}\n\n/**\n * Validate every `platforms[<id>].nativeHooks` declaration:\n * - the value must be an object keyed by HOST-NATIVE event name;\n * - an event name must NOT collide with the normalized {@link HookEventName}\n * union (those belong in the cross-platform `hooks` API, which gets\n * normalization, matcher evaluation, and HookResponse mapping);\n * - each handler must be a function (it is re-imported from the config module\n * at runtime, like normalized hook handlers);\n * - `matcher`, when present, must be a string (written verbatim into the\n * host's hook config — the host evaluates it).\n */\nfunction validateNativeHooks(platforms: ConnectorConfig[\"platforms\"]): void {\n if (platforms == null) return;\n for (const [platformId, override] of Object.entries(platforms)) {\n const native = override?.nativeHooks;\n if (native == null) continue;\n const where = `platforms.${platformId}.nativeHooks`;\n if (typeof native !== \"object\" || Array.isArray(native)) {\n throw new ConnectorConfigError(\n `${where} must be an object keyed by host-native event name`,\n );\n }\n for (const [event, def] of Object.entries(native)) {\n if (NORMALIZED_EVENT_SET.has(event)) {\n throw new ConnectorConfigError(\n `${where}.${event} collides with the normalized hook event \"${event}\"; ` +\n `declare it under \\`hooks.${event}\\` (the normalized, cross-platform hooks API) instead`,\n );\n }\n if (def == null || typeof def !== \"object\" || typeof def.handler !== \"function\") {\n throw new ConnectorConfigError(`${where}.${event}.handler must be a function`);\n }\n if (def.matcher !== undefined && typeof def.matcher !== \"string\") {\n throw new ConnectorConfigError(`${where}.${event}.matcher must be a string`);\n }\n }\n }\n}\n\n/**\n * Validate every `platforms[<id>].configPatch` declaration:\n * - the value must be an array of patch objects;\n * - `key` must be a dotted LEAF path whose segments match\n * {@link CONFIG_PATCH_SEGMENT_RE} — no dots-in-key, no array indices;\n * - `key` must not collide with the agent-connector-modeled namespace\n * (`hooks*` → hooks/nativeHooks; `mcpServers*` & friends → server/extra);\n * - duplicate keys within one platform's list are rejected (a duplicate\n * would double-apply / fight itself on refcounts);\n * - `value` must be JSON-serializable data (it is persisted whole in the\n * connector record and in the ownership ledger);\n * - `reason` is REQUIRED (printed in the install diff and every skip-warn);\n * - `docsUrl`, when present, must be a string.\n * The HOST-side sensitive-key denylist is deliberately NOT validated here —\n * it lives in (and is documented by) each supporting adapter.\n */\nfunction validateConfigPatches(platforms: ConnectorConfig[\"platforms\"]): void {\n if (platforms == null) return;\n for (const [platformId, override] of Object.entries(platforms)) {\n const patches = override?.configPatch;\n if (patches == null) continue;\n const where = `platforms.${platformId}.configPatch`;\n if (!Array.isArray(patches)) {\n throw new ConnectorConfigError(`${where} must be an array of patch objects`);\n }\n const seen = new Set<string>();\n patches.forEach((patch, i) => {\n if (patch == null || typeof patch !== \"object\" || Array.isArray(patch)) {\n throw new ConnectorConfigError(`${where}[${i}] must be an object`);\n }\n if (!isValidConfigPatchKey(patch.key)) {\n throw new ConnectorConfigError(\n `${where}[${i}].key must be a dotted LEAF path whose segments match ` +\n `${CONFIG_PATCH_SEGMENT_RE} (no dots-in-key, no array indices); ` +\n `got ${JSON.stringify(patch.key)}`,\n );\n }\n const violation = configPatchNamespaceViolation(patch.key);\n if (violation) {\n throw new ConnectorConfigError(`${where}[${i}].key ${violation}`);\n }\n if (seen.has(patch.key)) {\n throw new ConnectorConfigError(\n `${where}[${i}] duplicate key \"${patch.key}\"`,\n );\n }\n seen.add(patch.key);\n if (!isJsonValue(patch.value)) {\n throw new ConnectorConfigError(\n `${where}[${i}].value must be JSON-serializable data ` +\n `(string/finite number/boolean/null/array/plain object)`,\n );\n }\n if (typeof patch.reason !== \"string\" || patch.reason.trim() === \"\") {\n throw new ConnectorConfigError(\n `${where}[${i}].reason is required (a human-readable why, printed in ` +\n `the install diff and every skip-warn)`,\n );\n }\n if (patch.docsUrl !== undefined && typeof patch.docsUrl !== \"string\") {\n throw new ConnectorConfigError(`${where}[${i}].docsUrl must be a string`);\n }\n });\n }\n}\n\n/**\n * Validate the optional `publish` block (registry server.json + MCPB bundle\n * distribution metadata). Light, dependency-free: we only reject shapes that\n * would emit a clearly INVALID standard artifact. The per-format required-field\n * checks (e.g. server.json needs registryNamespace, mcpb needs author.name)\n * live in the emitters so a connector that never publishes pays nothing.\n */\nfunction normalizePublish(publish: PublishConfig): PublishConfig {\n if (typeof publish !== \"object\" || Array.isArray(publish)) {\n throw new ConnectorConfigError(\"publish must be an object\");\n }\n if (publish.registryNamespace !== undefined) {\n const ns = publish.registryNamespace;\n if (typeof ns !== \"string\" || !REGISTRY_NAMESPACE_RE.test(ns)) {\n throw new ConnectorConfigError(\n `publish.registryNamespace must be a reverse-DNS namespace matching ${REGISTRY_NAMESPACE_RE} ` +\n `(e.g. \"io.github.acme\" or \"com.acme\"); got ${JSON.stringify(ns)}`,\n );\n }\n }\n if (publish.packageName !== undefined && typeof publish.packageName !== \"string\") {\n throw new ConnectorConfigError(\"publish.packageName must be a string\");\n }\n if (publish.author !== undefined) {\n const a = publish.author;\n if (typeof a !== \"object\" || a == null || typeof a.name !== \"string\" || a.name === \"\") {\n throw new ConnectorConfigError(\"publish.author.name must be a non-empty string\");\n }\n }\n return { ...publish };\n}\n\n/** Validate a surface name (kebab-case) or throw a ConnectorConfigError. */\nfunction assertSurfaceName(\n surface: string,\n name: unknown,\n index: number,\n field = \"name\",\n): string {\n if (typeof name !== \"string\" || !SURFACE_NAME_RE.test(name)) {\n throw new ConnectorConfigError(\n `${surface}[${index}].${field} must be kebab-case matching ${SURFACE_NAME_RE} (got ${JSON.stringify(name)})`,\n );\n }\n return name;\n}\n\n/** Require a non-empty string field on a surface entry. */\nfunction assertNonEmptyString(\n surface: string,\n index: number,\n field: string,\n value: unknown,\n): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new ConnectorConfigError(`${surface}[${index}].${field} must be a non-empty string`);\n }\n return value;\n}\n\n/** Detect a duplicate surface name within one array, throwing on collision. */\nfunction assertNoDuplicate(\n surface: string,\n seen: Set<string>,\n name: string,\n index: number,\n field = \"name\",\n): void {\n if (seen.has(name)) {\n throw new ConnectorConfigError(`${surface}[${index}] duplicate ${field} \"${name}\"`);\n }\n seen.add(name);\n}\n\nfunction normalizeCommands(input: CommandDef[] | undefined): CommandDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"commands must be an array\");\n }\n const seen = new Set<string>();\n return input.map((cmd, i) => {\n if (!cmd || typeof cmd !== \"object\") {\n throw new ConnectorConfigError(`commands[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"commands\", cmd.name, i);\n assertNoDuplicate(\"commands\", seen, name, i);\n assertNonEmptyString(\"commands\", i, \"prompt\", cmd.prompt);\n // Pass through verbatim (content); arrays are content, not handlers.\n return { ...cmd, name };\n });\n}\n\nfunction normalizeSkills(input: SkillDef[] | undefined): SkillDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"skills must be an array\");\n }\n const seen = new Set<string>();\n return input.map((skill, i) => {\n if (!skill || typeof skill !== \"object\") {\n throw new ConnectorConfigError(`skills[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"skills\", skill.name, i);\n assertNoDuplicate(\"skills\", seen, name, i);\n const description = assertNonEmptyString(\"skills\", i, \"description\", skill.description);\n if (description.length > SKILL_DESCRIPTION_MAX) {\n throw new ConnectorConfigError(\n `skills[${i}].description exceeds ${SKILL_DESCRIPTION_MAX} chars (got ${description.length})`,\n );\n }\n assertNonEmptyString(\"skills\", i, \"body\", skill.body);\n assertSafeResourceKeys(\"skills\", i, skill.resources);\n return { ...skill, name };\n });\n}\n\n/**\n * Validate every `skill.resources` key as a SAFE relative path that stays inside\n * the skill dir. Each resource is later written/removed via join(skillDir, rel)\n * by every adapter, so an unvalidated key like \"../../settings.json\" would\n * escape the skill dir → arbitrary file write/delete. Reject (throw) when a key\n * is empty/\".\", absolute, or normalizes to a path that begins with \"..\" or\n * contains a \"/..\" (or platform-sep) traversal segment.\n */\nfunction assertSafeResourceKeys(\n surface: string,\n index: number,\n resources: Record<string, string> | undefined,\n): void {\n if (resources == null) return;\n if (typeof resources !== \"object\" || Array.isArray(resources)) {\n throw new ConnectorConfigError(`${surface}[${index}].resources must be an object`);\n }\n for (const rel of Object.keys(resources)) {\n if (rel === \"\" || rel === \".\" || rel.trim() === \"\") {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must be a non-empty relative path (got ${JSON.stringify(rel)})`,\n );\n }\n if (isAbsolute(rel)) {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must be a relative path inside the skill dir, not absolute (got ${JSON.stringify(rel)})`,\n );\n }\n // Normalize with BOTH posix and native separators flattened so a Windows\n // \"..\\\\x\" or a posix \"../x\" is caught regardless of the host OS.\n const norm = normalize(rel.replace(/\\\\/g, \"/\"));\n const segs = norm.split(/[/\\\\]/);\n if (norm === \"..\" || norm.startsWith(\"../\") || norm.startsWith(\"..\\\\\") || segs.includes(\"..\")) {\n throw new ConnectorConfigError(\n `${surface}[${index}].resources key must not escape the skill dir via \"..\" (got ${JSON.stringify(rel)})`,\n );\n }\n }\n}\n\n/** Default name for a single-entry memory declaration. */\nconst MEMORY_DEFAULT_NAME = \"memory\";\n\n/**\n * Validate + normalize `memory` entries (the managed-block content surface).\n * - `name` defaults to \"memory\", then must be kebab-case and unique — it is\n * half of the block marker id (`<connectorId>/<name>`) and must stay stable;\n * - `content` must be a non-empty string, ≤ 16 KiB (hard cap — memory files\n * are injected into every prompt of every session on the host), and must\n * NOT contain the literal marker tokens `agent-connector:begin` /\n * `agent-connector:end` (they would corrupt marker scanning in the shared\n * file — rephrase the guidance instead). The 4 KiB SOFT budget is reported\n * at install time as a `warn` ChangeRecord, not a config error\n * (defineConnector has no warning channel).\n */\nfunction normalizeMemory(input: MemoryDef[] | undefined): MemoryDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"memory must be an array\");\n }\n const seen = new Set<string>();\n return input.map((entry, i) => {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new ConnectorConfigError(`memory[${i}] must be an object`);\n }\n const name =\n entry.name === undefined\n ? MEMORY_DEFAULT_NAME\n : assertSurfaceName(\"memory\", entry.name, i);\n assertNoDuplicate(\"memory\", seen, name, i);\n const content = assertNonEmptyString(\"memory\", i, \"content\", entry.content);\n const bytes = Buffer.byteLength(content, \"utf8\");\n if (bytes > MEMORY_CONTENT_HARD_CAP_BYTES) {\n throw new ConnectorConfigError(\n `memory[${i}].content exceeds the ${MEMORY_CONTENT_HARD_CAP_BYTES}-byte hard cap ` +\n `(got ${bytes} bytes); memory is inlined into every prompt of every targeted host — keep it terse`,\n );\n }\n for (const token of [MANAGED_BLOCK_BEGIN_TOKEN, MANAGED_BLOCK_END_TOKEN]) {\n if (content.includes(token)) {\n throw new ConnectorConfigError(\n `memory[${i}].content must not contain the literal marker token \"${token}\" ` +\n `(it would corrupt managed-block scanning in the shared memory file); rephrase the guidance`,\n );\n }\n }\n if (entry.description !== undefined && typeof entry.description !== \"string\") {\n throw new ConnectorConfigError(`memory[${i}].description must be a string`);\n }\n return { ...entry, name };\n });\n}\n\n/** Default name for the (singular) statusline declaration. */\nconst STATUSLINE_DEFAULT_NAME = \"statusline\";\n\n/**\n * Validate + normalize the SINGULAR `statusline` (a HUD handler surface):\n * - `render` MUST be a function (it is the renderer, re-imported at runtime\n * like a hook handler);\n * - `name` defaults to \"statusline\", then must be kebab-case (the shared\n * surface-name validator);\n * - `description`, when present, must be a string.\n * Returns undefined when no statusline is declared (it is optional and singular,\n * unlike the memory[] array).\n */\nfunction normalizeStatusline(\n input: StatuslineDef | undefined,\n): StatuslineDef | undefined {\n if (input == null) return undefined;\n if (typeof input !== \"object\" || Array.isArray(input)) {\n throw new ConnectorConfigError(\"statusline must be an object\");\n }\n if (typeof input.render !== \"function\") {\n throw new ConnectorConfigError(\"statusline.render must be a function\");\n }\n const name =\n input.name === undefined\n ? STATUSLINE_DEFAULT_NAME\n : assertSurfaceName(\"statusline\", input.name, 0);\n if (input.description !== undefined && typeof input.description !== \"string\") {\n throw new ConnectorConfigError(\"statusline.description must be a string\");\n }\n // Per-host render override map: registered platform ids only, each render a\n // function (author-time hard error, mirroring the hook hosts-map validation).\n validateHostsMap(input.hosts, \"statusline.hosts\", \"render\");\n return { ...input, name };\n}\n\n/**\n * Validate + normalize `actions` (the user-invokable action handler surface):\n * - the value must be an array;\n * - each entry's `id` must be kebab-case (the shared surface-name validator)\n * and UNIQUE within the connector (ConnectorConfigError on dup — the id is\n * the verb's positional arg, so a duplicate would be ambiguous);\n * - `run` MUST be a function (it is the handler, re-imported at runtime like a\n * hook handler / statusline render);\n * - `description`, when present, must be a string;\n * - the per-host `hosts:` override map is validated via the shared\n * validateHostsMap (registered platform ids only, each `run` a function).\n * Returns [] when no actions are declared (defaults applied, like the other\n * array surfaces).\n */\nfunction normalizeActions(input: ActionDef[] | undefined): ActionDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"actions must be an array\");\n }\n const seen = new Set<string>();\n return input.map((action, i) => {\n if (!action || typeof action !== \"object\" || Array.isArray(action)) {\n throw new ConnectorConfigError(`actions[${i}] must be an object`);\n }\n const id = assertSurfaceName(\"actions\", action.id, i, \"id\");\n assertNoDuplicate(\"actions\", seen, id, i, \"id\");\n if (typeof action.run !== \"function\") {\n throw new ConnectorConfigError(`actions[${i}].run must be a function`);\n }\n if (action.description !== undefined && typeof action.description !== \"string\") {\n throw new ConnectorConfigError(`actions[${i}].description must be a string`);\n }\n // Per-host run override map: registered platform ids only, each run a\n // function (author-time hard error, mirroring the statusline/hook hosts-map).\n validateHostsMap(action.hosts, `actions.${id}.hosts`, \"run\");\n return { ...action, id };\n });\n}\n\n/**\n * Typed identity helper for authoring a status line in its own module:\n * export const myStatusline = defineStatusline({ render: (ctx) => \"…\" });\n * Mirrors the (informal) defineX helpers; gives the developer full type\n * inference on {@link StatuslineContext} without importing the type by hand.\n */\nexport const defineStatusline = (def: StatuslineDef): StatuslineDef => def;\n\n// ─────────────────────────────────────────────────────────────────────────\n// Typed identity helpers (the `define*` authoring family)\n//\n// Each is a one-line identity function: it returns its argument UNCHANGED and\n// does NOT validate or mutate (validation still happens centrally in\n// defineConnector). Their only job is ergonomics — authoring a surface in its\n// own module with full type inference + autocomplete on the *Def shape, and a\n// single import site — mirroring the existing {@link defineStatusline}.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Typed identity helper for authoring ONE normalized lifecycle hook in its own\n * module, EVENT-PARAMETERIZED so the handler's payload narrows to that event:\n *\n * export const onPre = defineHook(\"PreToolUse\", {\n * handler(evt) { evt.toolName // ← typed as a PreToolUseEvent, not the union\n * return { decision: \"deny\", reason: \"no\" };\n * },\n * });\n *\n * The leading `event` argument exists ONLY to infer `E`; the def is returned\n * unchanged (identity — validation still happens in defineConnector). Passing\n * `HookDefinition` without an event would widen the handler param to the full\n * {@link HookEventName} union and lose per-event narrowing, so the event tag is\n * required.\n */\nexport const defineHook = <E extends HookEventName>(\n event: E,\n def: HookDefinition<E>,\n): HookDefinition<E> => {\n void event; // tag-only: used solely to infer E (the def carries no event field)\n return def;\n};\n\n/**\n * Typed identity helper for authoring a slash {@link CommandDef} in its own\n * module. Returns the def unchanged (kebab-case name + prompt are validated by\n * defineConnector when the connector is assembled).\n */\nexport const defineCommand = (def: CommandDef): CommandDef => def;\n\n/**\n * Typed identity helper for authoring an Agent {@link SkillDef} in its own\n * module. Returns the def unchanged (name/description/body + safe resource keys\n * are validated by defineConnector).\n */\nexport const defineSkill = (def: SkillDef): SkillDef => def;\n\n/**\n * Typed identity helper for authoring a {@link SubagentDef} in its own module.\n * Returns the def unchanged (name/description/prompt validated by defineConnector).\n */\nexport const defineSubagent = (def: SubagentDef): SubagentDef => def;\n\n/**\n * Typed identity helper for authoring a {@link MemoryDef} (standing-guidance\n * managed block) in its own module. Returns the def unchanged (name/content +\n * byte budget + marker-token guard validated by defineConnector).\n */\nexport const defineMemory = (def: MemoryDef): MemoryDef => def;\n\n/**\n * Typed identity helper for authoring a user-invokable {@link ActionDef} in its\n * own module. Returns the def unchanged (kebab-case unique id + run-is-function\n * + the per-host hosts map validated by defineConnector when the connector is\n * assembled).\n */\nexport const defineAction = (def: ActionDef): ActionDef => def;\n\n/**\n * Typed identity helper for authoring a declarative {@link ConfigPatchDef}\n * (set-if-absent host-config key patch) in its own module. Returns the def\n * unchanged (leaf-path grammar + namespace guard + required reason validated by\n * defineConnector when it appears under `platforms[<id>].configPatch`).\n */\nexport const defineConfigPatch = (def: ConfigPatchDef): ConfigPatchDef => def;\n\n/**\n * Typed identity helper for authoring a {@link NativeHookDef} (host-native\n * passthrough hook) in its own module. Returns the def unchanged (event-name\n * collision + handler-is-function validated by defineConnector when it appears\n * under `platforms[<id>].nativeHooks`).\n */\nexport const defineNativeHook = (def: NativeHookDef): NativeHookDef => def;\n\nfunction normalizeSubagents(input: SubagentDef[] | undefined): SubagentDef[] {\n if (input == null) return [];\n if (!Array.isArray(input)) {\n throw new ConnectorConfigError(\"subagents must be an array\");\n }\n const seen = new Set<string>();\n return input.map((agent, i) => {\n if (!agent || typeof agent !== \"object\") {\n throw new ConnectorConfigError(`subagents[${i}] must be an object`);\n }\n const name = assertSurfaceName(\"subagents\", agent.name, i);\n assertNoDuplicate(\"subagents\", seen, name, i);\n assertNonEmptyString(\"subagents\", i, \"description\", agent.description);\n assertNonEmptyString(\"subagents\", i, \"prompt\", agent.prompt);\n return { ...agent, name };\n });\n}\n\nfunction normalizeServer(server: ConnectorConfig[\"server\"]): ResolvedConnector[\"server\"] {\n if (!server) return undefined;\n const wrapDefault = server.transport === \"stdio\";\n return {\n ...server,\n enabled: server.enabled ?? true,\n tools: server.tools ?? { include: [\"*\"] },\n wrapForTelemetry: server.wrapForTelemetry ?? wrapDefault,\n };\n}\n","/**\n * core/mcp-standard — pinned literals + guards for the official MCP standards\n * agent-connector EMITS (registry server.json, MCPB bundle) or SPEAKS (the\n * connection-lifecycle wire used by `doctor --probe`).\n *\n * Each pinned constant is the CURRENT released value verified 2026-06-05 against\n * the upstream spec/schema. They are deliberately centralized + annotated\n * MUST-VERIFY-AT-IMPLEMENTATION: a newer spec revision can supersede any of them\n * without breaking us, so the bump is a one-line change here, never scattered.\n *\n * Sources:\n * • server.schema.json 2025-12-11 — https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\n * • MCPB manifest 0.3 — github.com/anthropics/mcpb (MANIFEST.md)\n * • MCP protocol 2025-11-25 — modelcontextprotocol.io/specification/2025-11-25\n * (latest RELEASED revision. 2026-07-28 is a release-candidate only — do NOT\n * advance to it until it publishes as a stable dated revision. The probe\n * offers this version but ACCEPTS whatever a server negotiates, so the\n * offered literal being stale never breaks interop — only this constant.)\n */\n\nimport type { Transport } from \"./types.js\";\n\n/** Registry server.json JSON Schema URL (released 2025-12-11). MUST-VERIFY on bump. */\nexport const MCP_SERVER_SCHEMA_URL =\n \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\";\n\n/** Current MCPB bundle manifest_version (anthropics/mcpb). MUST-VERIFY on bump. */\nexport const MCPB_MANIFEST_VERSION = \"0.3\";\n\n/**\n * Latest RELEASED MCP protocol revision `doctor --probe` offers in `initialize`.\n * Current = 2025-11-25. MUST-VERIFY on bump; stay on the latest *released*\n * revision (2026-07-28 is RC-only as of 2026-06). Negotiation accepts whatever\n * the server returns, so this is the offered value, not a hard requirement.\n */\nexport const MCP_PROTOCOL_VERSION = \"2025-11-25\";\n\n/** Default npm registry base URL stamped into server.json npm packages. */\nexport const NPM_REGISTRY_BASE_URL = \"https://registry.npmjs.org\";\n\n/**\n * server.json transport.type enum — the EXACT values the 2025-12-11 schema\n * accepts (packages[].transport.type and remotes[].type). Our `http` Transport\n * maps to the spec slug \"streamable-http\"; \"ws\" is non-spec (see below).\n */\nexport type RegistryTransportType = \"stdio\" | \"streamable-http\" | \"sse\";\n\n/**\n * Map our {@link Transport} union to the registry/server.json transport slug.\n * Returns null for \"ws\" — WebSocket is NOT a spec transport (it can only exist\n * as a vendor custom transport, e.g. Claude Code's non-spec type:\"ws\"), so a\n * conformant artifact must REJECT it rather than emit an invalid type. The spec\n * defines exactly stdio + streamable-http, with sse the deprecated HTTP+SSE.\n */\nexport function registryTransportType(transport: Transport): RegistryTransportType | null {\n switch (transport) {\n case \"stdio\":\n return \"stdio\";\n case \"http\":\n return \"streamable-http\";\n case \"sse\":\n return \"sse\";\n case \"ws\":\n return null;\n }\n}\n\n/**\n * Is `v` a CONCRETE version the MCP Registry accepts? The registry rejects\n * ranges (\"^1.2.3\", \"~1.2.3\", \">=1.2.3\", \"1.x\", \"1.*\"); only a pinned SemVer\n * (optionally with a -prerelease / +build suffix) is valid. \"0.0.0\" is concrete\n * (so it passes this guard) but is the unset placeholder — callers warn on it\n * separately via {@link isPlaceholderVersion}.\n */\nexport function isConcreteSemver(v: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/.test(v);\n}\n\n/** The unset default version a connector ships with until the dev sets a real one. */\nexport function isPlaceholderVersion(v: string): boolean {\n return v === \"0.0.0\";\n}\n\n/**\n * Reverse-DNS registry namespace pattern (the part BEFORE the \"/\" in a\n * server.json `name`). The full name regex is\n * `^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$`; the namespace side allows letters, digits,\n * dots and hyphens (NO underscore) — e.g. \"io.github.acme\" or \"com.acme\".\n */\nexport const REGISTRY_NAMESPACE_RE = /^[a-zA-Z0-9.-]+$/;\n\n/** The full server.json `name` regex (namespace \"/\" server-name), per the schema. */\nexport const REGISTRY_NAME_RE = /^[a-zA-Z0-9.-]+\\/[a-zA-Z0-9._-]+$/;\n"],"mappings":"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","names":[]}
|