@theokit/sdk 5.11.1 → 5.12.1
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/CHANGELOG.md +115 -0
- package/dist/a2a/index.cjs +8 -8
- package/dist/a2a/index.js +3 -3
- package/dist/agent-BVKIUP7F.cjs +71 -0
- package/dist/{agent-OKP4LHG2.cjs.map → agent-BVKIUP7F.cjs.map} +1 -1
- package/dist/{agent-CmKwSyyD.d.ts → agent-CVzDy_Oj.d.ts} +17 -8
- package/dist/{agent-XMkGzff_.d.cts → agent-Cn0sphpA.d.cts} +17 -8
- package/dist/agent-IG7BP6GN.js +62 -0
- package/dist/{agent-DFV52V6E.js.map → agent-IG7BP6GN.js.map} +1 -1
- package/dist/agent-prims-CZyxfCmS.d.cts +206 -0
- package/dist/agent-prims-CZyxfCmS.d.ts +206 -0
- package/dist/agent-session-store-22GKA7O4.js +6 -0
- package/dist/{agent-session-store-ODD3IZCW.js.map → agent-session-store-22GKA7O4.js.map} +1 -1
- package/dist/agent-session-store-WP6NGDVX.cjs +23 -0
- package/dist/{agent-session-store-SVEJHSUA.cjs.map → agent-session-store-WP6NGDVX.cjs.map} +1 -1
- package/dist/auth/index.cjs +33 -33
- package/dist/auth/index.js +6 -6
- package/dist/{batch-427O54FQ.js → batch-EP3FCL6D.js} +9 -9
- package/dist/{batch-427O54FQ.js.map → batch-EP3FCL6D.js.map} +1 -1
- package/dist/{batch-U4SO4RLX.cjs → batch-IZ5EDKJ5.cjs} +29 -29
- package/dist/{batch-U4SO4RLX.cjs.map → batch-IZ5EDKJ5.cjs.map} +1 -1
- package/dist/{chunk-IIYJMXYW.cjs → chunk-27V5I2WW.cjs} +4 -4
- package/dist/{chunk-IIYJMXYW.cjs.map → chunk-27V5I2WW.cjs.map} +1 -1
- package/dist/{chunk-DPY2M74T.js → chunk-2C24NSNV.js} +5 -5
- package/dist/{chunk-DPY2M74T.js.map → chunk-2C24NSNV.js.map} +1 -1
- package/dist/{chunk-7F7XE6CM.js → chunk-2S4HQCDF.js} +3 -3
- package/dist/{chunk-7F7XE6CM.js.map → chunk-2S4HQCDF.js.map} +1 -1
- package/dist/{chunk-LZJXEPEQ.js → chunk-2SFLLOMN.js} +3 -3
- package/dist/{chunk-LZJXEPEQ.js.map → chunk-2SFLLOMN.js.map} +1 -1
- package/dist/{chunk-RGQICWFU.js → chunk-354F3OF5.js} +3 -3
- package/dist/{chunk-RGQICWFU.js.map → chunk-354F3OF5.js.map} +1 -1
- package/dist/{chunk-TX56N7FR.js → chunk-3FSBE4PS.js} +4 -4
- package/dist/{chunk-TX56N7FR.js.map → chunk-3FSBE4PS.js.map} +1 -1
- package/dist/{chunk-RQMCOFAI.cjs → chunk-3HA7N2BB.cjs} +8 -8
- package/dist/{chunk-RQMCOFAI.cjs.map → chunk-3HA7N2BB.cjs.map} +1 -1
- package/dist/{chunk-GM54F4FE.cjs → chunk-3KIUY67P.cjs} +9 -9
- package/dist/{chunk-GM54F4FE.cjs.map → chunk-3KIUY67P.cjs.map} +1 -1
- package/dist/{chunk-CZJ6Q7CW.js → chunk-3OY6H3I7.js} +66 -7
- package/dist/chunk-3OY6H3I7.js.map +1 -0
- package/dist/{chunk-ZWACQM5U.cjs → chunk-3WKWBYPO.cjs} +9 -9
- package/dist/{chunk-ZWACQM5U.cjs.map → chunk-3WKWBYPO.cjs.map} +1 -1
- package/dist/{chunk-Q34EBNYG.js → chunk-4GUFNDZR.js} +5 -5
- package/dist/{chunk-Q34EBNYG.js.map → chunk-4GUFNDZR.js.map} +1 -1
- package/dist/{chunk-XDUGSTYJ.cjs → chunk-4YD2WI57.cjs} +15 -15
- package/dist/{chunk-XDUGSTYJ.cjs.map → chunk-4YD2WI57.cjs.map} +1 -1
- package/dist/{chunk-SKDNCTXG.cjs → chunk-5A4UFBHW.cjs} +15 -15
- package/dist/{chunk-SKDNCTXG.cjs.map → chunk-5A4UFBHW.cjs.map} +1 -1
- package/dist/{chunk-SUVCHX4L.js → chunk-5AR6O7GW.js} +3 -3
- package/dist/{chunk-SUVCHX4L.js.map → chunk-5AR6O7GW.js.map} +1 -1
- package/dist/{chunk-SVCPJRUB.js → chunk-5HSMH2ZZ.js} +6 -6
- package/dist/{chunk-SVCPJRUB.js.map → chunk-5HSMH2ZZ.js.map} +1 -1
- package/dist/{chunk-GTX6FEDP.js → chunk-5IRDK5BC.js} +3 -3
- package/dist/{chunk-GTX6FEDP.js.map → chunk-5IRDK5BC.js.map} +1 -1
- package/dist/{chunk-H3CVJSFC.js → chunk-5IUDICRK.js} +3 -3
- package/dist/{chunk-H3CVJSFC.js.map → chunk-5IUDICRK.js.map} +1 -1
- package/dist/{chunk-FYEHYQ7T.cjs → chunk-6CXRYPG7.cjs} +15 -15
- package/dist/{chunk-FYEHYQ7T.cjs.map → chunk-6CXRYPG7.cjs.map} +1 -1
- package/dist/{chunk-NQVWSSX2.cjs → chunk-6OCJWGVD.cjs} +12 -12
- package/dist/{chunk-NQVWSSX2.cjs.map → chunk-6OCJWGVD.cjs.map} +1 -1
- package/dist/{chunk-MPR2ISL2.cjs → chunk-6XQLTQB6.cjs} +4 -4
- package/dist/{chunk-MPR2ISL2.cjs.map → chunk-6XQLTQB6.cjs.map} +1 -1
- package/dist/{chunk-72CPLZPC.cjs → chunk-7INPXUJI.cjs} +5 -5
- package/dist/{chunk-72CPLZPC.cjs.map → chunk-7INPXUJI.cjs.map} +1 -1
- package/dist/{chunk-HG5LKFG7.cjs → chunk-7WARNHWW.cjs} +16 -16
- package/dist/{chunk-HG5LKFG7.cjs.map → chunk-7WARNHWW.cjs.map} +1 -1
- package/dist/{chunk-KIKKTW5O.cjs → chunk-7YUPSBDV.cjs} +4 -4
- package/dist/{chunk-KIKKTW5O.cjs.map → chunk-7YUPSBDV.cjs.map} +1 -1
- package/dist/{chunk-IZDDSLL2.js → chunk-AB4OPJLD.js} +6 -6
- package/dist/{chunk-IZDDSLL2.js.map → chunk-AB4OPJLD.js.map} +1 -1
- package/dist/{chunk-XERM7PDQ.js → chunk-AGO5S5WD.js} +3 -3
- package/dist/{chunk-XERM7PDQ.js.map → chunk-AGO5S5WD.js.map} +1 -1
- package/dist/{chunk-ZWFTECOE.js → chunk-AHKS3DKI.js} +3 -3
- package/dist/{chunk-ZWFTECOE.js.map → chunk-AHKS3DKI.js.map} +1 -1
- package/dist/{chunk-ECD6ER3E.cjs → chunk-AIZ6WXSL.cjs} +4 -4
- package/dist/{chunk-ECD6ER3E.cjs.map → chunk-AIZ6WXSL.cjs.map} +1 -1
- package/dist/{chunk-KO6SHCEH.js → chunk-AQW54OWQ.js} +3 -3
- package/dist/{chunk-KO6SHCEH.js.map → chunk-AQW54OWQ.js.map} +1 -1
- package/dist/{chunk-P22BZE7G.cjs → chunk-AT2PESXB.cjs} +7 -7
- package/dist/{chunk-P22BZE7G.cjs.map → chunk-AT2PESXB.cjs.map} +1 -1
- package/dist/{chunk-MHIBYKWA.js → chunk-BJ7BPLQK.js} +131 -72
- package/dist/chunk-BJ7BPLQK.js.map +1 -0
- package/dist/{chunk-HV4V63CO.js → chunk-BO3PV3OP.js} +4 -4
- package/dist/{chunk-HV4V63CO.js.map → chunk-BO3PV3OP.js.map} +1 -1
- package/dist/{chunk-GHHIJEFX.cjs → chunk-BWYC7RZG.cjs} +9 -9
- package/dist/{chunk-GHHIJEFX.cjs.map → chunk-BWYC7RZG.cjs.map} +1 -1
- package/dist/{chunk-UNFBHV46.cjs → chunk-CEGU5AVD.cjs} +9 -9
- package/dist/chunk-CEGU5AVD.cjs.map +1 -0
- package/dist/{chunk-JV7JA7BM.cjs → chunk-CGH4BCIY.cjs} +11 -11
- package/dist/{chunk-JV7JA7BM.cjs.map → chunk-CGH4BCIY.cjs.map} +1 -1
- package/dist/{chunk-L3C6CCBG.cjs → chunk-CWQPQTUZ.cjs} +21 -21
- package/dist/{chunk-L3C6CCBG.cjs.map → chunk-CWQPQTUZ.cjs.map} +1 -1
- package/dist/{chunk-X3LOJJMO.js → chunk-DF77RBF4.js} +9 -9
- package/dist/{chunk-X3LOJJMO.js.map → chunk-DF77RBF4.js.map} +1 -1
- package/dist/{chunk-7NQ7HJ3E.cjs → chunk-EPANMGH7.cjs} +8 -7
- package/dist/chunk-EPANMGH7.cjs.map +1 -0
- package/dist/{chunk-NKIQTLFA.cjs → chunk-EQ5IDL6W.cjs} +4 -4
- package/dist/{chunk-NKIQTLFA.cjs.map → chunk-EQ5IDL6W.cjs.map} +1 -1
- package/dist/{chunk-D5KXKBEN.cjs → chunk-FEAWEBWN.cjs} +5 -5
- package/dist/{chunk-D5KXKBEN.cjs.map → chunk-FEAWEBWN.cjs.map} +1 -1
- package/dist/{chunk-NJSIAJEK.cjs → chunk-FLDMPVUC.cjs} +4 -4
- package/dist/{chunk-NJSIAJEK.cjs.map → chunk-FLDMPVUC.cjs.map} +1 -1
- package/dist/{chunk-ASK2OQM5.cjs → chunk-G7XBVM3T.cjs} +5 -5
- package/dist/{chunk-ASK2OQM5.cjs.map → chunk-G7XBVM3T.cjs.map} +1 -1
- package/dist/{chunk-U27XHOEF.js → chunk-H7G65PNI.js} +5 -5
- package/dist/{chunk-U27XHOEF.js.map → chunk-H7G65PNI.js.map} +1 -1
- package/dist/{chunk-JW3GVLM6.cjs → chunk-H7HD6RXO.cjs} +4 -4
- package/dist/{chunk-JW3GVLM6.cjs.map → chunk-H7HD6RXO.cjs.map} +1 -1
- package/dist/{chunk-ZHWKT2NI.js → chunk-HJ2LV464.js} +3 -3
- package/dist/{chunk-ZHWKT2NI.js.map → chunk-HJ2LV464.js.map} +1 -1
- package/dist/{chunk-KGZ66JVZ.cjs → chunk-HN4IKB6X.cjs} +14 -14
- package/dist/{chunk-KGZ66JVZ.cjs.map → chunk-HN4IKB6X.cjs.map} +1 -1
- package/dist/{chunk-XKMVJ443.js → chunk-HWMGR3HA.js} +6 -6
- package/dist/{chunk-XKMVJ443.js.map → chunk-HWMGR3HA.js.map} +1 -1
- package/dist/{chunk-EQ5NCH2Y.cjs → chunk-IO4RI5ZJ.cjs} +24 -24
- package/dist/{chunk-EQ5NCH2Y.cjs.map → chunk-IO4RI5ZJ.cjs.map} +1 -1
- package/dist/{chunk-OLSEWLK5.cjs → chunk-IOFMRS6T.cjs} +6 -6
- package/dist/{chunk-OLSEWLK5.cjs.map → chunk-IOFMRS6T.cjs.map} +1 -1
- package/dist/{chunk-7QJYKVP6.js → chunk-KCM3RMKR.js} +3 -3
- package/dist/{chunk-7QJYKVP6.js.map → chunk-KCM3RMKR.js.map} +1 -1
- package/dist/{chunk-7KIN3L2S.js → chunk-KU5O7WPK.js} +3 -3
- package/dist/{chunk-7KIN3L2S.js.map → chunk-KU5O7WPK.js.map} +1 -1
- package/dist/{chunk-J5623RIV.cjs → chunk-KX5QXLBS.cjs} +10 -10
- package/dist/{chunk-J5623RIV.cjs.map → chunk-KX5QXLBS.cjs.map} +1 -1
- package/dist/{chunk-J6GXS5A2.cjs → chunk-L7JPXZ2C.cjs} +17 -17
- package/dist/{chunk-J6GXS5A2.cjs.map → chunk-L7JPXZ2C.cjs.map} +1 -1
- package/dist/{chunk-P7NNKEWO.cjs → chunk-L7V4DDIE.cjs} +4 -4
- package/dist/{chunk-P7NNKEWO.cjs.map → chunk-L7V4DDIE.cjs.map} +1 -1
- package/dist/chunk-LO6LIUZ6.cjs +18 -0
- package/dist/{chunk-SGC7LSHF.cjs.map → chunk-LO6LIUZ6.cjs.map} +1 -1
- package/dist/{chunk-P5N3Q2NH.cjs → chunk-LSWHDOCL.cjs} +192 -148
- package/dist/chunk-LSWHDOCL.cjs.map +1 -0
- package/dist/{chunk-3JHIFQ4I.js → chunk-M4U24U6G.js} +3 -3
- package/dist/{chunk-3JHIFQ4I.js.map → chunk-M4U24U6G.js.map} +1 -1
- package/dist/{chunk-GGRPAQ3B.js → chunk-MLGETTYX.js} +5 -5
- package/dist/{chunk-GGRPAQ3B.js.map → chunk-MLGETTYX.js.map} +1 -1
- package/dist/{chunk-XZHJYSPB.js → chunk-MLJDW2YK.js} +4 -4
- package/dist/{chunk-XZHJYSPB.js.map → chunk-MLJDW2YK.js.map} +1 -1
- package/dist/{chunk-5Q4RTLMP.js → chunk-MS3TQSXU.js} +3 -3
- package/dist/{chunk-5Q4RTLMP.js.map → chunk-MS3TQSXU.js.map} +1 -1
- package/dist/{chunk-W2ZVTEOD.js → chunk-MSEL5E5P.js} +6 -6
- package/dist/{chunk-W2ZVTEOD.js.map → chunk-MSEL5E5P.js.map} +1 -1
- package/dist/{chunk-ZAM7BJXJ.cjs → chunk-MSW7LB2V.cjs} +5 -5
- package/dist/{chunk-ZAM7BJXJ.cjs.map → chunk-MSW7LB2V.cjs.map} +1 -1
- package/dist/{chunk-GMSRRJ53.js → chunk-NBNRQN6T.js} +5 -4
- package/dist/chunk-NBNRQN6T.js.map +1 -0
- package/dist/{chunk-7HUCU3KK.js → chunk-NHOEXFZT.js} +5 -5
- package/dist/{chunk-7HUCU3KK.js.map → chunk-NHOEXFZT.js.map} +1 -1
- package/dist/{chunk-6G4IRQUD.js → chunk-NJBJ3CUG.js} +6 -6
- package/dist/{chunk-6G4IRQUD.js.map → chunk-NJBJ3CUG.js.map} +1 -1
- package/dist/{chunk-I2ORT6QD.js → chunk-NKQVLWDF.js} +3 -3
- package/dist/{chunk-I2ORT6QD.js.map → chunk-NKQVLWDF.js.map} +1 -1
- package/dist/{chunk-N7KS3TIR.cjs → chunk-NP362THY.cjs} +28 -15
- package/dist/chunk-NP362THY.cjs.map +1 -0
- package/dist/{chunk-NWS2CS3Y.cjs → chunk-NWWTYBD3.cjs} +5 -5
- package/dist/{chunk-NWS2CS3Y.cjs.map → chunk-NWWTYBD3.cjs.map} +1 -1
- package/dist/{chunk-SVQIW5RJ.js → chunk-OLJOI4LK.js} +4 -4
- package/dist/{chunk-SVQIW5RJ.js.map → chunk-OLJOI4LK.js.map} +1 -1
- package/dist/{chunk-7JAKO3NM.js → chunk-ORLTGPOS.js} +3 -3
- package/dist/{chunk-7JAKO3NM.js.map → chunk-ORLTGPOS.js.map} +1 -1
- package/dist/{chunk-5DD4NJN3.js → chunk-PKRPV3R7.js} +3 -3
- package/dist/{chunk-5DD4NJN3.js.map → chunk-PKRPV3R7.js.map} +1 -1
- package/dist/{chunk-R5YPDAKD.cjs → chunk-POBZXULR.cjs} +4 -4
- package/dist/{chunk-R5YPDAKD.cjs.map → chunk-POBZXULR.cjs.map} +1 -1
- package/dist/{chunk-35KBC4BX.cjs → chunk-Q6WT5535.cjs} +446 -387
- package/dist/chunk-Q6WT5535.cjs.map +1 -0
- package/dist/{chunk-MQGSOKR3.js → chunk-Q7UG242V.js} +3 -3
- package/dist/{chunk-MQGSOKR3.js.map → chunk-Q7UG242V.js.map} +1 -1
- package/dist/{chunk-XKZFIP7Z.cjs → chunk-QG4SXZ2A.cjs} +4 -4
- package/dist/{chunk-XKZFIP7Z.cjs.map → chunk-QG4SXZ2A.cjs.map} +1 -1
- package/dist/{chunk-NAGPKWOD.cjs → chunk-QISSETTG.cjs} +4 -4
- package/dist/{chunk-NAGPKWOD.cjs.map → chunk-QISSETTG.cjs.map} +1 -1
- package/dist/{chunk-OR6XIWLB.js → chunk-QK2ERTO3.js} +4 -4
- package/dist/{chunk-OR6XIWLB.js.map → chunk-QK2ERTO3.js.map} +1 -1
- package/dist/{chunk-ZDYDBXSD.js → chunk-RN3XDZGS.js} +6 -6
- package/dist/{chunk-ZDYDBXSD.js.map → chunk-RN3XDZGS.js.map} +1 -1
- package/dist/{chunk-6LHQPOMI.cjs → chunk-RNXXYF22.cjs} +66 -7
- package/dist/chunk-RNXXYF22.cjs.map +1 -0
- package/dist/{chunk-TMKTSYDS.cjs → chunk-RS7YQR4X.cjs} +6 -6
- package/dist/{chunk-TMKTSYDS.cjs.map → chunk-RS7YQR4X.cjs.map} +1 -1
- package/dist/{chunk-HU666T3Q.cjs → chunk-S46WJTDL.cjs} +14 -14
- package/dist/{chunk-HU666T3Q.cjs.map → chunk-S46WJTDL.cjs.map} +1 -1
- package/dist/{chunk-ZD66GFH5.cjs → chunk-SDZ4XDWS.cjs} +35 -35
- package/dist/{chunk-ZD66GFH5.cjs.map → chunk-SDZ4XDWS.cjs.map} +1 -1
- package/dist/{chunk-ZJZ7LV3A.js → chunk-SV63WTA2.js} +3 -3
- package/dist/{chunk-ZJZ7LV3A.js.map → chunk-SV63WTA2.js.map} +1 -1
- package/dist/{chunk-CQYBJOB2.cjs → chunk-SXH37C7X.cjs} +4 -4
- package/dist/{chunk-CQYBJOB2.cjs.map → chunk-SXH37C7X.cjs.map} +1 -1
- package/dist/{chunk-GMRFAX4I.js → chunk-SZQFCHU6.js} +4 -4
- package/dist/{chunk-GMRFAX4I.js.map → chunk-SZQFCHU6.js.map} +1 -1
- package/dist/{chunk-6PB5AQCY.cjs → chunk-TLVI2OQK.cjs} +16 -16
- package/dist/{chunk-6PB5AQCY.cjs.map → chunk-TLVI2OQK.cjs.map} +1 -1
- package/dist/{chunk-B5MHRQJZ.js → chunk-TNFOU6SB.js} +3 -3
- package/dist/chunk-TNFOU6SB.js.map +1 -0
- package/dist/{chunk-N6OEX5TN.cjs → chunk-UDAKAE4Z.cjs} +16 -16
- package/dist/{chunk-N6OEX5TN.cjs.map → chunk-UDAKAE4Z.cjs.map} +1 -1
- package/dist/{chunk-26NEAJ6T.js → chunk-VVGFZQZC.js} +4 -4
- package/dist/{chunk-26NEAJ6T.js.map → chunk-VVGFZQZC.js.map} +1 -1
- package/dist/{chunk-XCQ432OX.js → chunk-VZGE7NAI.js} +3 -3
- package/dist/{chunk-XCQ432OX.js.map → chunk-VZGE7NAI.js.map} +1 -1
- package/dist/{chunk-C7GKRGKN.cjs → chunk-WDVG5R2Q.cjs} +19 -19
- package/dist/{chunk-C7GKRGKN.cjs.map → chunk-WDVG5R2Q.cjs.map} +1 -1
- package/dist/{chunk-FTZTZBCU.js → chunk-XESYJ4JC.js} +4 -4
- package/dist/{chunk-FTZTZBCU.js.map → chunk-XESYJ4JC.js.map} +1 -1
- package/dist/{chunk-OPAKPEPG.js → chunk-XJ3WU5CZ.js} +5 -5
- package/dist/{chunk-OPAKPEPG.js.map → chunk-XJ3WU5CZ.js.map} +1 -1
- package/dist/{chunk-BBJ77S62.js → chunk-XJ4C4NFX.js} +64 -20
- package/dist/chunk-XJ4C4NFX.js.map +1 -0
- package/dist/{chunk-AY43232P.js → chunk-XRPMZ7TO.js} +4 -4
- package/dist/{chunk-AY43232P.js.map → chunk-XRPMZ7TO.js.map} +1 -1
- package/dist/{chunk-X6DUEDAF.js → chunk-XZKIAENA.js} +3 -3
- package/dist/{chunk-X6DUEDAF.js.map → chunk-XZKIAENA.js.map} +1 -1
- package/dist/{chunk-X7EUUHXU.js → chunk-YMVTUV5K.js} +3 -3
- package/dist/{chunk-X7EUUHXU.js.map → chunk-YMVTUV5K.js.map} +1 -1
- package/dist/{chunk-3JANYJYS.cjs → chunk-YOVZMN4P.cjs} +8 -8
- package/dist/{chunk-3JANYJYS.cjs.map → chunk-YOVZMN4P.cjs.map} +1 -1
- package/dist/{chunk-TY56BKSK.cjs → chunk-YQ3XC6C2.cjs} +4 -4
- package/dist/{chunk-TY56BKSK.cjs.map → chunk-YQ3XC6C2.cjs.map} +1 -1
- package/dist/{chunk-JLIRFWUG.js → chunk-ZJAYFSVI.js} +22 -9
- package/dist/chunk-ZJAYFSVI.js.map +1 -0
- package/dist/{chunk-MU7IAA7G.js → chunk-ZLFFDPXC.js} +9 -9
- package/dist/{chunk-MU7IAA7G.js.map → chunk-ZLFFDPXC.js.map} +1 -1
- package/dist/{chunk-JLRLCBJ4.cjs → chunk-ZOFRNNZD.cjs} +4 -4
- package/dist/{chunk-JLRLCBJ4.cjs.map → chunk-ZOFRNNZD.cjs.map} +1 -1
- package/dist/{chunk-MPQJE3G4.cjs → chunk-ZWLFHSON.cjs} +15 -15
- package/dist/{chunk-MPQJE3G4.cjs.map → chunk-ZWLFHSON.cjs.map} +1 -1
- package/dist/compact-session-3U2JYTVG.js +25 -0
- package/dist/{compact-session-4HAUP3HK.js.map → compact-session-3U2JYTVG.js.map} +1 -1
- package/dist/{compact-session-Y56D3S57.cjs → compact-session-NM56BREJ.cjs} +24 -24
- package/dist/{compact-session-Y56D3S57.cjs.map → compact-session-NM56BREJ.cjs.map} +1 -1
- package/dist/compaction.cjs +18 -18
- package/dist/compaction.js +3 -3
- package/dist/concurrency.cjs +6 -6
- package/dist/concurrency.js +4 -4
- package/dist/context/index.cjs +9 -9
- package/dist/context/index.js +6 -6
- package/dist/context-4RSI3AXT.cjs +24 -0
- package/dist/{context-B5J4GX2F.cjs.map → context-4RSI3AXT.cjs.map} +1 -1
- package/dist/context-7EBZST3L.js +7 -0
- package/dist/{context-3FZRAO5K.js.map → context-7EBZST3L.js.map} +1 -1
- package/dist/{cron-Blj6wMYg.d.cts → cron-BWDYetbq.d.cts} +2 -2
- package/dist/{cron-BPccQCO8.d.ts → cron-CHn-F0fC.d.ts} +2 -2
- package/dist/cron.cjs +42 -42
- package/dist/cron.d.cts +4 -4
- package/dist/cron.d.ts +4 -4
- package/dist/cron.js +41 -41
- package/dist/{errors-BWOJeWTZ.d.cts → errors-BiDQFWTG.d.cts} +10 -7
- package/dist/{errors-DDvG18IB.d.ts → errors-Pf22PJJ-.d.ts} +10 -7
- package/dist/errors.cjs +23 -23
- package/dist/errors.d.cts +2 -3
- package/dist/errors.d.ts +2 -3
- package/dist/errors.js +2 -2
- package/dist/eval.cjs +54 -54
- package/dist/eval.js +42 -42
- package/dist/{executor-3NI7KNL4.js → executor-3D2UDJHW.js} +8 -8
- package/dist/{executor-3NI7KNL4.js.map → executor-3D2UDJHW.js.map} +1 -1
- package/dist/{executor-2SUGI3ZT.cjs → executor-HKHT4CWS.cjs} +26 -26
- package/dist/{executor-2SUGI3ZT.cjs.map → executor-HKHT4CWS.cjs.map} +1 -1
- package/dist/filesystem/index.cjs +12 -12
- package/dist/filesystem/index.js +3 -3
- package/dist/fs-session-store-6ZCCJX3K.js +11 -0
- package/dist/{fs-session-store-3VI4CPTX.js.map → fs-session-store-6ZCCJX3K.js.map} +1 -1
- package/dist/fs-session-store-S2AXVJIX.cjs +20 -0
- package/dist/{fs-session-store-IQ6PEHYF.cjs.map → fs-session-store-S2AXVJIX.cjs.map} +1 -1
- package/dist/generate-object-S7MNVF2V.js +7 -0
- package/dist/{generate-object-NTKAWE7D.js.map → generate-object-S7MNVF2V.js.map} +1 -1
- package/dist/generate-object-XBWD2PU7.cjs +20 -0
- package/dist/{generate-object-YHTWG5DB.cjs.map → generate-object-XBWD2PU7.cjs.map} +1 -1
- package/dist/index-manager-HO456AVW.cjs +27 -0
- package/dist/{index-manager-YGXRFJMZ.cjs.map → index-manager-HO456AVW.cjs.map} +1 -1
- package/dist/index-manager-QDH56FER.js +18 -0
- package/dist/{index-manager-JVYIVTZG.js.map → index-manager-QDH56FER.js.map} +1 -1
- package/dist/index.cjs +172 -172
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +59 -59
- package/dist/{inject-session-R73PRM7B.cjs → inject-session-JVTZNQF6.cjs} +9 -9
- package/dist/{inject-session-R73PRM7B.cjs.map → inject-session-JVTZNQF6.cjs.map} +1 -1
- package/dist/{inject-session-LJX5JGV5.js → inject-session-THABTKJY.js} +7 -7
- package/dist/{inject-session-LJX5JGV5.js.map → inject-session-THABTKJY.js.map} +1 -1
- package/dist/interactive/index.cjs +4 -4
- package/dist/interactive/index.js +2 -2
- package/dist/internal/budget/normalize-usage.d.ts +5 -1
- package/dist/internal/error-mappers/openai-compatible.d.ts +3 -1
- package/dist/internal/memory/adapters/index.cjs +8 -8
- package/dist/internal/memory/adapters/index.js +7 -7
- package/dist/internal/memory/storage/index.cjs +42 -42
- package/dist/internal/memory/storage/index.js +10 -10
- package/dist/internal/persistence/index.cjs +25 -25
- package/dist/internal/persistence/index.js +8 -8
- package/dist/internal/providers/catalog-schema.d.ts +1 -1
- package/dist/internal/runtime/lifecycle/throw-on-error-run.d.ts +18 -0
- package/dist/internal/runtime/model-selection.d.ts +1 -1
- package/dist/internal/runtime/system-prompt/native-reasoning.d.ts +2 -2
- package/dist/internal/runtime/typed-run-error.d.ts +16 -0
- package/dist/internal/security/index.cjs +13 -13
- package/dist/internal/security/index.js +3 -3
- package/dist/judge-call-4LR4IFVT.js +5 -0
- package/dist/{judge-call-LOL5FQEX.js.map → judge-call-4LR4IFVT.js.map} +1 -1
- package/dist/judge-call-ZTUQMQRL.cjs +22 -0
- package/dist/{judge-call-AQQYK6VT.cjs.map → judge-call-ZTUQMQRL.cjs.map} +1 -1
- package/dist/mcp-auth.cjs +14 -14
- package/dist/mcp-auth.js +4 -4
- package/dist/models.cjs +22 -22
- package/dist/models.js +8 -8
- package/dist/oauth-transaction-store-OMX436YB.cjs +38 -0
- package/dist/{oauth-transaction-store-ZINDV7AF.cjs.map → oauth-transaction-store-OMX436YB.cjs.map} +1 -1
- package/dist/oauth-transaction-store-U34YLEGP.js +5 -0
- package/dist/{oauth-transaction-store-NP2YHZ6D.js.map → oauth-transaction-store-U34YLEGP.js.map} +1 -1
- package/dist/path-safety.cjs +10 -10
- package/dist/path-safety.js +3 -3
- package/dist/persistence.cjs +31 -31
- package/dist/persistence.js +10 -10
- package/dist/project.cjs +5 -5
- package/dist/project.js +3 -3
- package/dist/providers.cjs +9 -9
- package/dist/providers.js +7 -7
- package/dist/registry-5TDAF7RK.js +9 -0
- package/dist/{registry-5FK42CRV.js.map → registry-5TDAF7RK.js.map} +1 -1
- package/dist/registry-W7TSCLQO.cjs +46 -0
- package/dist/{registry-BVTNO73E.cjs.map → registry-W7TSCLQO.cjs.map} +1 -1
- package/dist/retry.cjs +4 -4
- package/dist/retry.js +3 -3
- package/dist/{run-until-DG2KXWB2.cjs → run-until-4KHVF7YC.cjs} +8 -4
- package/dist/run-until-4KHVF7YC.cjs.map +1 -0
- package/dist/{run-until-6D6TQ4JN.js → run-until-G6BW2DWM.js} +8 -4
- package/dist/run-until-G6BW2DWM.js.map +1 -0
- package/dist/sandbox/index.cjs +114 -41
- package/dist/sandbox/index.cjs.map +1 -1
- package/dist/sandbox/index.js +99 -26
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/linux-sandbox.d.cts +24 -4
- package/dist/sandbox/linux-sandbox.d.ts +24 -4
- package/dist/sandbox/seccomp-delivery.d.cts +46 -0
- package/dist/sandbox/seccomp-delivery.d.ts +46 -0
- package/dist/{run-DsA5Ip2i.d.cts → sdk-agent-4-dXI2GS.d.cts} +1500 -753
- package/dist/{run-DsA5Ip2i.d.ts → sdk-agent-CTOJlWA_.d.ts} +1500 -753
- package/dist/server/auth/index.cjs +22 -22
- package/dist/server/auth/index.js +7 -7
- package/dist/server/errors-envelope.cjs +14 -14
- package/dist/server/errors-envelope.d.cts +2 -3
- package/dist/server/errors-envelope.d.ts +2 -3
- package/dist/server/errors-envelope.js +3 -3
- package/dist/session-transcript-7TZ6HXSX.cjs +62 -0
- package/dist/{session-transcript-RACG7BK2.cjs.map → session-transcript-7TZ6HXSX.cjs.map} +1 -1
- package/dist/session-transcript-BSHZYYY6.js +5 -0
- package/dist/{session-transcript-AR7IBICT.js.map → session-transcript-BSHZYYY6.js.map} +1 -1
- package/dist/skills.cjs +8 -8
- package/dist/skills.js +5 -5
- package/dist/stream-object-R6SMYV4R.js +7 -0
- package/dist/{stream-object-QAEHG4YO.js.map → stream-object-R6SMYV4R.js.map} +1 -1
- package/dist/stream-object-TEEK3CAX.cjs +20 -0
- package/dist/{stream-object-TKVGGA54.cjs.map → stream-object-TEEK3CAX.cjs.map} +1 -1
- package/dist/subagents-loader-TXOA5EJJ.cjs +16 -0
- package/dist/{subagents-loader-5TZCAIYQ.cjs.map → subagents-loader-TXOA5EJJ.cjs.map} +1 -1
- package/dist/subagents-loader-Z744TAKD.js +7 -0
- package/dist/{subagents-loader-PEV3PD4Q.js.map → subagents-loader-Z744TAKD.js.map} +1 -1
- package/dist/subagents-loader.cjs +7 -7
- package/dist/subagents-loader.d.cts +4 -3
- package/dist/subagents-loader.d.ts +4 -3
- package/dist/subagents-loader.js +5 -5
- package/dist/subscription/index.cjs +3 -3
- package/dist/subscription/index.js +2 -2
- package/dist/task-store.cjs +7 -7
- package/dist/task-store.js +4 -4
- package/dist/types/agent-prims.d.ts +16 -0
- package/dist/types/agent.d.ts +15 -6
- package/dist/types/run.d.ts +14 -14
- package/dist/workflow.cjs +27 -27
- package/dist/workflow.d.cts +2 -2
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +6 -6
- package/docs/error-codes.md +24 -23
- package/docs/harness-capability-map.md +5 -5
- package/package.json +1 -1
- package/dist/agent-DFV52V6E.js +0 -62
- package/dist/agent-OKP4LHG2.cjs +0 -71
- package/dist/agent-session-store-ODD3IZCW.js +0 -6
- package/dist/agent-session-store-SVEJHSUA.cjs +0 -23
- package/dist/chunk-35KBC4BX.cjs.map +0 -1
- package/dist/chunk-6LHQPOMI.cjs.map +0 -1
- package/dist/chunk-7NQ7HJ3E.cjs.map +0 -1
- package/dist/chunk-B5MHRQJZ.js.map +0 -1
- package/dist/chunk-BBJ77S62.js.map +0 -1
- package/dist/chunk-CZJ6Q7CW.js.map +0 -1
- package/dist/chunk-GMSRRJ53.js.map +0 -1
- package/dist/chunk-JLIRFWUG.js.map +0 -1
- package/dist/chunk-MHIBYKWA.js.map +0 -1
- package/dist/chunk-N7KS3TIR.cjs.map +0 -1
- package/dist/chunk-P5N3Q2NH.cjs.map +0 -1
- package/dist/chunk-SGC7LSHF.cjs +0 -18
- package/dist/chunk-UNFBHV46.cjs.map +0 -1
- package/dist/compact-session-4HAUP3HK.js +0 -25
- package/dist/context-3FZRAO5K.js +0 -7
- package/dist/context-B5J4GX2F.cjs +0 -24
- package/dist/fs-session-store-3VI4CPTX.js +0 -11
- package/dist/fs-session-store-IQ6PEHYF.cjs +0 -20
- package/dist/generate-object-NTKAWE7D.js +0 -7
- package/dist/generate-object-YHTWG5DB.cjs +0 -20
- package/dist/index-manager-JVYIVTZG.js +0 -18
- package/dist/index-manager-YGXRFJMZ.cjs +0 -27
- package/dist/judge-call-AQQYK6VT.cjs +0 -22
- package/dist/judge-call-LOL5FQEX.js +0 -5
- package/dist/oauth-transaction-store-NP2YHZ6D.js +0 -5
- package/dist/oauth-transaction-store-ZINDV7AF.cjs +0 -38
- package/dist/registry-5FK42CRV.js +0 -9
- package/dist/registry-BVTNO73E.cjs +0 -46
- package/dist/run-until-6D6TQ4JN.js.map +0 -1
- package/dist/run-until-DG2KXWB2.cjs.map +0 -1
- package/dist/sdk-agent-dNwjwCt5.d.cts +0 -940
- package/dist/sdk-agent-wEOLXD3F.d.ts +0 -940
- package/dist/session-transcript-AR7IBICT.js +0 -5
- package/dist/session-transcript-RACG7BK2.cjs +0 -62
- package/dist/stream-object-QAEHG4YO.js +0 -7
- package/dist/stream-object-TKVGGA54.cjs +0 -20
- package/dist/subagents-loader-5TZCAIYQ.cjs +0 -16
- package/dist/subagents-loader-PEV3PD4Q.js +0 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/auth/errors.ts"],"names":["TheokitAgentError"],"mappings":";;;;;AAYO,IAAM,eAAA,GAAN,cAA8BA,mCAAA,CAAkB;AAAA,EACnC,IAAA,GAAO,iBAAA;AAAA,EACP,IAAA;AAAA,EAElB,WAAA,CAAY,MAAc,OAAA,EAAiB;AAEzC,IAAA,KAAA,CAAM,CAAA,CAAA,EAAI,IAAI,CAAA,EAAA,EAAK,OAAO,IAAI,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,CAAA;AAC1D,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAMO,IAAM,yBAAA,GAAN,cAAwCA,mCAAA,CAAkB;AAAA,EAC7C,IAAA,GAAO,2BAAA;AAAA,EAChB,YAAA;AAAA,EAET,YAAY,YAAA,EAAsB;AAEhC,IAAA,KAAA;AAAA,MACE,6BAA6B,YAAY,CAAA,mDAAA,CAAA;AAAA,MACzC,EAAE,IAAA,EAAM,yBAAA,EAA2B,WAAA,EAAa,KAAA;AAAM,KACxD;AACA,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AACF;AAcO,IAAM,iBAAA,GAAN,cAAgCA,mCAAA,CAAkB;AAAA;AAAA,EAErC,IAAA,GAAe,mBAAA;AAAA,EACf,IAAA;AAAA,EAElB,WAAA,CAAY,MAAc,OAAA,EAAkB;AAG1C,IAAA,KAAA,CAAM,OAAA,IAAW,yBAAyB,IAAI,CAAA,CAAA,EAAI,EAAE,IAAA,EAAM,WAAA,EAAa,OAAO,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAUO,IAAM,kBAAA,GAAN,cAAiC,iBAAA,CAAkB;AAAA,EACtC,IAAA,GAAe,oBAAA;AAAA,EACxB,gBAAA;AAAA,EAET,YAAY,gBAAA,EAA2B;AACrC,IAAA,KAAA,CAAM,uBAAA,EAAyB,oBAAoB,mCAAmC,CAAA;AACtF,IAAA,IAAA,CAAK,gBAAA,GAAmB,gBAAA;AAAA,EAC1B;AACF","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/server/auth/errors.ts"],"names":["TheokitAgentError"],"mappings":";;;;;AAYO,IAAM,eAAA,GAAN,cAA8BA,mCAAA,CAAkB;AAAA,EACnC,IAAA,GAAO,iBAAA;AAAA,EACP,IAAA;AAAA,EAElB,WAAA,CAAY,MAAc,OAAA,EAAiB;AAEzC,IAAA,KAAA,CAAM,CAAA,CAAA,EAAI,IAAI,CAAA,EAAA,EAAK,OAAO,IAAI,EAAE,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,CAAA;AAC1D,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAMO,IAAM,yBAAA,GAAN,cAAwCA,mCAAA,CAAkB;AAAA,EAC7C,IAAA,GAAO,2BAAA;AAAA,EAChB,YAAA;AAAA,EAET,YAAY,YAAA,EAAsB;AAEhC,IAAA,KAAA;AAAA,MACE,6BAA6B,YAAY,CAAA,mDAAA,CAAA;AAAA,MACzC,EAAE,IAAA,EAAM,yBAAA,EAA2B,WAAA,EAAa,KAAA;AAAM,KACxD;AACA,IAAA,IAAA,CAAK,YAAA,GAAe,YAAA;AAAA,EACtB;AACF;AAcO,IAAM,iBAAA,GAAN,cAAgCA,mCAAA,CAAkB;AAAA;AAAA,EAErC,IAAA,GAAe,mBAAA;AAAA,EACf,IAAA;AAAA,EAElB,WAAA,CAAY,MAAc,OAAA,EAAkB;AAG1C,IAAA,KAAA,CAAM,OAAA,IAAW,yBAAyB,IAAI,CAAA,CAAA,EAAI,EAAE,IAAA,EAAM,WAAA,EAAa,OAAO,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAUO,IAAM,kBAAA,GAAN,cAAiC,iBAAA,CAAkB;AAAA,EACtC,IAAA,GAAe,oBAAA;AAAA,EACxB,gBAAA;AAAA,EAET,YAAY,gBAAA,EAA2B;AACrC,IAAA,KAAA,CAAM,uBAAA,EAAyB,oBAAoB,mCAAmC,CAAA;AACtF,IAAA,IAAA,CAAK,gBAAA,GAAmB,gBAAA;AAAA,EAC1B;AACF","file":"chunk-IOFMRS6T.cjs","sourcesContent":["/**\n * Typed error classes — `@theokit/sdk/server/auth`.\n *\n * Plan T1.2 + v1.1 EC-1 (AuthCancelledError for OAuth provider error response RFC 6749 §4.1.2.1).\n */\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * Thrown at `defineAuth()` time when configuration is invalid\n * (e.g., duplicate provider name, invalid email shape per EC-V1-12).\n */\nexport class AuthConfigError extends TheokitAgentError {\n override readonly name = \"AuthConfigError\";\n override readonly code: string;\n\n constructor(code: string, message: string) {\n // Not retryable: a misconfiguration is fixed by an operator, not by trying again.\n super(`[${code}] ${message}`, { code, isRetryable: false });\n this.code = code;\n }\n}\n\n/**\n * Thrown at `startSignIn(providerName, ...)` or `finishSignIn(providerName, ...)`\n * when the named provider is not registered in `providers[]`.\n */\nexport class AuthProviderNotFoundError extends TheokitAgentError {\n override readonly name = \"AuthProviderNotFoundError\";\n readonly providerName: string;\n\n constructor(providerName: string) {\n // Not retryable: the provider is absent from the configuration until someone adds it.\n super(\n `Auth provider not found: '${providerName}'. Register it in defineAuth({ providers: [...] }).`,\n { code: \"auth_provider_not_found\", isRetryable: false },\n );\n this.providerName = providerName;\n }\n}\n\n/**\n * Thrown during OAuth callback handling for state mismatches, expired\n * transactions, missing query params, or provider 4xx/5xx errors.\n *\n * Typed `code` field lets consumers branch on cause:\n * - 'oauth_transaction_expired' — cookie tx > 10min old (per ADR D5)\n * - 'oauth_state_mismatch' — query state ≠ cookie state (CSRF defense per RFC 6749 §10.12)\n * - 'oauth_provider_error' — non-access_denied error in callback URL\n * - 'oauth_token_exchange_failed' — provider rejected code-for-tokens swap\n * - 'oauth_userinfo_failed' — userinfo endpoint returned error\n * - 'oauth_missing_code_or_state' — required query params absent\n */\nexport class AuthCallbackError extends TheokitAgentError {\n // Widened to string on purpose: AuthCancelledError below narrows it to its own name.\n override readonly name: string = \"AuthCallbackError\";\n override readonly code: string;\n\n constructor(code: string, message?: string) {\n // Not retryable here: the code comes from the provider on a callback that already happened;\n // recovering means restarting the flow, not re-throwing the same callback.\n super(message ?? `OAuth callback error: ${code}`, { code, isRetryable: false });\n this.code = code;\n }\n}\n\n/**\n * Per v1.1 EC-1 MUST FIX — typed subclass of AuthCallbackError for the\n * specific case where user declined consent at provider screen.\n *\n * OAuth 2.0 RFC 6749 §4.1.2.1: provider redirects with `?error=access_denied`.\n * Apps can catch this distinctly from network/server errors to render\n * \"Login cancelled — try again\" UX instead of opaque \"callback failed\".\n */\nexport class AuthCancelledError extends AuthCallbackError {\n override readonly name: string = \"AuthCancelledError\";\n readonly errorDescription?: string;\n\n constructor(errorDescription?: string) {\n super(\"user_declined_consent\", errorDescription ?? \"User declined consent at provider\");\n this.errorDescription = errorDescription;\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { warnOnce } from './chunk-
|
|
1
|
+
import { warnOnce } from './chunk-XJ3WU5CZ.js';
|
|
2
2
|
import { splitFrontmatter, parseSimpleYaml } from './chunk-KVNWIAO4.js';
|
|
3
3
|
import { findGitRoot, DEFAULT_DISCOVERY_SPECS, admittedSpecs, withheldSpecs, walkUpForFile, walkUpForGlob } from './chunk-4QCJAJAG.js';
|
|
4
4
|
import { insideRoot } from './chunk-UKMBRMGT.js';
|
|
@@ -286,5 +286,5 @@ async function loadPlainMarkdownSource(spec, path, id, opts, gitRoot) {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
export { DEFAULT_MAX_BYTES_PER_FILE, parseRules, resolveImports, runDiscovery, shouldActivateRule, truncateWithMarker };
|
|
289
|
-
//# sourceMappingURL=chunk-
|
|
290
|
-
//# sourceMappingURL=chunk-
|
|
289
|
+
//# sourceMappingURL=chunk-KCM3RMKR.js.map
|
|
290
|
+
//# sourceMappingURL=chunk-KCM3RMKR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/context/context-loaders.ts","../src/internal/runtime/context/context-import-resolver.ts","../src/internal/runtime/context/context-glob.ts","../src/internal/runtime/context/context-rules-frontmatter.ts","../src/internal/runtime/context/context-mdc-parser.ts","../src/internal/runtime/context/context-discovery-runner.ts"],"names":["resolvePath","z","dirname","readFile"],"mappings":"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","file":"chunk-7QJYKVP6.js","sourcesContent":["/**\n * Context file loaders + truncation (T1.2, ADRs D154 / D155 / D159).\n *\n * Reads context source files from disk, applies the per-file size cap\n * with 70%/20% head/tail truncation, and emits the\n * `context_files_truncated` telemetry counter when the cap fires.\n * Pure (no I/O) for `truncateWithMarker` — testable in isolation.\n *\n * @internal\n */\n\nimport { readFile } from \"node:fs/promises\";\n\nconst HEAD_RATIO = 0.7;\nconst TAIL_RATIO = 0.2;\nconst MARKER = \"\\n\\n…[truncated by theokit]\\n\\n\";\n\n/** Default per-file cap (40k chars ≈ 10k tokens). D155. */\nexport const DEFAULT_MAX_BYTES_PER_FILE = 40_000;\n\nexport interface LoadedSource {\n /** Absolute path the content came from. */\n readonly source: string;\n readonly content: string;\n readonly originalBytes: number;\n readonly truncated: boolean;\n}\n\nexport interface TruncateResult {\n readonly truncated: boolean;\n readonly finalContent: string;\n}\n\n/**\n * Cap `content` at `max` characters with 70%/20% head/tail truncation\n * and an explicit marker. Pure — no I/O, no telemetry.\n *\n * EC-C: when `max <= MARKER.length`, skip the marker entirely and\n * return a head-only slice. Without this guard, `budget = max -\n * MARKER.length` would go negative and `content.slice(-tailBytes)`\n * would slice from the END of the string rather than truncating.\n *\n * EC-H: codepoint integrity — `String.slice()` operates on UTF-16\n * code units, which can split surrogate pairs. We accept that\n * trailing/leading replacement chars (U+FFFD) may appear at boundaries.\n * Modern LLMs tolerate them; byte-exact safety would require Buffer\n * slicing + TextDecoder with `fatal: true` retry, which adds complexity\n * without value.\n *\n * Deliberately untagged for the public surface. A tag was here and no entry exported it, so the\n * docblock promised a symbol\n * nobody could import. Exporting it was the other option and the wrong one: this is a pure\n * truncation helper whose docblock describes implementation edge cases (EC-C, EC-H), not a\n * contract — and an export is a semver commitment somebody then has to keep.\n */\nexport function truncateWithMarker(content: string, max: number): TruncateResult {\n if (content.length <= max) {\n return { truncated: false, finalContent: content };\n }\n // EC-C: max too small to fit even the marker — return head-only slice.\n if (max <= MARKER.length) {\n return { truncated: true, finalContent: content.slice(0, max) };\n }\n const budget = max - MARKER.length;\n const headBytes = Math.floor(budget * (HEAD_RATIO / (HEAD_RATIO + TAIL_RATIO)));\n const tailBytes = budget - headBytes;\n return {\n truncated: true,\n finalContent: content.slice(0, headBytes) + MARKER + content.slice(-tailBytes),\n };\n}\n\n/**\n * Read a file from disk and apply the per-file cap. Emits telemetry\n * counter when truncation fires.\n *\n * EC-G: file deleted between discovery and read (FS race) → returns\n * `undefined`, never throws.\n *\n * @internal\n */\nexport async function loadPlainMarkdown(\n absPath: string,\n opts: { maxBytesPerFile?: number } = {},\n): Promise<LoadedSource | undefined> {\n const max = opts.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE;\n let content: string;\n try {\n content = await readFile(absPath, \"utf8\");\n } catch {\n // EC-G: ENOENT / EACCES / etc → caller treats as missing.\n return undefined;\n }\n const { truncated, finalContent } = truncateWithMarker(content, max);\n if (truncated) {\n emitTruncationCounter(absPath);\n }\n return {\n source: absPath,\n content: finalContent,\n originalBytes: content.length,\n truncated,\n };\n}\n\n/**\n * EC-L: telemetry no-op when OTel is not imported. We import the tracer\n * lazily and use `safeCall` so the lookup never throws and never pulls\n * OTel into the import graph for users without `@opentelemetry/api`\n * installed.\n *\n * @internal\n */\nfunction emitTruncationCounter(source: string): void {\n // Lazy resolution via globalThis avoids static import of telemetry/tracer.\n // Tests can spy via the same path; production users without OTel see no-op.\n const tracer = (\n globalThis as { __theokit_tracer?: { inc?: (k: string, attrs: unknown) => void } }\n ).__theokit_tracer;\n if (tracer?.inc === undefined) return;\n try {\n tracer.inc(\"context_files_truncated\", { file: source });\n } catch {\n // Telemetry must never break the loader.\n }\n}\n","/**\n * `@path/to/file` import resolver (T2.1, ADR D156).\n *\n * Anthropic/Gemini convention: lines that are EXACTLY `@path` get\n * replaced with the imported file's content, recursively (5-hop cap),\n * with cycle detection. Inline `see @x.md, also @y.md` references are\n * NOT resolved (EC-Q — own-line only, mirrors Anthropic's actual\n * behavior).\n *\n * EC-D fix: every imported file is itself capped at `maxBytesPerFile`\n * via `loadPlainMarkdown` BEFORE concatenation. Prevents a CLAUDE.md\n * with 5 imports of 30k each from ballooning to 150k of imported\n * content before the outer cap fires.\n *\n * @internal\n */\n\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { insideRoot } from \"../../security/path-containment.js\";\nimport { loadPlainMarkdown } from \"./context-loaders.js\";\n\n/** EC-Q: line-anchored. `@path` must be alone on its line. */\nconst IMPORT_RE = /^@(\\S+)\\s*$/gm;\nconst MAX_HOPS = 5;\n\nexport interface ResolveImportsOptions {\n /** Absolute paths already resolved (cycle detection). */\n readonly visited: Set<string>;\n /** Current recursion depth — caps at MAX_HOPS. */\n readonly depth: number;\n /** Per-import file cap (EC-D). Forwarded to loadPlainMarkdown. */\n readonly maxBytesPerFile: number;\n /**\n * The directory an import may not escape. When set, a target resolving outside it is\n * refused and replaced with a placeholder.\n *\n * The file carrying the import is REPOSITORY-CONTROLLED — `CLAUDE.md` and `GEMINI.md`\n * are the two default specs with `followImports: true`, and both are found by\n * `git-root-walk` inside the tree the agent was pointed at. Without a root, a cloned\n * repository could name `@~/.ssh/id_rsa` or any absolute path and have its contents\n * inlined into the system prompt, and from there sent to the model provider. The\n * traversal guard that already existed (`isSafePattern`) guards the discovery PATTERN,\n * not the import TARGET, so it never saw this.\n *\n * OPTIONAL, so a caller outside the discovery path keeps the previous behaviour rather\n * than breaking on an upgrade. `runDiscovery` always supplies `gitRoot ?? cwd` — the\n * same value it already uses to keep absolute paths out of `<source name=\"\">`.\n */\n readonly projectRoot?: string;\n}\n\n/**\n * Resolve `@path` directives in `content`. Each match is replaced with\n * the imported file content (already cap-truncated per EC-D), and\n * recursion continues on the resolved content until MAX_HOPS or cycle.\n *\n * Failure modes (placeholders, never throws):\n * - file not found → `[@import not found: <path>]`\n * - cycle detected → `[@import cycle detected: <path>]`\n * - depth exceeded → trailing `\\n\\n…[@import depth limit 5 reached]\\n\\n`\n *\n * @internal\n */\nexport async function resolveImports(\n content: string,\n basePath: string,\n opts: ResolveImportsOptions,\n): Promise<string> {\n if (opts.depth >= MAX_HOPS) {\n return `${content}\\n\\n…[@import depth limit ${MAX_HOPS} reached]\\n\\n`;\n }\n const baseDir = dirname(basePath);\n return replaceAsync(content, IMPORT_RE, async (raw) => {\n const absolute = resolveImportPath(raw, baseDir);\n // Containment BEFORE the read. Reporting the refusal after loading the file would\n // already have put the bytes in memory, and the placeholder names the path the author\n // wrote rather than the resolved one — echoing `/home/<user>/.ssh/id_rsa` back into the\n // prompt would leak the layout of the machine to the same untrusted document.\n if (opts.projectRoot !== undefined && !insideRoot(absolute, opts.projectRoot)) {\n return `[@import outside the project root, refused: ${raw}]`;\n }\n if (opts.visited.has(absolute)) {\n return `[@import cycle detected: ${raw}]`;\n }\n opts.visited.add(absolute);\n const loaded = await loadPlainMarkdown(absolute, { maxBytesPerFile: opts.maxBytesPerFile });\n if (loaded === undefined) {\n return `[@import not found: ${raw}]`;\n }\n // Recurse with same visited set + incremented depth. `projectRoot` is FORWARDED: an\n // imported file is repository-controlled too, so a root that applied only at depth 0\n // would be escapable in one extra hop.\n return resolveImports(loaded.content, absolute, {\n visited: opts.visited,\n depth: opts.depth + 1,\n maxBytesPerFile: opts.maxBytesPerFile,\n ...(opts.projectRoot === undefined ? {} : { projectRoot: opts.projectRoot }),\n });\n });\n}\n\nfunction resolveImportPath(raw: string, baseDir: string): string {\n if (raw.startsWith(\"~/\")) {\n return resolvePath(join(homedir(), raw.slice(2)));\n }\n if (isAbsolute(raw)) {\n return resolvePath(raw);\n }\n return resolvePath(join(baseDir, raw));\n}\n\n/**\n * Async equivalent of `String.prototype.replace` — sequential. Returns\n * `content` with every `re` match replaced by `await replacer(match)`.\n *\n * @internal\n */\nasync function replaceAsync(\n content: string,\n re: RegExp,\n replacer: (raw: string) => Promise<string>,\n): Promise<string> {\n const matches: Array<{ match: string; raw: string; index: number }> = [];\n // Reset lastIndex; clone the regex to avoid stateful traps.\n const localRe = new RegExp(re.source, re.flags);\n let m: RegExpExecArray | null;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop\n while ((m = localRe.exec(content)) !== null) {\n matches.push({ match: m[0], raw: m[1] ?? \"\", index: m.index });\n if (m.index === localRe.lastIndex) localRe.lastIndex += 1;\n }\n if (matches.length === 0) return content;\n let result = \"\";\n let cursor = 0;\n for (const entry of matches) {\n result += content.slice(cursor, entry.index);\n result += await replacer(entry.raw);\n cursor = entry.index + entry.match.length;\n }\n result += content.slice(cursor);\n return result;\n}\n","/**\n * Shared glob → RegExp compiler for context discovery (extracted from the\n * MDC parser so the `.cursor/rules/*.mdc` and `.theokit/rules/*.md` parsers\n * share ONE implementation — DRY, no new dependency).\n *\n * Supports `**` (any depth), `*` (single path segment, no `/`), `?` (single\n * non-separator char). Sufficient for the patterns Cursor and Claude Code\n * themselves recommend.\n *\n * @internal\n */\nexport function globToRegex(glob: string): RegExp {\n // Support **/ (zero-or-more path segments), ** (any depth), * (single\n // segment), ? (single non-separator char). `**/` collapses so `src/**/*.ts`\n // matches `src/foo.ts` AND `src/a/b/foo.ts` — the semantics Cursor and Claude\n // Code document (\"src/**/* → all files under src/\"). `*` and `?` never cross a\n // `/` (strict glob segment semantics). Escape other regex metas.\n const compiled = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*\\//g, \"::GLOBSTAR_SLASH::\")\n .replace(/\\*\\*/g, \"::GLOBSTAR::\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/\\?/g, \"[^/]\")\n .replace(/::GLOBSTAR_SLASH::/g, \"(?:.*/)?\")\n .replace(/::GLOBSTAR::/g, \".*\");\n return new RegExp(`^${compiled}$`);\n}\n\n/**\n * True when any of `patterns` glob-matches any of `paths`. Central helper so\n * both rule parsers agree on activation semantics.\n *\n * @internal\n */\nexport function anyGlobMatches(\n patterns: ReadonlyArray<string>,\n paths: ReadonlyArray<string>,\n): boolean {\n if (patterns.length === 0 || paths.length === 0) return false;\n const res = patterns.map(globToRegex);\n return paths.some((p) => res.some((re) => re.test(p)));\n}\n","/**\n * Parser for `.theokit/rules/*.md` — theokit-native path-scoped rules,\n * mirroring Claude Code's `.claude/rules/`.\n *\n * Frontmatter fields:\n * - `paths` — glob-pattern array (Claude Code parity: `.claude/rules` uses `paths:`).\n * - `globs` — glob-pattern array (Cursor-compatible alias; unioned with `paths`).\n * - `alwaysApply` — activate unconditionally (no scope needed).\n * - `enabled` — set `false` to disable the rule entirely (wins over everything).\n * - `description` — human note; not used for activation in v1.\n *\n * Activation (`shouldActivateRule`):\n * - `enabled: false` → never activates.\n * - `alwaysApply: true` → always activates.\n * - otherwise → activates iff a `paths`/`globs` pattern matches an in-scope\n * file (the caller's `agent.send(..., { contextPaths })`). With no in-scope\n * files, a scoped rule stays dormant (parity with Cursor `.mdc`).\n *\n * A file with no `---` frontmatter is treated as an unconditional rule\n * (`alwaysApply: true`). Malformed YAML → `undefined` (caller drops + counts).\n *\n * Glob + YAML subset are shared with the MDC parser (DRY, no new dependency).\n *\n * NOTE — no internal-visibility tag in this block. `tsconfig.base.json` sets `stripInternal: true`,\n * and TypeScript scans EVERY leading comment range of the declaration that follows, including the\n * import right below this one. The tag that used to sit here deleted that import from the emitted\n * `.d.ts`, leaving the types it binds unresolvable for any consumer running type-aware lint\n * (usetheodev/theokit-sdk#283 records the same trap on a declaration).\n */\n\nimport { z } from \"zod\";\n\nimport { anyGlobMatches } from \"./context-glob.js\";\nimport { parseSimpleYaml, splitFrontmatter } from \"./context-yaml-lite.js\";\n\n/**\n * YAML frontmatter schema for `.theokit/rules/*.md` files.\n *\n * Every field is optional, and unknown keys are dropped rather than rejected — `safeParse` failing\n * therefore means a declared key had the wrong TYPE (`paths` as a bare string, `enabled` as the\n * string `\"false\"`), never that an unexpected key was present.\n *\n * Exported because {@link RulesFrontmatter} is inferred from it and appears in the signature of\n * {@link shouldActivateRule}, which `@theokit/sdk/context` publishes: the constant has to reach the\n * emitted declarations for that inferred type to resolve in a consumer's project.\n */\nexport const RulesFrontmatterSchema = z.object({\n description: z.string().optional(),\n paths: z.array(z.string()).optional(),\n globs: z.array(z.string()).optional(),\n alwaysApply: z.boolean().optional(),\n enabled: z.boolean().optional(),\n});\n\nexport type RulesFrontmatter = z.infer<typeof RulesFrontmatterSchema>;\n\nexport interface RulesParseResult {\n readonly frontmatter: RulesFrontmatter;\n readonly body: string;\n}\n\n/**\n * Split a `.theokit/rules/*.md` document into its frontmatter and its body.\n *\n * A file with no `---` fence is not an error: the whole text becomes the body and the frontmatter\n * is synthesised as `{ alwaysApply: true }`, so a plain markdown rule with no metadata applies\n * unconditionally. That default is the one thing to know before adding a fence — adding one and\n * omitting both `alwaysApply` and a path pattern turns an always-on rule into a dormant one.\n *\n * `undefined` means the fence was there and its contents did not survive: YAML the lite parser\n * could not read, or a shape the schema rejected (`paths` as a bare string rather than a list,\n * `enabled` as `\"false\"` rather than `false`). It never throws, and it does not say WHICH of the\n * two happened — the caller drops the file and counts it.\n *\n * Unknown frontmatter keys are dropped rather than rejected, so a misspelled `alwaysAply` parses\n * fine and simply has no effect.\n *\n * Nothing here decides whether the rule applies; that is `shouldActivateRule`.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport function parseRules(content: string): RulesParseResult | undefined {\n const { yaml, body } = splitFrontmatter(content);\n if (yaml === undefined) {\n return { frontmatter: { alwaysApply: true }, body: content };\n }\n try {\n const parsed = parseSimpleYaml(yaml);\n const validated = RulesFrontmatterSchema.safeParse(parsed);\n if (!validated.success) return undefined;\n return { frontmatter: validated.data, body };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decide whether a parsed rule applies to this turn, given the files in scope.\n *\n * Checked in strict order, first match wins:\n *\n * 1. `enabled: false` — never applies, whatever else the frontmatter says. This overrides\n * `alwaysApply: true`, which is the point of having it.\n * 2. `alwaysApply: true` — applies, and `inScopePaths` is not consulted.\n * 3. otherwise — applies only if some pattern from `paths` or `globs` matches some path in\n * `inScopePaths`. The two lists are a union, not alternatives; `globs` exists for\n * Cursor compatibility and behaves identically.\n *\n * A scoped rule with an EMPTY `inScopePaths` never applies. That is the case worth planning for:\n * `contextPaths` is empty on a plain `agent.send()`, so a rule scoped by path stays dormant until\n * the caller declares which files the turn is about. It is quiet — a dormant rule looks exactly\n * like a rule that was never written.\n *\n * A rule with a fence but no patterns and no `alwaysApply` also never applies, since step 3 has\n * nothing to match.\n *\n * `description` is not consulted. It is a note for humans, not an activation condition.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport function shouldActivateRule(\n fm: RulesFrontmatter,\n inScopePaths: ReadonlyArray<string>,\n): boolean {\n if (fm.enabled === false) return false;\n if (fm.alwaysApply === true) return true;\n const patterns = [...(fm.paths ?? []), ...(fm.globs ?? [])];\n if (patterns.length === 0) return false;\n return anyGlobMatches(patterns, inScopePaths);\n}\n","/**\n * MDC (Markdown Cursor) parser for `.cursor/rules/*.mdc` (T3.1, ADR D154).\n *\n * Parses YAML frontmatter with `description` / `globs` / `alwaysApply`\n * fields. Honors activation per Cursor's docs:\n * - `alwaysApply: true` → activates unconditionally.\n * - `alwaysApply: false` + matching glob in `touchedFiles` → activates.\n * - `alwaysApply: false` + no glob match → skipped.\n *\n * **EC-I (v1 semantic):** at `agent.send()` time, `touchedFiles` is empty\n * UNLESS the caller passes `contextPaths` (the in-scope file set). When\n * `contextPaths` is provided, per-glob activation fires; otherwise only\n * `alwaysApply: true` rules activate. Description-based \"agent requested\"\n * classification is out of scope.\n *\n * Glob matching + the YAML subset are shared with the `.theokit/rules/*.md`\n * parser via `context-glob.ts` / `context-yaml-lite.ts` (DRY, no new dep).\n *\n * @internal\n */\n\nimport { z } from \"zod\";\n\nimport { anyGlobMatches } from \"./context-glob.js\";\nimport { parseSimpleYaml, splitFrontmatter } from \"./context-yaml-lite.js\";\n\n/** YAML frontmatter schema for MDC files. @internal */\nexport const McdFrontmatterSchema = z.object({\n description: z.string().optional(),\n globs: z.array(z.string()).optional(),\n alwaysApply: z.boolean().optional(),\n});\n\nexport type McdFrontmatter = z.infer<typeof McdFrontmatterSchema>;\n\nexport interface McdParseResult {\n readonly frontmatter: McdFrontmatter;\n readonly body: string;\n}\n\n/**\n * Parse `.mdc` content. Returns frontmatter + body. EC-18: when the\n * file has no `---` frontmatter, returns `frontmatter: {alwaysApply:\n * true}` so the body is treated as an unconditional rule.\n *\n * On YAML parse error, returns `undefined` and the caller emits a\n * telemetry counter (EC-21).\n *\n * @internal\n */\nexport function parseMdc(content: string): McdParseResult | undefined {\n const { yaml, body } = splitFrontmatter(content);\n if (yaml === undefined) {\n // EC-18: no frontmatter — treat as alwaysApply\n return { frontmatter: { alwaysApply: true }, body: content };\n }\n try {\n const parsed = parseSimpleYaml(yaml);\n const validated = McdFrontmatterSchema.safeParse(parsed);\n if (!validated.success) return undefined;\n return { frontmatter: validated.data, body };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decide whether an MDC rule activates given the currently-touched\n * files. EC-I: empty `touchedFiles` → only `alwaysApply: true`\n * activates.\n *\n * @internal\n */\nexport function shouldActivate(fm: McdFrontmatter, touchedFiles: ReadonlyArray<string>): boolean {\n if (fm.alwaysApply === true) return true;\n if (fm.globs === undefined || fm.globs.length === 0) return false;\n return anyGlobMatches(fm.globs, touchedFiles);\n}\n","/**\n * Multi-format context discovery runner (T5.1, ADRs D150-D156).\n *\n * Walks `DEFAULT_DISCOVERY_SPECS` (or caller override), loads each\n * spec via the appropriate parser, applies `@import` resolution where\n * declared, and returns a flat list of `AggregatorSource[]` ready for\n * the aggregate cap.\n *\n * **EC-E privacy fix:** source disambiguation uses\n * `relative(gitRoot ?? cwd, ...)` — NEVER absolute paths in\n * `<source name=\"\">`.\n *\n * @internal\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, relative } from \"node:path\";\nimport { warnOnce } from \"../hooks/hooks-source.js\";\nimport type { AggregatorSource } from \"./context-aggregator.js\";\nimport {\n admittedSpecs,\n DEFAULT_DISCOVERY_SPECS,\n type DiscoverySpec,\n findGitRoot,\n walkUpForFile,\n walkUpForGlob,\n withheldSpecs,\n} from \"./context-discovery.js\";\nimport { resolveImports } from \"./context-import-resolver.js\";\nimport { loadPlainMarkdown } from \"./context-loaders.js\";\nimport { parseMdc, shouldActivate } from \"./context-mdc-parser.js\";\nimport { parseRules, shouldActivateRule } from \"./context-rules-frontmatter.js\";\n\n/**\n * Input to the context-discovery run: where to walk, how much of each file to keep, and which\n * trust boundary an `@import` may not cross.\n *\n * `cwd` and `maxBytesPerFile` are required because neither has a safe default — an unset root walks\n * the wrong tree, and an unset cap lets one large file consume the context window.\n *\n * `importRoot` is the field to reach for when the caller's trust boundary is narrower than the\n * repository. Left unset, the repository IS the boundary (`gitRoot ?? cwd`), which is the honest\n * default for a document found by walking the repository — but it does mean an `@import` can pull\n * in any file the repo contains.\n *\n * @public — re-exported from '@theokit/sdk/context', and therefore under semver.\n */\nexport interface DiscoveryRunnerOptions {\n /** Workspace root passed to all discovery scopes. */\n readonly cwd: string;\n /** Per-file truncation cap (D155). */\n readonly maxBytesPerFile: number;\n /** Optional override of the default registry. */\n readonly specs?: ReadonlyArray<DiscoverySpec>;\n /**\n * The foreign dialects the consumer granted the `context` surface to — the output of\n * `adaptersForSurface(compatSources, \"context\")`, mapped to `.kind`.\n *\n * Left unset, every spec runs, which is what every caller before usetheokit/theokit-sdk#652 got.\n * Set, it filters specs carrying a {@link DiscoverySpec.dialect} the list does not name — so an\n * empty array is a real value meaning \"no foreign dialect granted\", NOT the same as absent.\n *\n * That distinction is the whole option. `resolveCompatSources` returns `[]` for a consumer who\n * declared nothing, and collapsing `[]` into `undefined` here would restore the defect: a\n * repository's `.claude/rules/*.md` entering the system prompt of a consumer who never asked for\n * that dialect.\n */\n readonly declaredCompatKinds?: ReadonlyArray<string>;\n /** Cursor MDC: file paths the LLM has touched this turn (EC-I: empty at send-time). */\n readonly touchedFiles?: ReadonlyArray<string>;\n /** When true, skip `theokit-context` spec — caller already handles the legacy path. */\n readonly skipLegacyTheokitContext?: boolean;\n /**\n * The root an `@import` may not escape. Defaults to `gitRoot ?? cwd` — the same value\n * this runner already uses to keep absolute paths out of `<source name=\"\">`.\n *\n * Present so an embedder with a trust boundary narrower than the repository can declare\n * it. Absent, the repository IS the boundary, which is the honest default for a document\n * discovered by walking the repository.\n */\n readonly importRoot?: string;\n}\n\n/**\n * Find, read and parse every context file the specs describe, and return them ready for the\n * aggregator.\n *\n * Specs are processed in the order given — `opts.specs` when supplied, otherwise\n * `DEFAULT_DISCOVERY_SPECS` — and within a spec, in the order its scope resolves paths. The\n * returned array carries `priority` on each source; it is NOT sorted here, so the aggregator is\n * what applies the ordering.\n *\n * A path already emitted is skipped, across specs as well as within one. Paths arrive resolved\n * through `realpath`, so two specs pointing at the same physical file through a symlink produce\n * one source rather than two.\n *\n * Missing and unreadable files are not errors: a file that does not exist is simply not matched,\n * and one that fails to read or parse is dropped and the run continues. A frontmatter file whose\n * activation conditions do not hold — `enabled: false`, or a scope that no touched file matches —\n * is dropped the same way. So a shorter result than expected means \"nothing qualified\", and this\n * function will not tell you which of those it was.\n *\n * Reads the filesystem and nothing else. No network, no writes, and no mutation of `opts`.\n *\n * Privacy: `<source name=\"\">` is built from the path RELATIVE to the git root (or `cwd` when\n * there is no git root), never an absolute path, so a home directory never reaches the prompt.\n * `@import` resolution is bounded by `opts.importRoot`, defaulting to that same root — a document\n * cannot pull in a file from outside the repository.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: per-spec dispatch ladder + cross-spec dedup is a flat orchestrator; splitting would obscure the priority-merge invariant.\nexport async function runDiscovery(opts: DiscoveryRunnerOptions): Promise<AggregatorSource[]> {\n const gitRoot = findGitRoot(opts.cwd);\n const registry = opts.specs ?? DEFAULT_DISCOVERY_SPECS;\n const specs = admittedSpecs(registry, opts.declaredCompatKinds);\n\n // The loud half of B-081. Gating the four repo-root instruction files means a consumer who\n // declares nothing goes from four files in the prompt to none, and a silent drop there is the\n // failure this whole line of work removes: the operator cannot tell \"not granted\" from \"not\n // present\" by any experiment they can run.\n //\n // Each file is NAMED with the grant that restores it, never counted. A count cannot be acted on —\n // you cannot decide whether to grant a dialect without knowing which file it brings.\n const withheld = withheldSpecs(registry, opts.declaredCompatKinds);\n if (withheld.length > 0) {\n warnOnce(\n `context-withheld:${withheld.map((w) => w.id).join(\",\")}`,\n `[theokit-sdk] not loaded, no grant declared: ${withheld\n .map((w) => `${w.pattern} (declare '${w.grant}' in compatSources)`)\n .join(\"; \")}`,\n );\n }\n const out: AggregatorSource[] = [];\n const seenReal = new Set<string>();\n\n for (const spec of specs) {\n if (opts.skipLegacyTheokitContext && spec.id === \"theokit-context\") continue;\n const paths = await resolvePathsForSpec(spec, opts.cwd, gitRoot);\n for (const path of paths) {\n if (seenReal.has(path)) continue;\n seenReal.add(path);\n const source = await loadOneSource(spec, path, opts, gitRoot);\n if (source !== undefined) out.push(source);\n }\n }\n return out;\n}\n\nasync function resolvePathsForSpec(\n spec: DiscoverySpec,\n cwd: string,\n gitRoot: string | undefined,\n): Promise<string[]> {\n if (spec.scope === \"cwd-only\") {\n return walkUpForFile(cwd, spec.pattern, cwd);\n }\n if (spec.scope === \"git-root-walk\") {\n return walkUpForFile(cwd, spec.pattern, gitRoot ?? cwd);\n }\n // globbed\n return walkUpForGlob(cwd, spec.pattern);\n}\n\nasync function loadOneSource(\n spec: DiscoverySpec,\n path: string,\n opts: DiscoveryRunnerOptions,\n gitRoot: string | undefined,\n): Promise<AggregatorSource | undefined> {\n // EC-E privacy: name uses relative-to-git-root for disambiguation;\n // NEVER absolute paths in the public `<source name=\"\">` attribute.\n // For cwd-only specs (THEO.md), no disambiguation is needed because\n // the scope can only ever match a single file.\n const relPath = relative(gitRoot ?? opts.cwd, dirname(path));\n const needsSuffix = spec.scope !== \"cwd-only\" && relPath !== \"\" && relPath !== \".\";\n const id = needsSuffix ? `${spec.id}@${relPath}` : spec.id;\n\n if (spec.parser === \"mdc\") {\n return loadMdcSource(spec, path, id, opts);\n }\n if (spec.parser === \"rules-frontmatter\") {\n return loadRulesSource(spec, path, id, opts);\n }\n if (spec.parser === \"frontmatter-zod\") {\n // Legacy `.theokit/context/*.md` — handled by `loadContextConfig` in\n // `context-manager.ts` for backward compat. We skip here unless caller\n // explicitly wants us to load it (currently always skipped).\n return undefined;\n }\n // plain-markdown\n return loadPlainMarkdownSource(spec, path, id, opts, gitRoot);\n}\n\n/**\n * Shared read → parse → activation-gate → source pipeline for the frontmatter\n * discovery parsers (mdc, rules). Binds the parser + the activation predicate so\n * the mdc and rules loaders differ only in those two, not in the surrounding\n * read/guard/shape boilerplate (DRY).\n */\nasync function loadParsedSource<F>(\n spec: DiscoverySpec,\n path: string,\n id: string,\n parse: (raw: string) => { frontmatter: F; body: string } | undefined,\n isActive: (frontmatter: F) => boolean,\n): Promise<AggregatorSource | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n return undefined;\n }\n const parsed = parse(raw);\n if (parsed === undefined || !isActive(parsed.frontmatter)) return undefined;\n return { id, source: path, content: parsed.body, priority: spec.priority, truncated: false };\n}\n\nfunction loadMdcSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n): Promise<AggregatorSource | undefined> {\n return loadParsedSource(spec, path, id, parseMdc, (fm) =>\n shouldActivate(fm, opts.touchedFiles ?? []),\n );\n}\n\nfunction loadRulesSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n): Promise<AggregatorSource | undefined> {\n return loadParsedSource(spec, path, id, parseRules, (fm) =>\n shouldActivateRule(fm, opts.touchedFiles ?? []),\n );\n}\n\nasync function loadPlainMarkdownSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n gitRoot: string | undefined,\n): Promise<AggregatorSource | undefined> {\n const loaded = await loadPlainMarkdown(path, { maxBytesPerFile: opts.maxBytesPerFile });\n if (loaded === undefined) return undefined;\n let content = loaded.content;\n if (spec.followImports) {\n content = await resolveImports(content, path, {\n visited: new Set([path]),\n depth: 0,\n maxBytesPerFile: opts.maxBytesPerFile,\n // The document carrying the import is repository-controlled, so the repository is the\n // boundary it may not cross. Without this, `CLAUDE.md` / `GEMINI.md` — the two specs\n // with `followImports: true` — could name any absolute or `~/` path and have it\n // inlined into the system prompt.\n projectRoot: opts.importRoot ?? gitRoot ?? opts.cwd,\n });\n }\n return {\n id,\n source: path,\n content,\n priority: spec.priority,\n truncated: loaded.truncated,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/context/context-loaders.ts","../src/internal/runtime/context/context-import-resolver.ts","../src/internal/runtime/context/context-glob.ts","../src/internal/runtime/context/context-rules-frontmatter.ts","../src/internal/runtime/context/context-mdc-parser.ts","../src/internal/runtime/context/context-discovery-runner.ts"],"names":["resolvePath","z","dirname","readFile"],"mappings":"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","file":"chunk-KCM3RMKR.js","sourcesContent":["/**\n * Context file loaders + truncation (T1.2, ADRs D154 / D155 / D159).\n *\n * Reads context source files from disk, applies the per-file size cap\n * with 70%/20% head/tail truncation, and emits the\n * `context_files_truncated` telemetry counter when the cap fires.\n * Pure (no I/O) for `truncateWithMarker` — testable in isolation.\n *\n * @internal\n */\n\nimport { readFile } from \"node:fs/promises\";\n\nconst HEAD_RATIO = 0.7;\nconst TAIL_RATIO = 0.2;\nconst MARKER = \"\\n\\n…[truncated by theokit]\\n\\n\";\n\n/** Default per-file cap (40k chars ≈ 10k tokens). D155. */\nexport const DEFAULT_MAX_BYTES_PER_FILE = 40_000;\n\nexport interface LoadedSource {\n /** Absolute path the content came from. */\n readonly source: string;\n readonly content: string;\n readonly originalBytes: number;\n readonly truncated: boolean;\n}\n\nexport interface TruncateResult {\n readonly truncated: boolean;\n readonly finalContent: string;\n}\n\n/**\n * Cap `content` at `max` characters with 70%/20% head/tail truncation\n * and an explicit marker. Pure — no I/O, no telemetry.\n *\n * EC-C: when `max <= MARKER.length`, skip the marker entirely and\n * return a head-only slice. Without this guard, `budget = max -\n * MARKER.length` would go negative and `content.slice(-tailBytes)`\n * would slice from the END of the string rather than truncating.\n *\n * EC-H: codepoint integrity — `String.slice()` operates on UTF-16\n * code units, which can split surrogate pairs. We accept that\n * trailing/leading replacement chars (U+FFFD) may appear at boundaries.\n * Modern LLMs tolerate them; byte-exact safety would require Buffer\n * slicing + TextDecoder with `fatal: true` retry, which adds complexity\n * without value.\n *\n * Deliberately untagged for the public surface. A tag was here and no entry exported it, so the\n * docblock promised a symbol\n * nobody could import. Exporting it was the other option and the wrong one: this is a pure\n * truncation helper whose docblock describes implementation edge cases (EC-C, EC-H), not a\n * contract — and an export is a semver commitment somebody then has to keep.\n */\nexport function truncateWithMarker(content: string, max: number): TruncateResult {\n if (content.length <= max) {\n return { truncated: false, finalContent: content };\n }\n // EC-C: max too small to fit even the marker — return head-only slice.\n if (max <= MARKER.length) {\n return { truncated: true, finalContent: content.slice(0, max) };\n }\n const budget = max - MARKER.length;\n const headBytes = Math.floor(budget * (HEAD_RATIO / (HEAD_RATIO + TAIL_RATIO)));\n const tailBytes = budget - headBytes;\n return {\n truncated: true,\n finalContent: content.slice(0, headBytes) + MARKER + content.slice(-tailBytes),\n };\n}\n\n/**\n * Read a file from disk and apply the per-file cap. Emits telemetry\n * counter when truncation fires.\n *\n * EC-G: file deleted between discovery and read (FS race) → returns\n * `undefined`, never throws.\n *\n * @internal\n */\nexport async function loadPlainMarkdown(\n absPath: string,\n opts: { maxBytesPerFile?: number } = {},\n): Promise<LoadedSource | undefined> {\n const max = opts.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE;\n let content: string;\n try {\n content = await readFile(absPath, \"utf8\");\n } catch {\n // EC-G: ENOENT / EACCES / etc → caller treats as missing.\n return undefined;\n }\n const { truncated, finalContent } = truncateWithMarker(content, max);\n if (truncated) {\n emitTruncationCounter(absPath);\n }\n return {\n source: absPath,\n content: finalContent,\n originalBytes: content.length,\n truncated,\n };\n}\n\n/**\n * EC-L: telemetry no-op when OTel is not imported. We import the tracer\n * lazily and use `safeCall` so the lookup never throws and never pulls\n * OTel into the import graph for users without `@opentelemetry/api`\n * installed.\n *\n * @internal\n */\nfunction emitTruncationCounter(source: string): void {\n // Lazy resolution via globalThis avoids static import of telemetry/tracer.\n // Tests can spy via the same path; production users without OTel see no-op.\n const tracer = (\n globalThis as { __theokit_tracer?: { inc?: (k: string, attrs: unknown) => void } }\n ).__theokit_tracer;\n if (tracer?.inc === undefined) return;\n try {\n tracer.inc(\"context_files_truncated\", { file: source });\n } catch {\n // Telemetry must never break the loader.\n }\n}\n","/**\n * `@path/to/file` import resolver (T2.1, ADR D156).\n *\n * Anthropic/Gemini convention: lines that are EXACTLY `@path` get\n * replaced with the imported file's content, recursively (5-hop cap),\n * with cycle detection. Inline `see @x.md, also @y.md` references are\n * NOT resolved (EC-Q — own-line only, mirrors Anthropic's actual\n * behavior).\n *\n * EC-D fix: every imported file is itself capped at `maxBytesPerFile`\n * via `loadPlainMarkdown` BEFORE concatenation. Prevents a CLAUDE.md\n * with 5 imports of 30k each from ballooning to 150k of imported\n * content before the outer cap fires.\n *\n * @internal\n */\n\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { insideRoot } from \"../../security/path-containment.js\";\nimport { loadPlainMarkdown } from \"./context-loaders.js\";\n\n/** EC-Q: line-anchored. `@path` must be alone on its line. */\nconst IMPORT_RE = /^@(\\S+)\\s*$/gm;\nconst MAX_HOPS = 5;\n\nexport interface ResolveImportsOptions {\n /** Absolute paths already resolved (cycle detection). */\n readonly visited: Set<string>;\n /** Current recursion depth — caps at MAX_HOPS. */\n readonly depth: number;\n /** Per-import file cap (EC-D). Forwarded to loadPlainMarkdown. */\n readonly maxBytesPerFile: number;\n /**\n * The directory an import may not escape. When set, a target resolving outside it is\n * refused and replaced with a placeholder.\n *\n * The file carrying the import is REPOSITORY-CONTROLLED — `CLAUDE.md` and `GEMINI.md`\n * are the two default specs with `followImports: true`, and both are found by\n * `git-root-walk` inside the tree the agent was pointed at. Without a root, a cloned\n * repository could name `@~/.ssh/id_rsa` or any absolute path and have its contents\n * inlined into the system prompt, and from there sent to the model provider. The\n * traversal guard that already existed (`isSafePattern`) guards the discovery PATTERN,\n * not the import TARGET, so it never saw this.\n *\n * OPTIONAL, so a caller outside the discovery path keeps the previous behaviour rather\n * than breaking on an upgrade. `runDiscovery` always supplies `gitRoot ?? cwd` — the\n * same value it already uses to keep absolute paths out of `<source name=\"\">`.\n */\n readonly projectRoot?: string;\n}\n\n/**\n * Resolve `@path` directives in `content`. Each match is replaced with\n * the imported file content (already cap-truncated per EC-D), and\n * recursion continues on the resolved content until MAX_HOPS or cycle.\n *\n * Failure modes (placeholders, never throws):\n * - file not found → `[@import not found: <path>]`\n * - cycle detected → `[@import cycle detected: <path>]`\n * - depth exceeded → trailing `\\n\\n…[@import depth limit 5 reached]\\n\\n`\n *\n * @internal\n */\nexport async function resolveImports(\n content: string,\n basePath: string,\n opts: ResolveImportsOptions,\n): Promise<string> {\n if (opts.depth >= MAX_HOPS) {\n return `${content}\\n\\n…[@import depth limit ${MAX_HOPS} reached]\\n\\n`;\n }\n const baseDir = dirname(basePath);\n return replaceAsync(content, IMPORT_RE, async (raw) => {\n const absolute = resolveImportPath(raw, baseDir);\n // Containment BEFORE the read. Reporting the refusal after loading the file would\n // already have put the bytes in memory, and the placeholder names the path the author\n // wrote rather than the resolved one — echoing `/home/<user>/.ssh/id_rsa` back into the\n // prompt would leak the layout of the machine to the same untrusted document.\n if (opts.projectRoot !== undefined && !insideRoot(absolute, opts.projectRoot)) {\n return `[@import outside the project root, refused: ${raw}]`;\n }\n if (opts.visited.has(absolute)) {\n return `[@import cycle detected: ${raw}]`;\n }\n opts.visited.add(absolute);\n const loaded = await loadPlainMarkdown(absolute, { maxBytesPerFile: opts.maxBytesPerFile });\n if (loaded === undefined) {\n return `[@import not found: ${raw}]`;\n }\n // Recurse with same visited set + incremented depth. `projectRoot` is FORWARDED: an\n // imported file is repository-controlled too, so a root that applied only at depth 0\n // would be escapable in one extra hop.\n return resolveImports(loaded.content, absolute, {\n visited: opts.visited,\n depth: opts.depth + 1,\n maxBytesPerFile: opts.maxBytesPerFile,\n ...(opts.projectRoot === undefined ? {} : { projectRoot: opts.projectRoot }),\n });\n });\n}\n\nfunction resolveImportPath(raw: string, baseDir: string): string {\n if (raw.startsWith(\"~/\")) {\n return resolvePath(join(homedir(), raw.slice(2)));\n }\n if (isAbsolute(raw)) {\n return resolvePath(raw);\n }\n return resolvePath(join(baseDir, raw));\n}\n\n/**\n * Async equivalent of `String.prototype.replace` — sequential. Returns\n * `content` with every `re` match replaced by `await replacer(match)`.\n *\n * @internal\n */\nasync function replaceAsync(\n content: string,\n re: RegExp,\n replacer: (raw: string) => Promise<string>,\n): Promise<string> {\n const matches: Array<{ match: string; raw: string; index: number }> = [];\n // Reset lastIndex; clone the regex to avoid stateful traps.\n const localRe = new RegExp(re.source, re.flags);\n let m: RegExpExecArray | null;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic regex loop\n while ((m = localRe.exec(content)) !== null) {\n matches.push({ match: m[0], raw: m[1] ?? \"\", index: m.index });\n if (m.index === localRe.lastIndex) localRe.lastIndex += 1;\n }\n if (matches.length === 0) return content;\n let result = \"\";\n let cursor = 0;\n for (const entry of matches) {\n result += content.slice(cursor, entry.index);\n result += await replacer(entry.raw);\n cursor = entry.index + entry.match.length;\n }\n result += content.slice(cursor);\n return result;\n}\n","/**\n * Shared glob → RegExp compiler for context discovery (extracted from the\n * MDC parser so the `.cursor/rules/*.mdc` and `.theokit/rules/*.md` parsers\n * share ONE implementation — DRY, no new dependency).\n *\n * Supports `**` (any depth), `*` (single path segment, no `/`), `?` (single\n * non-separator char). Sufficient for the patterns Cursor and Claude Code\n * themselves recommend.\n *\n * @internal\n */\nexport function globToRegex(glob: string): RegExp {\n // Support **/ (zero-or-more path segments), ** (any depth), * (single\n // segment), ? (single non-separator char). `**/` collapses so `src/**/*.ts`\n // matches `src/foo.ts` AND `src/a/b/foo.ts` — the semantics Cursor and Claude\n // Code document (\"src/**/* → all files under src/\"). `*` and `?` never cross a\n // `/` (strict glob segment semantics). Escape other regex metas.\n const compiled = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*\\*\\//g, \"::GLOBSTAR_SLASH::\")\n .replace(/\\*\\*/g, \"::GLOBSTAR::\")\n .replace(/\\*/g, \"[^/]*\")\n .replace(/\\?/g, \"[^/]\")\n .replace(/::GLOBSTAR_SLASH::/g, \"(?:.*/)?\")\n .replace(/::GLOBSTAR::/g, \".*\");\n return new RegExp(`^${compiled}$`);\n}\n\n/**\n * True when any of `patterns` glob-matches any of `paths`. Central helper so\n * both rule parsers agree on activation semantics.\n *\n * @internal\n */\nexport function anyGlobMatches(\n patterns: ReadonlyArray<string>,\n paths: ReadonlyArray<string>,\n): boolean {\n if (patterns.length === 0 || paths.length === 0) return false;\n const res = patterns.map(globToRegex);\n return paths.some((p) => res.some((re) => re.test(p)));\n}\n","/**\n * Parser for `.theokit/rules/*.md` — theokit-native path-scoped rules,\n * mirroring Claude Code's `.claude/rules/`.\n *\n * Frontmatter fields:\n * - `paths` — glob-pattern array (Claude Code parity: `.claude/rules` uses `paths:`).\n * - `globs` — glob-pattern array (Cursor-compatible alias; unioned with `paths`).\n * - `alwaysApply` — activate unconditionally (no scope needed).\n * - `enabled` — set `false` to disable the rule entirely (wins over everything).\n * - `description` — human note; not used for activation in v1.\n *\n * Activation (`shouldActivateRule`):\n * - `enabled: false` → never activates.\n * - `alwaysApply: true` → always activates.\n * - otherwise → activates iff a `paths`/`globs` pattern matches an in-scope\n * file (the caller's `agent.send(..., { contextPaths })`). With no in-scope\n * files, a scoped rule stays dormant (parity with Cursor `.mdc`).\n *\n * A file with no `---` frontmatter is treated as an unconditional rule\n * (`alwaysApply: true`). Malformed YAML → `undefined` (caller drops + counts).\n *\n * Glob + YAML subset are shared with the MDC parser (DRY, no new dependency).\n *\n * NOTE — no internal-visibility tag in this block. `tsconfig.base.json` sets `stripInternal: true`,\n * and TypeScript scans EVERY leading comment range of the declaration that follows, including the\n * import right below this one. The tag that used to sit here deleted that import from the emitted\n * `.d.ts`, leaving the types it binds unresolvable for any consumer running type-aware lint\n * (usetheodev/theokit-sdk#283 records the same trap on a declaration).\n */\n\nimport { z } from \"zod\";\n\nimport { anyGlobMatches } from \"./context-glob.js\";\nimport { parseSimpleYaml, splitFrontmatter } from \"./context-yaml-lite.js\";\n\n/**\n * YAML frontmatter schema for `.theokit/rules/*.md` files.\n *\n * Every field is optional, and unknown keys are dropped rather than rejected — `safeParse` failing\n * therefore means a declared key had the wrong TYPE (`paths` as a bare string, `enabled` as the\n * string `\"false\"`), never that an unexpected key was present.\n *\n * Exported because {@link RulesFrontmatter} is inferred from it and appears in the signature of\n * {@link shouldActivateRule}, which `@theokit/sdk/context` publishes: the constant has to reach the\n * emitted declarations for that inferred type to resolve in a consumer's project.\n */\nexport const RulesFrontmatterSchema = z.object({\n description: z.string().optional(),\n paths: z.array(z.string()).optional(),\n globs: z.array(z.string()).optional(),\n alwaysApply: z.boolean().optional(),\n enabled: z.boolean().optional(),\n});\n\nexport type RulesFrontmatter = z.infer<typeof RulesFrontmatterSchema>;\n\nexport interface RulesParseResult {\n readonly frontmatter: RulesFrontmatter;\n readonly body: string;\n}\n\n/**\n * Split a `.theokit/rules/*.md` document into its frontmatter and its body.\n *\n * A file with no `---` fence is not an error: the whole text becomes the body and the frontmatter\n * is synthesised as `{ alwaysApply: true }`, so a plain markdown rule with no metadata applies\n * unconditionally. That default is the one thing to know before adding a fence — adding one and\n * omitting both `alwaysApply` and a path pattern turns an always-on rule into a dormant one.\n *\n * `undefined` means the fence was there and its contents did not survive: YAML the lite parser\n * could not read, or a shape the schema rejected (`paths` as a bare string rather than a list,\n * `enabled` as `\"false\"` rather than `false`). It never throws, and it does not say WHICH of the\n * two happened — the caller drops the file and counts it.\n *\n * Unknown frontmatter keys are dropped rather than rejected, so a misspelled `alwaysAply` parses\n * fine and simply has no effect.\n *\n * Nothing here decides whether the rule applies; that is `shouldActivateRule`.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport function parseRules(content: string): RulesParseResult | undefined {\n const { yaml, body } = splitFrontmatter(content);\n if (yaml === undefined) {\n return { frontmatter: { alwaysApply: true }, body: content };\n }\n try {\n const parsed = parseSimpleYaml(yaml);\n const validated = RulesFrontmatterSchema.safeParse(parsed);\n if (!validated.success) return undefined;\n return { frontmatter: validated.data, body };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decide whether a parsed rule applies to this turn, given the files in scope.\n *\n * Checked in strict order, first match wins:\n *\n * 1. `enabled: false` — never applies, whatever else the frontmatter says. This overrides\n * `alwaysApply: true`, which is the point of having it.\n * 2. `alwaysApply: true` — applies, and `inScopePaths` is not consulted.\n * 3. otherwise — applies only if some pattern from `paths` or `globs` matches some path in\n * `inScopePaths`. The two lists are a union, not alternatives; `globs` exists for\n * Cursor compatibility and behaves identically.\n *\n * A scoped rule with an EMPTY `inScopePaths` never applies. That is the case worth planning for:\n * `contextPaths` is empty on a plain `agent.send()`, so a rule scoped by path stays dormant until\n * the caller declares which files the turn is about. It is quiet — a dormant rule looks exactly\n * like a rule that was never written.\n *\n * A rule with a fence but no patterns and no `alwaysApply` also never applies, since step 3 has\n * nothing to match.\n *\n * `description` is not consulted. It is a note for humans, not an activation condition.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport function shouldActivateRule(\n fm: RulesFrontmatter,\n inScopePaths: ReadonlyArray<string>,\n): boolean {\n if (fm.enabled === false) return false;\n if (fm.alwaysApply === true) return true;\n const patterns = [...(fm.paths ?? []), ...(fm.globs ?? [])];\n if (patterns.length === 0) return false;\n return anyGlobMatches(patterns, inScopePaths);\n}\n","/**\n * MDC (Markdown Cursor) parser for `.cursor/rules/*.mdc` (T3.1, ADR D154).\n *\n * Parses YAML frontmatter with `description` / `globs` / `alwaysApply`\n * fields. Honors activation per Cursor's docs:\n * - `alwaysApply: true` → activates unconditionally.\n * - `alwaysApply: false` + matching glob in `touchedFiles` → activates.\n * - `alwaysApply: false` + no glob match → skipped.\n *\n * **EC-I (v1 semantic):** at `agent.send()` time, `touchedFiles` is empty\n * UNLESS the caller passes `contextPaths` (the in-scope file set). When\n * `contextPaths` is provided, per-glob activation fires; otherwise only\n * `alwaysApply: true` rules activate. Description-based \"agent requested\"\n * classification is out of scope.\n *\n * Glob matching + the YAML subset are shared with the `.theokit/rules/*.md`\n * parser via `context-glob.ts` / `context-yaml-lite.ts` (DRY, no new dep).\n *\n * @internal\n */\n\nimport { z } from \"zod\";\n\nimport { anyGlobMatches } from \"./context-glob.js\";\nimport { parseSimpleYaml, splitFrontmatter } from \"./context-yaml-lite.js\";\n\n/** YAML frontmatter schema for MDC files. @internal */\nexport const McdFrontmatterSchema = z.object({\n description: z.string().optional(),\n globs: z.array(z.string()).optional(),\n alwaysApply: z.boolean().optional(),\n});\n\nexport type McdFrontmatter = z.infer<typeof McdFrontmatterSchema>;\n\nexport interface McdParseResult {\n readonly frontmatter: McdFrontmatter;\n readonly body: string;\n}\n\n/**\n * Parse `.mdc` content. Returns frontmatter + body. EC-18: when the\n * file has no `---` frontmatter, returns `frontmatter: {alwaysApply:\n * true}` so the body is treated as an unconditional rule.\n *\n * On YAML parse error, returns `undefined` and the caller emits a\n * telemetry counter (EC-21).\n *\n * @internal\n */\nexport function parseMdc(content: string): McdParseResult | undefined {\n const { yaml, body } = splitFrontmatter(content);\n if (yaml === undefined) {\n // EC-18: no frontmatter — treat as alwaysApply\n return { frontmatter: { alwaysApply: true }, body: content };\n }\n try {\n const parsed = parseSimpleYaml(yaml);\n const validated = McdFrontmatterSchema.safeParse(parsed);\n if (!validated.success) return undefined;\n return { frontmatter: validated.data, body };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decide whether an MDC rule activates given the currently-touched\n * files. EC-I: empty `touchedFiles` → only `alwaysApply: true`\n * activates.\n *\n * @internal\n */\nexport function shouldActivate(fm: McdFrontmatter, touchedFiles: ReadonlyArray<string>): boolean {\n if (fm.alwaysApply === true) return true;\n if (fm.globs === undefined || fm.globs.length === 0) return false;\n return anyGlobMatches(fm.globs, touchedFiles);\n}\n","/**\n * Multi-format context discovery runner (T5.1, ADRs D150-D156).\n *\n * Walks `DEFAULT_DISCOVERY_SPECS` (or caller override), loads each\n * spec via the appropriate parser, applies `@import` resolution where\n * declared, and returns a flat list of `AggregatorSource[]` ready for\n * the aggregate cap.\n *\n * **EC-E privacy fix:** source disambiguation uses\n * `relative(gitRoot ?? cwd, ...)` — NEVER absolute paths in\n * `<source name=\"\">`.\n *\n * @internal\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, relative } from \"node:path\";\nimport { warnOnce } from \"../hooks/hooks-source.js\";\nimport type { AggregatorSource } from \"./context-aggregator.js\";\nimport {\n admittedSpecs,\n DEFAULT_DISCOVERY_SPECS,\n type DiscoverySpec,\n findGitRoot,\n walkUpForFile,\n walkUpForGlob,\n withheldSpecs,\n} from \"./context-discovery.js\";\nimport { resolveImports } from \"./context-import-resolver.js\";\nimport { loadPlainMarkdown } from \"./context-loaders.js\";\nimport { parseMdc, shouldActivate } from \"./context-mdc-parser.js\";\nimport { parseRules, shouldActivateRule } from \"./context-rules-frontmatter.js\";\n\n/**\n * Input to the context-discovery run: where to walk, how much of each file to keep, and which\n * trust boundary an `@import` may not cross.\n *\n * `cwd` and `maxBytesPerFile` are required because neither has a safe default — an unset root walks\n * the wrong tree, and an unset cap lets one large file consume the context window.\n *\n * `importRoot` is the field to reach for when the caller's trust boundary is narrower than the\n * repository. Left unset, the repository IS the boundary (`gitRoot ?? cwd`), which is the honest\n * default for a document found by walking the repository — but it does mean an `@import` can pull\n * in any file the repo contains.\n *\n * @public — re-exported from '@theokit/sdk/context', and therefore under semver.\n */\nexport interface DiscoveryRunnerOptions {\n /** Workspace root passed to all discovery scopes. */\n readonly cwd: string;\n /** Per-file truncation cap (D155). */\n readonly maxBytesPerFile: number;\n /** Optional override of the default registry. */\n readonly specs?: ReadonlyArray<DiscoverySpec>;\n /**\n * The foreign dialects the consumer granted the `context` surface to — the output of\n * `adaptersForSurface(compatSources, \"context\")`, mapped to `.kind`.\n *\n * Left unset, every spec runs, which is what every caller before usetheokit/theokit-sdk#652 got.\n * Set, it filters specs carrying a {@link DiscoverySpec.dialect} the list does not name — so an\n * empty array is a real value meaning \"no foreign dialect granted\", NOT the same as absent.\n *\n * That distinction is the whole option. `resolveCompatSources` returns `[]` for a consumer who\n * declared nothing, and collapsing `[]` into `undefined` here would restore the defect: a\n * repository's `.claude/rules/*.md` entering the system prompt of a consumer who never asked for\n * that dialect.\n */\n readonly declaredCompatKinds?: ReadonlyArray<string>;\n /** Cursor MDC: file paths the LLM has touched this turn (EC-I: empty at send-time). */\n readonly touchedFiles?: ReadonlyArray<string>;\n /** When true, skip `theokit-context` spec — caller already handles the legacy path. */\n readonly skipLegacyTheokitContext?: boolean;\n /**\n * The root an `@import` may not escape. Defaults to `gitRoot ?? cwd` — the same value\n * this runner already uses to keep absolute paths out of `<source name=\"\">`.\n *\n * Present so an embedder with a trust boundary narrower than the repository can declare\n * it. Absent, the repository IS the boundary, which is the honest default for a document\n * discovered by walking the repository.\n */\n readonly importRoot?: string;\n}\n\n/**\n * Find, read and parse every context file the specs describe, and return them ready for the\n * aggregator.\n *\n * Specs are processed in the order given — `opts.specs` when supplied, otherwise\n * `DEFAULT_DISCOVERY_SPECS` — and within a spec, in the order its scope resolves paths. The\n * returned array carries `priority` on each source; it is NOT sorted here, so the aggregator is\n * what applies the ordering.\n *\n * A path already emitted is skipped, across specs as well as within one. Paths arrive resolved\n * through `realpath`, so two specs pointing at the same physical file through a symlink produce\n * one source rather than two.\n *\n * Missing and unreadable files are not errors: a file that does not exist is simply not matched,\n * and one that fails to read or parse is dropped and the run continues. A frontmatter file whose\n * activation conditions do not hold — `enabled: false`, or a scope that no touched file matches —\n * is dropped the same way. So a shorter result than expected means \"nothing qualified\", and this\n * function will not tell you which of those it was.\n *\n * Reads the filesystem and nothing else. No network, no writes, and no mutation of `opts`.\n *\n * Privacy: `<source name=\"\">` is built from the path RELATIVE to the git root (or `cwd` when\n * there is no git root), never an absolute path, so a home directory never reaches the prompt.\n * `@import` resolution is bounded by `opts.importRoot`, defaulting to that same root — a document\n * cannot pull in a file from outside the repository.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: per-spec dispatch ladder + cross-spec dedup is a flat orchestrator; splitting would obscure the priority-merge invariant.\nexport async function runDiscovery(opts: DiscoveryRunnerOptions): Promise<AggregatorSource[]> {\n const gitRoot = findGitRoot(opts.cwd);\n const registry = opts.specs ?? DEFAULT_DISCOVERY_SPECS;\n const specs = admittedSpecs(registry, opts.declaredCompatKinds);\n\n // The loud half of B-081. Gating the four repo-root instruction files means a consumer who\n // declares nothing goes from four files in the prompt to none, and a silent drop there is the\n // failure this whole line of work removes: the operator cannot tell \"not granted\" from \"not\n // present\" by any experiment they can run.\n //\n // Each file is NAMED with the grant that restores it, never counted. A count cannot be acted on —\n // you cannot decide whether to grant a dialect without knowing which file it brings.\n const withheld = withheldSpecs(registry, opts.declaredCompatKinds);\n if (withheld.length > 0) {\n warnOnce(\n `context-withheld:${withheld.map((w) => w.id).join(\",\")}`,\n `[theokit-sdk] not loaded, no grant declared: ${withheld\n .map((w) => `${w.pattern} (declare '${w.grant}' in compatSources)`)\n .join(\"; \")}`,\n );\n }\n const out: AggregatorSource[] = [];\n const seenReal = new Set<string>();\n\n for (const spec of specs) {\n if (opts.skipLegacyTheokitContext && spec.id === \"theokit-context\") continue;\n const paths = await resolvePathsForSpec(spec, opts.cwd, gitRoot);\n for (const path of paths) {\n if (seenReal.has(path)) continue;\n seenReal.add(path);\n const source = await loadOneSource(spec, path, opts, gitRoot);\n if (source !== undefined) out.push(source);\n }\n }\n return out;\n}\n\nasync function resolvePathsForSpec(\n spec: DiscoverySpec,\n cwd: string,\n gitRoot: string | undefined,\n): Promise<string[]> {\n if (spec.scope === \"cwd-only\") {\n return walkUpForFile(cwd, spec.pattern, cwd);\n }\n if (spec.scope === \"git-root-walk\") {\n return walkUpForFile(cwd, spec.pattern, gitRoot ?? cwd);\n }\n // globbed\n return walkUpForGlob(cwd, spec.pattern);\n}\n\nasync function loadOneSource(\n spec: DiscoverySpec,\n path: string,\n opts: DiscoveryRunnerOptions,\n gitRoot: string | undefined,\n): Promise<AggregatorSource | undefined> {\n // EC-E privacy: name uses relative-to-git-root for disambiguation;\n // NEVER absolute paths in the public `<source name=\"\">` attribute.\n // For cwd-only specs (THEO.md), no disambiguation is needed because\n // the scope can only ever match a single file.\n const relPath = relative(gitRoot ?? opts.cwd, dirname(path));\n const needsSuffix = spec.scope !== \"cwd-only\" && relPath !== \"\" && relPath !== \".\";\n const id = needsSuffix ? `${spec.id}@${relPath}` : spec.id;\n\n if (spec.parser === \"mdc\") {\n return loadMdcSource(spec, path, id, opts);\n }\n if (spec.parser === \"rules-frontmatter\") {\n return loadRulesSource(spec, path, id, opts);\n }\n if (spec.parser === \"frontmatter-zod\") {\n // Legacy `.theokit/context/*.md` — handled by `loadContextConfig` in\n // `context-manager.ts` for backward compat. We skip here unless caller\n // explicitly wants us to load it (currently always skipped).\n return undefined;\n }\n // plain-markdown\n return loadPlainMarkdownSource(spec, path, id, opts, gitRoot);\n}\n\n/**\n * Shared read → parse → activation-gate → source pipeline for the frontmatter\n * discovery parsers (mdc, rules). Binds the parser + the activation predicate so\n * the mdc and rules loaders differ only in those two, not in the surrounding\n * read/guard/shape boilerplate (DRY).\n */\nasync function loadParsedSource<F>(\n spec: DiscoverySpec,\n path: string,\n id: string,\n parse: (raw: string) => { frontmatter: F; body: string } | undefined,\n isActive: (frontmatter: F) => boolean,\n): Promise<AggregatorSource | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n return undefined;\n }\n const parsed = parse(raw);\n if (parsed === undefined || !isActive(parsed.frontmatter)) return undefined;\n return { id, source: path, content: parsed.body, priority: spec.priority, truncated: false };\n}\n\nfunction loadMdcSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n): Promise<AggregatorSource | undefined> {\n return loadParsedSource(spec, path, id, parseMdc, (fm) =>\n shouldActivate(fm, opts.touchedFiles ?? []),\n );\n}\n\nfunction loadRulesSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n): Promise<AggregatorSource | undefined> {\n return loadParsedSource(spec, path, id, parseRules, (fm) =>\n shouldActivateRule(fm, opts.touchedFiles ?? []),\n );\n}\n\nasync function loadPlainMarkdownSource(\n spec: DiscoverySpec,\n path: string,\n id: string,\n opts: DiscoveryRunnerOptions,\n gitRoot: string | undefined,\n): Promise<AggregatorSource | undefined> {\n const loaded = await loadPlainMarkdown(path, { maxBytesPerFile: opts.maxBytesPerFile });\n if (loaded === undefined) return undefined;\n let content = loaded.content;\n if (spec.followImports) {\n content = await resolveImports(content, path, {\n visited: new Set([path]),\n depth: 0,\n maxBytesPerFile: opts.maxBytesPerFile,\n // The document carrying the import is repository-controlled, so the repository is the\n // boundary it may not cross. Without this, `CLAUDE.md` / `GEMINI.md` — the two specs\n // with `followImports: true` — could name any absolute or `~/` path and have it\n // inlined into the system prompt.\n projectRoot: opts.importRoot ?? gitRoot ?? opts.cwd,\n });\n }\n return {\n id,\n source: path,\n content,\n priority: spec.priority,\n truncated: loaded.truncated,\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TheokitAgentError } from './chunk-
|
|
1
|
+
import { TheokitAgentError } from './chunk-TNFOU6SB.js';
|
|
2
2
|
import { readFileSync, mkdirSync, openSync, writeSync, closeSync, fstatSync, readSync } from 'fs';
|
|
3
3
|
import { dirname } from 'path';
|
|
4
4
|
|
|
@@ -99,5 +99,5 @@ function readJsonlIds(path, keyFn) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export { JsonlParseError, appendJsonl, loadJsonl, readJsonlIds };
|
|
102
|
-
//# sourceMappingURL=chunk-
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
102
|
+
//# sourceMappingURL=chunk-KU5O7WPK.js.map
|
|
103
|
+
//# sourceMappingURL=chunk-KU5O7WPK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/persistence/jsonl.ts"],"names":[],"mappings":"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","file":"chunk-7KIN3L2S.js","sourcesContent":["/**\n * Durable JSONL primitives shared by the eval harness (M6).\n *\n * - `loadJsonl` — generic dataset reader (split/trim/skip-blank/parse) with a\n * line-numbered {@link JsonlParseError}. The dataset SCHEMA is the caller's\n * concern via `map` (M6 ADR D3) — this module owns only the parse.\n * - `appendJsonl` / `readJsonlIds` — crash-durable, resumable batch persistence\n * (M6 ADR D1): each record is appended as one whole `\\n`-terminated line the\n * instant it is produced, and a re-run resumes by skipping already-keyed rows.\n *\n * reference: knowledge-base/references/theocode-eval/lib/swebench-dataset.ts:82\n * (parseJsonl + line-N error) and swebench-batch.ts:113,205 (resume + per-line\n * flush).\n *\n * @internal\n */\nimport {\n closeSync,\n fstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readSync,\n writeSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/** Raised when a JSONL line is not valid JSON or is not a JSON object. Carries the 1-based line number. */\nexport class JsonlParseError extends TheokitAgentError {\n override readonly name = \"JsonlParseError\";\n constructor(\n message: string,\n readonly line: number,\n ) {\n // Not retryable: the bytes on the line do not change between reads.\n super(message, { code: \"jsonl_parse_failed\", isRetryable: false });\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Parse a single trimmed line to a plain object, or `undefined` if blank / invalid / non-object. */\nfunction tryParseObjectLine(line: string): Record<string, unknown> | undefined {\n if (line.length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return undefined;\n }\n return isPlainObject(parsed) ? parsed : undefined;\n}\n\n/**\n * Parse a JSONL file into rows. Blank lines are skipped. A malformed or\n * non-object line throws {@link JsonlParseError} naming the 1-based line. When\n * `map` is provided, each raw object is mapped to the typed row (the SWE-bench\n * schema lives in the caller's `map`, per M6 ADR D3).\n */\nexport function loadJsonl<T = Record<string, unknown>>(\n path: string,\n opts: {\n map?: (raw: Record<string, unknown>, lineNumber: number) => T;\n /**\n * M81 — tolerate a truncated LAST line (a crash artifact: the process died mid-write).\n *\n * Opt-in on purpose. As a default it would also swallow corruption in the MIDDLE of the file,\n * turning loud data loss into silent data loss — the wrong trade for a session store.\n */\n tolerateTrailingPartialLine?: boolean;\n } = {},\n): T[] {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n const out: T[] = [];\n for (let i = 0; i < lines.length; i += 1) {\n const lineNumber = i + 1;\n const line = (lines[i] ?? \"\").trim();\n if (line.length === 0) continue;\n // M81 — `undefined` = a tolerated invalid line (the last one, truncated by a crash). Any other\n // invalid line has already thrown inside `parseLine`.\n const parsed = parseLine(\n line,\n lineNumber,\n opts.tolerateTrailingPartialLine === true && lineNumber === lines.length,\n );\n if (parsed === undefined) break;\n out.push(opts.map ? opts.map(parsed, lineNumber) : (parsed as unknown as T));\n }\n return out;\n}\n\n/**\n * Parses ONE line, returning `undefined` when it is a tolerated truncated last line.\n *\n * Extracted because `loadJsonl` went over the cognitive-complexity ceiling once it gained the\n * tolerance — and because parsing a line and iterating a file are two responsibilities that were\n * only together out of habit.\n */\nfunction parseLine(\n line: string,\n lineNumber: number,\n tolerate: boolean,\n): Record<string, unknown> | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n if (tolerate) return undefined;\n throw new JsonlParseError(`line ${lineNumber}: invalid JSON`, lineNumber);\n }\n if (!isPlainObject(parsed)) {\n throw new JsonlParseError(`line ${lineNumber}: not a JSON object`, lineNumber);\n }\n return parsed;\n}\n\n/**\n * Append one record as a whole `\\n`-terminated JSON line. Creates the parent dir\n * if missing. `appendFileSync` is synchronous, so within a single Node process\n * the event loop serializes writes and each call writes its line atomically —\n * interleave-safe for the bounded-concurrency batch runner.\n *\n * reference: swebench-batch.ts:192 (mkdir-before-append), :205 (per-line flush).\n */\nexport function appendJsonl(path: string, record: unknown): void {\n // T2.4 — `0o700` for the same reason the file below is `0o600`, and it is the half M93 missed. A\n // private file inside a group-writable directory can be replaced wholesale, and the replacement's\n // mode is whatever the writer chose. Under `umask 002` this directory was born `0775`.\n //\n // The directory sits under `~/.theokit`, shared with the credential and trust stores, and the\n // framework already wrote the diagnosis while fixing a sibling (`@theokit/agents\n // config/trust-store.ts:157-161`): the mode argument is a no-op on a directory that already\n // exists, \"and this one is shared with the SDK's transcript root — whoever creates it first sets\n // the permissions\". Whoever creates it first is usually THIS function, because a session writes a\n // transcript before it ever touches a credential. So `assertSecureModes` was not wrong to demand a\n // private directory; this path was wrong to produce one that fails it, depending on run order.\n //\n // Creation-time only, deliberately: repairing a pre-existing directory means `stat` + `chmod` on\n // every append, and this is the hot path of every session. The pre-existing case is what\n // `assertSecureModes` is for.\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n const prefix = needsLineBreakBefore(path) ? \"\\n\" : \"\";\n // M93 (adversarial review, H1) — `0o600`. `appendFileSync` takes no `mode`, so the permission came\n // from the umask: under `umask 022` the transcript was born `0664`, world-readable. The previous\n // path (`replaceFileAtomic`) pinned `0o600` on purpose — \"holds the FULL in-flight content\n // (credential snapshots, OAuth tokens)\" (`atomic-write.ts:107`) — and switching to append lost\n // that silently. The same class had already been caught in the consumer (`atomic-sync.ts`, M88\n // HIGH-1).\n //\n // `mode` only applies on CREATION; a pre-existing file keeps whatever permission it already has.\n const fd = openSync(path, \"a\", 0o600);\n try {\n writeSync(fd, `${prefix}${JSON.stringify(record)}\\n`);\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Does the file end without a `\\n`? Then the last line is truncated — a crash mid-append.\n *\n * Without this check the next append **glues** itself onto the middle of the broken line, producing\n * an invalid line the reader discards: the NEW record disappears along with the partial one. The\n * previous path (read-modify-write) self-healed from this because it rewrote the whole file.\n * Measured in M93's adversarial review (H2): after an append over a truncated file, the\n * just-written record was no longer readable.\n *\n * Reads **one byte**, not the file.\n */\nfunction needsLineBreakBefore(path: string): boolean {\n let fd: number;\n try {\n fd = openSync(path, \"r\");\n } catch {\n return false; // the file does not exist yet: nothing to splice onto\n }\n try {\n const size = fstatSync(fd).size;\n if (size === 0) return false;\n const buf = Buffer.alloc(1);\n readSync(fd, buf, 0, 1, size - 1);\n return buf[0] !== 0x0a;\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Read the set of keys from an existing JSONL file for which `keyFn(parsed)`\n * returns a non-empty string. Used to resume a crashed batch by skipping rows\n * already persisted with a successful result. A trailing partial line from an\n * interrupted append is tolerated (skipped, not thrown), and a missing file\n * yields an empty set.\n *\n * reference: swebench-batch.ts:113 (readDoneIds), :129 (success-only),\n * :131 (tolerate partial line).\n */\nexport function readJsonlIds(\n path: string,\n keyFn: (parsed: Record<string, unknown>) => string | undefined,\n): Set<string> {\n const done = new Set<string>();\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch {\n return done; // no file yet → nothing done\n }\n for (const rawLine of text.split(\"\\n\")) {\n // A trailing partial line from an interrupted run parses to undefined → skipped.\n const parsed = tryParseObjectLine(rawLine.trim());\n if (parsed === undefined) continue;\n const key = keyFn(parsed);\n if (typeof key === \"string\" && key.length > 0) done.add(key);\n }\n return done;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/jsonl.ts"],"names":[],"mappings":"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","file":"chunk-KU5O7WPK.js","sourcesContent":["/**\n * Durable JSONL primitives shared by the eval harness (M6).\n *\n * - `loadJsonl` — generic dataset reader (split/trim/skip-blank/parse) with a\n * line-numbered {@link JsonlParseError}. The dataset SCHEMA is the caller's\n * concern via `map` (M6 ADR D3) — this module owns only the parse.\n * - `appendJsonl` / `readJsonlIds` — crash-durable, resumable batch persistence\n * (M6 ADR D1): each record is appended as one whole `\\n`-terminated line the\n * instant it is produced, and a re-run resumes by skipping already-keyed rows.\n *\n * reference: knowledge-base/references/theocode-eval/lib/swebench-dataset.ts:82\n * (parseJsonl + line-N error) and swebench-batch.ts:113,205 (resume + per-line\n * flush).\n *\n * @internal\n */\nimport {\n closeSync,\n fstatSync,\n mkdirSync,\n openSync,\n readFileSync,\n readSync,\n writeSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/** Raised when a JSONL line is not valid JSON or is not a JSON object. Carries the 1-based line number. */\nexport class JsonlParseError extends TheokitAgentError {\n override readonly name = \"JsonlParseError\";\n constructor(\n message: string,\n readonly line: number,\n ) {\n // Not retryable: the bytes on the line do not change between reads.\n super(message, { code: \"jsonl_parse_failed\", isRetryable: false });\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Parse a single trimmed line to a plain object, or `undefined` if blank / invalid / non-object. */\nfunction tryParseObjectLine(line: string): Record<string, unknown> | undefined {\n if (line.length === 0) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return undefined;\n }\n return isPlainObject(parsed) ? parsed : undefined;\n}\n\n/**\n * Parse a JSONL file into rows. Blank lines are skipped. A malformed or\n * non-object line throws {@link JsonlParseError} naming the 1-based line. When\n * `map` is provided, each raw object is mapped to the typed row (the SWE-bench\n * schema lives in the caller's `map`, per M6 ADR D3).\n */\nexport function loadJsonl<T = Record<string, unknown>>(\n path: string,\n opts: {\n map?: (raw: Record<string, unknown>, lineNumber: number) => T;\n /**\n * M81 — tolerate a truncated LAST line (a crash artifact: the process died mid-write).\n *\n * Opt-in on purpose. As a default it would also swallow corruption in the MIDDLE of the file,\n * turning loud data loss into silent data loss — the wrong trade for a session store.\n */\n tolerateTrailingPartialLine?: boolean;\n } = {},\n): T[] {\n const lines = readFileSync(path, \"utf8\").split(\"\\n\");\n const out: T[] = [];\n for (let i = 0; i < lines.length; i += 1) {\n const lineNumber = i + 1;\n const line = (lines[i] ?? \"\").trim();\n if (line.length === 0) continue;\n // M81 — `undefined` = a tolerated invalid line (the last one, truncated by a crash). Any other\n // invalid line has already thrown inside `parseLine`.\n const parsed = parseLine(\n line,\n lineNumber,\n opts.tolerateTrailingPartialLine === true && lineNumber === lines.length,\n );\n if (parsed === undefined) break;\n out.push(opts.map ? opts.map(parsed, lineNumber) : (parsed as unknown as T));\n }\n return out;\n}\n\n/**\n * Parses ONE line, returning `undefined` when it is a tolerated truncated last line.\n *\n * Extracted because `loadJsonl` went over the cognitive-complexity ceiling once it gained the\n * tolerance — and because parsing a line and iterating a file are two responsibilities that were\n * only together out of habit.\n */\nfunction parseLine(\n line: string,\n lineNumber: number,\n tolerate: boolean,\n): Record<string, unknown> | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n if (tolerate) return undefined;\n throw new JsonlParseError(`line ${lineNumber}: invalid JSON`, lineNumber);\n }\n if (!isPlainObject(parsed)) {\n throw new JsonlParseError(`line ${lineNumber}: not a JSON object`, lineNumber);\n }\n return parsed;\n}\n\n/**\n * Append one record as a whole `\\n`-terminated JSON line. Creates the parent dir\n * if missing. `appendFileSync` is synchronous, so within a single Node process\n * the event loop serializes writes and each call writes its line atomically —\n * interleave-safe for the bounded-concurrency batch runner.\n *\n * reference: swebench-batch.ts:192 (mkdir-before-append), :205 (per-line flush).\n */\nexport function appendJsonl(path: string, record: unknown): void {\n // T2.4 — `0o700` for the same reason the file below is `0o600`, and it is the half M93 missed. A\n // private file inside a group-writable directory can be replaced wholesale, and the replacement's\n // mode is whatever the writer chose. Under `umask 002` this directory was born `0775`.\n //\n // The directory sits under `~/.theokit`, shared with the credential and trust stores, and the\n // framework already wrote the diagnosis while fixing a sibling (`@theokit/agents\n // config/trust-store.ts:157-161`): the mode argument is a no-op on a directory that already\n // exists, \"and this one is shared with the SDK's transcript root — whoever creates it first sets\n // the permissions\". Whoever creates it first is usually THIS function, because a session writes a\n // transcript before it ever touches a credential. So `assertSecureModes` was not wrong to demand a\n // private directory; this path was wrong to produce one that fails it, depending on run order.\n //\n // Creation-time only, deliberately: repairing a pre-existing directory means `stat` + `chmod` on\n // every append, and this is the hot path of every session. The pre-existing case is what\n // `assertSecureModes` is for.\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 });\n const prefix = needsLineBreakBefore(path) ? \"\\n\" : \"\";\n // M93 (adversarial review, H1) — `0o600`. `appendFileSync` takes no `mode`, so the permission came\n // from the umask: under `umask 022` the transcript was born `0664`, world-readable. The previous\n // path (`replaceFileAtomic`) pinned `0o600` on purpose — \"holds the FULL in-flight content\n // (credential snapshots, OAuth tokens)\" (`atomic-write.ts:107`) — and switching to append lost\n // that silently. The same class had already been caught in the consumer (`atomic-sync.ts`, M88\n // HIGH-1).\n //\n // `mode` only applies on CREATION; a pre-existing file keeps whatever permission it already has.\n const fd = openSync(path, \"a\", 0o600);\n try {\n writeSync(fd, `${prefix}${JSON.stringify(record)}\\n`);\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Does the file end without a `\\n`? Then the last line is truncated — a crash mid-append.\n *\n * Without this check the next append **glues** itself onto the middle of the broken line, producing\n * an invalid line the reader discards: the NEW record disappears along with the partial one. The\n * previous path (read-modify-write) self-healed from this because it rewrote the whole file.\n * Measured in M93's adversarial review (H2): after an append over a truncated file, the\n * just-written record was no longer readable.\n *\n * Reads **one byte**, not the file.\n */\nfunction needsLineBreakBefore(path: string): boolean {\n let fd: number;\n try {\n fd = openSync(path, \"r\");\n } catch {\n return false; // the file does not exist yet: nothing to splice onto\n }\n try {\n const size = fstatSync(fd).size;\n if (size === 0) return false;\n const buf = Buffer.alloc(1);\n readSync(fd, buf, 0, 1, size - 1);\n return buf[0] !== 0x0a;\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Read the set of keys from an existing JSONL file for which `keyFn(parsed)`\n * returns a non-empty string. Used to resume a crashed batch by skipping rows\n * already persisted with a successful result. A trailing partial line from an\n * interrupted append is tolerated (skipped, not thrown), and a missing file\n * yields an empty set.\n *\n * reference: swebench-batch.ts:113 (readDoneIds), :129 (success-only),\n * :131 (tolerate partial line).\n */\nexport function readJsonlIds(\n path: string,\n keyFn: (parsed: Record<string, unknown>) => string | undefined,\n): Set<string> {\n const done = new Set<string>();\n let text: string;\n try {\n text = readFileSync(path, \"utf8\");\n } catch {\n return done; // no file yet → nothing done\n }\n for (const rawLine of text.split(\"\\n\")) {\n // A trailing partial line from an interrupted run parses to undefined → skipped.\n const parsed = tryParseObjectLine(rawLine.trim());\n if (parsed === undefined) continue;\n const key = keyFn(parsed);\n if (typeof key === \"string\" && key.length > 0) done.add(key);\n }\n return done;\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
|
|
4
|
+
var chunkRNXXYF22_cjs = require('./chunk-RNXXYF22.cjs');
|
|
5
5
|
|
|
6
6
|
// src/internal/runtime/compression/compression-helpers.ts
|
|
7
7
|
function selectCompressionWindow(messages, preserveLast = 6) {
|
|
@@ -49,7 +49,7 @@ var WILDCARD_REGISTRY = [
|
|
|
49
49
|
["bedrock/anthropic.claude-3-5-sonnet*", "bedrock/anthropic.claude-3-5-haiku*"]
|
|
50
50
|
];
|
|
51
51
|
var NO_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["ollama", "lmstudio", "llamacpp"]);
|
|
52
|
-
var CompressionModelUnresolvedError = class extends
|
|
52
|
+
var CompressionModelUnresolvedError = class extends chunkCEGU5AVD_cjs.TheokitAgentError {
|
|
53
53
|
name = "CompressionModelUnresolvedError";
|
|
54
54
|
agentModel;
|
|
55
55
|
constructor(agentModel) {
|
|
@@ -110,7 +110,7 @@ Anything that MUST NOT be forgotten (preserve verbatim: error messages, exact va
|
|
|
110
110
|
File paths touched or referenced (verbatim).`;
|
|
111
111
|
function assertMarker(marker) {
|
|
112
112
|
if (marker === "") {
|
|
113
|
-
throw new
|
|
113
|
+
throw new chunkCEGU5AVD_cjs.TheokitAgentError("compaction marker must be non-empty", {
|
|
114
114
|
code: "invalid_argument"
|
|
115
115
|
});
|
|
116
116
|
}
|
|
@@ -143,8 +143,8 @@ async function runSummarize(summarize, head, template, failSafe) {
|
|
|
143
143
|
return await summarize(head, template);
|
|
144
144
|
} catch (err) {
|
|
145
145
|
if (!failSafe) throw err;
|
|
146
|
-
|
|
147
|
-
`[compaction] summarizer failed \u2014 proceeding uncompacted: ${
|
|
146
|
+
chunkRNXXYF22_cjs.diag(
|
|
147
|
+
`[compaction] summarizer failed \u2014 proceeding uncompacted: ${chunkRNXXYF22_cjs.redactSecrets(err instanceof Error ? err.message : String(err))}
|
|
148
148
|
`
|
|
149
149
|
);
|
|
150
150
|
return FAILSAFE_ABORT;
|
|
@@ -187,9 +187,9 @@ function filterFromLatestCheckpoint(messages, options = {}) {
|
|
|
187
187
|
return [...messages];
|
|
188
188
|
}
|
|
189
189
|
function isContextOverflowError(err) {
|
|
190
|
-
return err instanceof
|
|
190
|
+
return err instanceof chunkCEGU5AVD_cjs.TheokitAgentError && (err.code === "context_too_long" || err.metadata?.code === "context_too_long");
|
|
191
191
|
}
|
|
192
|
-
var ContextWindowMarginError = class extends
|
|
192
|
+
var ContextWindowMarginError = class extends chunkCEGU5AVD_cjs.TheokitAgentError {
|
|
193
193
|
constructor(margin) {
|
|
194
194
|
super(
|
|
195
195
|
`context-window margin must be in (0, 1], got ${String(margin)}. A margin above 1 grows the assumed window and delays compaction past the real limit.`,
|
|
@@ -241,5 +241,5 @@ exports.isContextOverflowError = isContextOverflowError;
|
|
|
241
241
|
exports.resolveCompressionModel = resolveCompressionModel;
|
|
242
242
|
exports.resolveEffectiveContextWindow = resolveEffectiveContextWindow;
|
|
243
243
|
exports.shouldCompact = shouldCompact;
|
|
244
|
-
//# sourceMappingURL=chunk-
|
|
245
|
-
//# sourceMappingURL=chunk-
|
|
244
|
+
//# sourceMappingURL=chunk-KX5QXLBS.cjs.map
|
|
245
|
+
//# sourceMappingURL=chunk-KX5QXLBS.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/compression/compression-helpers.ts","../src/internal/runtime/compression/compression-model-registry.ts","../src/compaction.ts"],"names":["TheokitAgentError","diag","redactSecrets"],"mappings":"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","file":"chunk-J5623RIV.cjs","sourcesContent":["/**\n * Compression helpers (T2.3, ADR D92).\n *\n * Scaffold for future compression LLM integration:\n * - `selectCompressionWindow` — splits messages into compress/preserve halves\n * - `assertCompressionReduced` — 10% reduction floor to detect \"compression placebo\"\n *\n * The compression LLM call itself is out of scope for this plan (requires\n * an auxiliary-model ADR). These helpers are used by `Agent.send` when a\n * future iteration adds compression.\n *\n * @internal\n */\n\nexport interface CompressionWindow<M> {\n toCompress: M[];\n toPreserve: M[];\n}\n\n/**\n * Split `messages` into the half to compress (older) and the half to\n * preserve verbatim (recent). When `messages.length <= preserveLast`,\n * everything is preserved.\n *\n * @internal\n */\nexport function selectCompressionWindow<M>(\n messages: readonly M[],\n preserveLast = 6,\n): CompressionWindow<M> {\n if (messages.length <= preserveLast) {\n return { toCompress: [], toPreserve: [...messages] };\n }\n return {\n toCompress: messages.slice(0, -preserveLast),\n toPreserve: messages.slice(-preserveLast),\n };\n}\n\nexport interface CompressionCheck {\n reduced: boolean;\n reductionPct: number;\n reason?: string;\n}\n\n/**\n * Check that compression actually reduced token count by at least `minPct`\n * (default 10%). Returns `{ reduced: false }` for spirals-in-formation\n * (compression LLM outputs that grow or barely shrink).\n *\n * @internal\n */\nexport function assertCompressionReduced(\n before: number,\n after: number,\n minPct = 10,\n): CompressionCheck {\n if (before <= 0) {\n return { reduced: false, reductionPct: 0, reason: \"before count was zero\" };\n }\n const reductionPct = ((before - after) / before) * 100;\n if (reductionPct >= minPct) {\n return { reduced: true, reductionPct };\n }\n return {\n reduced: false,\n reductionPct,\n reason: `compression reduced ${reductionPct.toFixed(1)}% (< ${minPct}% min). Spiral likely.`,\n };\n}\n","/**\n * T2.2 — Provider-agnostic compression-model registry (ADR D440).\n *\n * Resolves a cheaper-tier summarization model in the SAME vendor family\n * as the agent's main model. Provider-agnostic by design: a consumer\n * running on Anthropic-only / Ollama-only / Bedrock-only never needs\n * to provision a second vendor's key for the compression aux-LLM.\n *\n * Resolution algorithm:\n *\n * 1. Exact match in `EXACT_REGISTRY` → cheaper-tier id (most cases).\n * 2. Wildcard match in `WILDCARD_REGISTRY` (`*` suffix in key) →\n * swap matched suffix (Bedrock region-prefixed variants).\n * 3. Provider in `NO_AUTH_PROVIDERS` (Ollama / LM Studio / llama.cpp)\n * → return SAME model id (local — cost N/A; running the same\n * model for summarization costs only the round-trip latency,\n * acceptable in dev/local mode).\n * 4. No match → throw `CompressionModelUnresolvedError` with the\n * actionable message naming the model + override path + the\n * registry-PR remediation hint.\n *\n * Step 1 of T2.2 (compression-helpers wire). Step 2 wires this into\n * `compression-config.ts`. Step 3 builds the OTel-instrumented aux\n * client. Step 4 wires into `loop.ts` ContextWindowExceededError catch.\n *\n * @internal\n */\n\nimport { TheokitAgentError } from \"../../../errors.js\";\n\n/**\n * Exact `agent-model → compression-model` map. Most cases land here.\n * Entries MUST keep cheaper-tier model within the SAME vendor family\n * as the key (Unbreakable Rule 9 — provider-agnostic).\n */\nconst EXACT_REGISTRY: ReadonlyMap<string, string> = new Map([\n // OpenAI family\n [\"openai/gpt-4o\", \"openai/gpt-4o-mini\"],\n [\"openai/gpt-4-turbo\", \"openai/gpt-4o-mini\"],\n [\"openai/gpt-4\", \"openai/gpt-4o-mini\"],\n [\"openai/o1-preview\", \"openai/gpt-4o-mini\"],\n [\"openai/o1\", \"openai/gpt-4o-mini\"],\n [\"openai/o3\", \"openai/gpt-4o-mini\"],\n [\"openai/o3-mini\", \"openai/gpt-4o-mini\"],\n // Anthropic family\n [\"anthropic/claude-opus-4\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-sonnet-4\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-5-sonnet\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-5-sonnet-latest\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-opus\", \"anthropic/claude-3-haiku\"],\n [\"anthropic/claude-3-sonnet\", \"anthropic/claude-3-haiku\"],\n // Vertex (Gemini + Anthropic-on-Vertex)\n [\"vertex/gemini-1.5-pro\", \"vertex/gemini-1.5-flash\"],\n [\"vertex/gemini-2.0-pro\", \"vertex/gemini-1.5-flash\"],\n [\"vertex/claude-3-5-sonnet\", \"vertex/claude-3-5-haiku\"],\n [\"vertex/claude-3-opus\", \"vertex/claude-3-haiku\"],\n // OpenRouter (preserve openrouter prefix, swap tier within same vendor)\n [\"openrouter/openai/gpt-4o\", \"openrouter/openai/gpt-4o-mini\"],\n [\"openrouter/openai/gpt-4-turbo\", \"openrouter/openai/gpt-4o-mini\"],\n [\"openrouter/anthropic/claude-3-5-sonnet\", \"openrouter/anthropic/claude-3-5-haiku\"],\n [\"openrouter/anthropic/claude-opus-4\", \"openrouter/anthropic/claude-3-5-haiku\"],\n]);\n\n/**\n * Wildcard `agent-model-prefix* → compression-model-prefix*` map.\n * Used for region-prefixed Bedrock variants where the registry can't\n * enumerate every region literal. Matching algorithm: the registry\n * key MUST end with `*`; the input MUST match the prefix; the input's\n * suffix after the prefix is appended to the resolved value (also\n * ending with `*` to mark the swap point).\n */\nconst WILDCARD_REGISTRY: ReadonlyArray<readonly [string, string]> = [\n // Bedrock Anthropic: us.anthropic.claude-sonnet-* → us.anthropic.claude-3-haiku-*\n [\"bedrock/anthropic.claude-sonnet*\", \"bedrock/anthropic.claude-3-haiku*\"],\n [\"bedrock/anthropic.claude-opus*\", \"bedrock/anthropic.claude-3-haiku*\"],\n [\"bedrock/anthropic.claude-3-5-sonnet*\", \"bedrock/anthropic.claude-3-5-haiku*\"],\n];\n\n/**\n * Providers declaring `authType: \"none\"` in their profile (D182, D188,\n * D189). For these, return the SAME model — local runtime has no\n * \"cheaper tier\" semantics, latency penalty of summarizing twice is\n * acceptable in dev/local mode. Keys are the provider prefix BEFORE\n * the slash.\n */\nconst NO_AUTH_PROVIDERS: ReadonlySet<string> = new Set([\"ollama\", \"lmstudio\", \"llamacpp\"]);\n\n/**\n * T2.2 — Typed error thrown when `resolveCompressionModel` cannot\n * find a same-family-cheaper-tier mapping for `agentModel`. Surfaces\n * the offending model id so operators can either (a) provide\n * `Agent.create({compression: {model: ...}})` explicitly or (b) open\n * a registry-PR adding the missing entry.\n *\n * @public\n */\nexport class CompressionModelUnresolvedError extends TheokitAgentError {\n override readonly name = \"CompressionModelUnresolvedError\";\n readonly agentModel: string;\n constructor(agentModel: string) {\n // Not retryable: the registry has no entry for this model until someone adds one.\n super(\n `Could not resolve a same-family-cheaper-tier compression model for \"${agentModel}\". ` +\n `Provide Agent.create({compression: {model: \"<your-cheaper-model>\"}}) ` +\n `OR add \"${agentModel}\" to the compression-model-registry (see ADR D440).`,\n { code: \"compression_model_unresolved\", isRetryable: false },\n );\n this.agentModel = agentModel;\n }\n}\n\n/**\n * T2.2 — Resolve a cheaper-tier compression model from the agent's\n * main model id. Pure function — no I/O, no mutation, deterministic.\n *\n * @throws `CompressionModelUnresolvedError` when no rule matches.\n *\n * @internal\n */\nexport function resolveCompressionModel(agentModel: string): string {\n const exact = EXACT_REGISTRY.get(agentModel);\n if (exact !== undefined) return exact;\n const wildcard = matchWildcard(agentModel);\n if (wildcard !== null) return wildcard;\n if (isNoAuthProvider(agentModel)) return agentModel;\n throw new CompressionModelUnresolvedError(agentModel);\n}\n\n/** Wildcard suffix match — Bedrock region-prefixed variants. */\nfunction matchWildcard(agentModel: string): string | null {\n for (const [pattern, replacement] of WILDCARD_REGISTRY) {\n const prefix = pattern.endsWith(\"*\") ? pattern.slice(0, -1) : pattern;\n if (!agentModel.startsWith(prefix)) continue;\n const suffix = agentModel.slice(prefix.length);\n const replPrefix = replacement.endsWith(\"*\") ? replacement.slice(0, -1) : replacement;\n return `${replPrefix}${suffix}`;\n }\n return null;\n}\n\n/** Provider declares `authType: \"none\"` — local runtime, same model. */\nfunction isNoAuthProvider(agentModel: string): boolean {\n const slashIdx = agentModel.indexOf(\"/\");\n if (slashIdx <= 0) return false;\n return NO_AUTH_PROVIDERS.has(agentModel.slice(0, slashIdx));\n}\n","/**\n * Public compaction / context-management helpers (M2-1, extended V3-3).\n *\n * Promotes the SDK's compaction capability to a public surface so consumers can\n * compact a transcript, mark/filter conversation checkpoints, and detect\n * context-overflow — without reaching into `internal/`.\n *\n * Two recent-window modes (V3-3):\n * - `keepRecent` (turn-count, default) — keeps the last N turns verbatim and\n * always preserves leading system PROMPTS; reuses the internal\n * `selectCompressionWindow` (no second algorithm).\n * - `keepTokens` (token-budget) — keeps the trailing turns whose accumulated\n * `estimateTokens` fits the budget (theocode `splitTranscript` semantics). In\n * this mode leading system prompts are NOT special-cased (D6).\n *\n * Summarization is delegated to a caller-supplied callback (which receives the\n * older window + the summary template). With `failSafe`, a thrown summarizer\n * returns the ORIGINAL transcript + a structured warn (compaction is an\n * optimization, never a cause of data loss); without it, the error propagates.\n *\n * Public from the `@theokit/sdk/compaction` sub-path.\n */\n\nimport { TheokitAgentError } from \"./errors.js\";\nimport { diag } from \"./internal/diagnostics.js\";\nimport { selectCompressionWindow } from \"./internal/runtime/compression/compression-helpers.js\";\nimport { redactSecrets } from \"./internal/security/redact.js\";\n\n/**\n * Minimal message shape for compaction/compression input. THE canonical public origin (leaf type —\n * rollup-plugin-dts cannot re-export types from internal modules into entry bundles; M42 lesson).\n *\n * @public\n */\nexport interface CompressibleMessage {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n}\n\n/**\n * Sentinel prefix marking a conversation checkpoint turn. A visible, structured,\n * prose-unlikely token (no invisible/control bytes — safe to persist and to read\n * in source). Only {@link buildCheckpoint} should produce content beginning with it.\n */\nexport const CHECKPOINT_MARKER = \"[[theokit:checkpoint]] \";\n\n/**\n * The 7-section summary template handed to the `summarize` callback (theocode\n * parity shape). Every header is always present so the summarizer cannot silently\n * drop a category; the model is told to preserve file paths, commands, and error\n * text verbatim. Override per-call via {@link CompactTranscriptOptions.summaryTemplate}.\n */\nexport const SUMMARY_TEMPLATE = `Summarize the conversation so far into these sections (keep every header even if empty):\n\n## Goal\nWhat the user is ultimately trying to achieve.\n\n## Constraints\nHard requirements, conventions, and rules stated.\n\n## Progress\nWhat has been done so far.\n\n## Decisions\nChoices made and their rationale.\n\n## Next\nThe immediate next steps.\n\n## Critical\nAnything that MUST NOT be forgotten (preserve verbatim: error messages, exact values).\n\n## Files\nFile paths touched or referenced (verbatim).`;\n\n/** Reject an empty marker — it would match every turn via `startsWith(\"\")` (EC-3). */\nfunction assertMarker(marker: string): void {\n if (marker === \"\") {\n throw new TheokitAgentError(\"compaction marker must be non-empty\", {\n code: \"invalid_argument\",\n });\n }\n}\n\n/** True for a real system prompt — a `system` turn that is NOT a checkpoint marker. */\nfunction isSystemPrompt(message: CompressibleMessage, marker: string): boolean {\n return message.role === \"system\" && !message.content.startsWith(marker);\n}\n\n/** Carried split: leading system prompts (preserved), the older window, the verbatim tail. */\ninterface TranscriptSplit {\n systemPrompts: CompressibleMessage[];\n head: CompressibleMessage[];\n recent: CompressibleMessage[];\n}\n\n/**\n * Token-budget split (theocode `splitTranscript`): walk from the END accumulating\n * `estimateTokens` until `keepTokens` is exceeded; everything older is the head.\n * Always keeps ≥ 1 recent turn. No system-prompt special-casing (D6).\n */\nfunction selectByTokenBudget(\n messages: CompressibleMessage[],\n keepTokens: number,\n): { head: CompressibleMessage[]; recent: CompressibleMessage[] } {\n let acc = 0;\n let splitIndex = messages.length;\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n acc += estimateTokens(messages[i]?.content ?? \"\");\n if (acc > keepTokens && i < messages.length - 1) {\n splitIndex = i + 1;\n break;\n }\n splitIndex = i;\n }\n return { head: messages.slice(0, splitIndex), recent: messages.slice(splitIndex) };\n}\n\n/** Turn-count split (M2): preserve leading system prompts; window the rest by `keepRecent`. */\nfunction splitByRecent(\n messages: CompressibleMessage[],\n keepRecent: number,\n marker: string,\n): TranscriptSplit {\n const systemPrompts = messages.filter((m) => isSystemPrompt(m, marker));\n const rest = messages.filter((m) => !isSystemPrompt(m, marker));\n const { toCompress, toPreserve } = selectCompressionWindow(rest, keepRecent);\n return { systemPrompts, head: toCompress, recent: toPreserve };\n}\n\n/** Options for {@link compactTranscript}. */\nexport interface CompactTranscriptOptions {\n /** Trailing turns preserved verbatim by COUNT (default 6). Ignored when `keepTokens` is set. */\n keepRecent?: number;\n /**\n * Trailing turns preserved verbatim by TOKEN BUDGET (theocode mode). When set,\n * takes precedence over `keepRecent` and disables system-prompt preservation (D6).\n */\n keepTokens?: number;\n /** Checkpoint marker (default {@link CHECKPOINT_MARKER}). Must be non-empty. */\n marker?: string;\n /** Summary template passed to `summarize` (default {@link SUMMARY_TEMPLATE}). */\n summaryTemplate?: string;\n /** Summarize the older window into one turn; if omitted, the older window is dropped. */\n summarize?: (older: CompressibleMessage[], template: string) => Promise<CompressibleMessage>;\n /**\n * When true, a thrown `summarize` returns the ORIGINAL transcript + a structured\n * warn (compaction never loses data). Default false: the error propagates.\n */\n failSafe?: boolean;\n}\n\n/** Sentinel returned by {@link runSummarize} when fail-safe swallowed a throw. */\nconst FAILSAFE_ABORT = Symbol(\"failsafe-abort\");\n\n/** Run the summarizer; on throw, either propagate or (fail-safe) warn + signal abort. */\nasync function runSummarize(\n summarize: NonNullable<CompactTranscriptOptions[\"summarize\"]>,\n head: CompressibleMessage[],\n template: string,\n failSafe: boolean,\n): Promise<CompressibleMessage | typeof FAILSAFE_ABORT> {\n try {\n return await summarize(head, template);\n } catch (err) {\n if (!failSafe) throw err;\n // Unbreakable Rule 8 — never fail silently. The breadcrumb points at the root\n // cause when a summarizer fails every turn and context grows unchecked. The\n // summarizer is caller-supplied, so its error text is routed through\n // `redactSecrets` (ADR D68 — no unredacted output sink in src/).\n // theokit#147 — through the interceptable channel. A summarizer failing every turn is exactly\n // the breadcrumb a TUI host wants in its own panel, not smeared across its alternate screen.\n diag(\n `[compaction] summarizer failed — proceeding uncompacted: ${redactSecrets(err instanceof Error ? err.message : String(err))}\\n`,\n );\n return FAILSAFE_ABORT;\n }\n}\n\n/**\n * Compact a transcript. In `keepRecent` mode (default) the last `keepRecent` turns\n * are kept verbatim and leading system PROMPTS preserved; in `keepTokens` mode the\n * trailing turns within the token budget are kept (no system special-casing, D6).\n * The older window is summarized (via `summarize`, receiving the template) or\n * dropped. Never mutates the input.\n */\nexport async function compactTranscript(\n messages: CompressibleMessage[],\n options: CompactTranscriptOptions = {},\n): Promise<CompressibleMessage[]> {\n const marker = options.marker ?? CHECKPOINT_MARKER;\n assertMarker(marker);\n const split =\n options.keepTokens != null\n ? { systemPrompts: [], ...selectByTokenBudget(messages, options.keepTokens) }\n : splitByRecent(messages, options.keepRecent ?? 6, marker);\n if (split.head.length === 0) {\n return [...messages];\n }\n if (!options.summarize) {\n return [...split.systemPrompts, ...split.recent];\n }\n const template = options.summaryTemplate ?? SUMMARY_TEMPLATE;\n const summary = await runSummarize(\n options.summarize,\n split.head,\n template,\n options.failSafe ?? false,\n );\n if (summary === FAILSAFE_ABORT) {\n return [...messages];\n }\n return [...split.systemPrompts, summary, ...split.recent];\n}\n\n/**\n * Build a checkpoint marker turn (a `system` turn whose content starts with\n * `marker`, default {@link CHECKPOINT_MARKER}). `marker` must be non-empty.\n */\nexport function buildCheckpoint(\n label?: string,\n marker: string = CHECKPOINT_MARKER,\n): CompressibleMessage {\n assertMarker(marker);\n return { role: \"system\", content: marker + (label ?? \"\") };\n}\n\n/** Options for {@link filterFromLatestCheckpoint}. */\nexport interface FilterCheckpointOptions {\n /** Marker to scan for (default {@link CHECKPOINT_MARKER}). */\n marker?: string;\n /**\n * `'after'` (default, M2) returns turns AFTER the latest marker (exclusive);\n * `'from'` (theocode) returns turns FROM the latest marker (inclusive).\n */\n include?: \"after\" | \"from\";\n}\n\n/**\n * Return the turns relative to the most recent checkpoint marker (all turns if\n * none). `include: 'after'` (default) excludes the checkpoint; `'from'` includes\n * it (the summary stands in for the pruned head). Never mutates the input.\n */\nexport function filterFromLatestCheckpoint(\n messages: CompressibleMessage[],\n options: FilterCheckpointOptions = {},\n): CompressibleMessage[] {\n const marker = options.marker ?? CHECKPOINT_MARKER;\n const offset = options.include === \"from\" ? 0 : 1;\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.content.startsWith(marker)) {\n return messages.slice(i + offset);\n }\n }\n return [...messages];\n}\n\n/**\n * True iff `err` is a {@link TheokitAgentError} (or subclass) reporting a\n * context-window-exceeded condition (the typed `context_too_long` code). Reads\n * both `code` (set by provider mappers) and `metadata.code` (the preferred field)\n * — never a brittle message regex.\n */\nexport function isContextOverflowError(err: unknown): boolean {\n return (\n err instanceof TheokitAgentError &&\n (err.code === \"context_too_long\" || err.metadata?.code === \"context_too_long\")\n );\n}\n\n/**\n * M77 — the margin is outside `(0, 1]`. Typed, because a margin > 1 would GROW the assumed window,\n * which is the unsafe direction: `shouldCompact` is monotonically decreasing in `contextWindow`, so\n * overestimating it makes the trigger fire too late and the context overflow.\n */\nexport class ContextWindowMarginError extends TheokitAgentError {\n constructor(readonly margin: number) {\n super(\n `context-window margin must be in (0, 1], got ${String(margin)}. ` +\n `A margin above 1 grows the assumed window and delays compaction past the real limit.`,\n { code: \"invalid_context_window_margin\" },\n );\n }\n}\n\n/**\n * M77 — default safety margin on the context window.\n *\n * `0.95` is not a guess: it is the single reference's value. Codex ships\n * `effective_context_window_percent: 95` (`models-manager/src/model_info.rs:158`) and never budgets\n * against 100% of a window. Being MULTIPLICATIVE, it stays conservative at any window size, which a\n * fixed subtraction would not.\n */\nexport const CONTEXT_WINDOW_MARGIN = 0.95;\n\n/**\n * M77 — the floor used ONLY when neither the catalog nor the caller knows the window.\n *\n * Honest about its own weakness. This number has **one** source (the M77 milestone text); the search\n * for a second one failed, because the reference has no fallback at all — Codex returns `Option` and\n * its callers early-return (`core/src/session/turn_context.rs:213`, `core/src/compact_remote.rs:374`).\n *\n * It is conservative only UPWARD. For a model whose real window is larger, budgeting against 128k\n * compacts early — wasteful but safe. For a model whose real window is SMALLER (an 8k or 32k model\n * absent from the catalog), it compacts too late and the context still overflows. That residual risk\n * is real and is why {@link resolveEffectiveContextWindow} never lets this floor compete with a known\n * catalog value, and why the `compaction_fallback` event exists: a surface can show that the budget is\n * a guess instead of the user finding out when the provider rejects the request.\n */\nexport const CONTEXT_WINDOW_FLOOR = 128_000;\n\n/** Where the effective window came from — carried into the M77 structured event. */\nexport type ContextWindowSource = \"override\" | \"catalog\" | \"fallback\";\n\n/** Input to {@link resolveEffectiveContextWindow}. */\nexport interface EffectiveContextWindowInput {\n /** Caller-supplied window, in tokens. Clamped by `catalog` when both are present. */\n readonly override?: number | undefined;\n /** The model's window from the catalog, in tokens. Absent when the model is unknown. */\n readonly catalog?: number | undefined;\n /** Safety margin in `(0, 1]`. Codex uses 0.95 (`model_info.rs:158`). */\n readonly margin: number;\n /** Conservative floor used ONLY when neither `override` nor `catalog` is available. */\n readonly floor?: number | undefined;\n}\n\n/** Result of {@link resolveEffectiveContextWindow}. */\nexport interface EffectiveContextWindow {\n /** The window to budget against, after clamp and margin. */\n readonly window: number;\n /** Which input won — `\"fallback\"` means neither override nor catalog was available. */\n readonly source: ContextWindowSource;\n /** `true` when an `override` above the catalog window was clamped down to it. */\n readonly clamped: boolean;\n}\n\n/**\n * Absolute cap on a declared context window, applied when no catalog entry exists to compare against.\n *\n * **10M**, not 2M. The first version used 2M on the rationale that it sat \"comfortably above the\n * largest published window\" — adversarial review measured the opposite: Llama 4 Scout publishes\n * **10M**, and it arrives precisely via OpenRouter, the provider **without** a catalog, which is the\n * case this cap exists to cover. The user would have silently lost 80% of the declared window.\n *\n * The cap still serves what motivates it — one extra zero on 400k gives 4M, which passes; two zeros\n * give 40M, which does not. A limit that rejects legitimate configuration is worse than no limit,\n * because failing OPEN is visible when it happens and silently losing 80% is not.\n */\nexport const ABSOLUTE_CONTEXT_WINDOW_CAP = 10_000_000;\n\n/**\n * Resolve the window to budget against — the fail-SAFE replacement for reading the catalog directly.\n *\n * Today `post-run-lifecycle.ts` reads `getCatalogModelInfo(model)?.limit?.context` and, when that is\n * `undefined`, auto-compaction simply never fires. That is fail-OPEN: the context grows until the\n * provider rejects the request. (The comment there calls it \"fail-safe\" — M77 corrects it.)\n *\n * Three techniques, two of them borrowed from the single reference:\n *\n * 1. **Override, CLAMPED by the catalog** — Codex `models-manager/src/model_info.rs:26-31` lets\n * config override the window but limits it with `context_window.min(max_context_window)`.\n * Without the clamp, declaring 999k on a 200k model reopens the overflow through another door.\n * 2. **Multiplicative margin** — Codex `model_info.rs:158` (`effective_context_window_percent: 95`)\n * never budgets against 100% of the window. Being multiplicative, it is conservative for ANY\n * window size.\n * 3. **Floor, only when nothing is known** — deliberately NOT a blanket default. A fixed floor is\n * conservative only upward: applied to a small model it would inflate the assumed window and\n * reproduce the very fail-open this function exists to close. Hence the floor never competes\n * with a known catalog value.\n *\n * Pure — no catalog lookup, no I/O. The caller supplies the numbers, mirroring `shouldCompact`.\n */\nexport function resolveEffectiveContextWindow(\n input: EffectiveContextWindowInput,\n): EffectiveContextWindow {\n if (!(input.margin > 0) || input.margin > 1) {\n throw new ContextWindowMarginError(input.margin);\n }\n\n const withMargin = (raw: number): number => Math.floor(raw * input.margin);\n\n if (input.override !== undefined) {\n // The catalog is the preferred cap; without it the ABSOLUTE cap applies.\n //\n // M95 (adversarial review of M94) — the previous version clamped **only** when a catalog entry\n // existed, and the whole reason the `contextWindow` key exists is the model that has NO entry\n // (OpenRouter has zero). So the clamp was missing in exactly the case that justifies the\n // feature, while the documentation — including an already-published CHANGELOG — stated without\n // qualification that \"declaring 10M does not blow past the provider\".\n //\n // The concrete scenario is one extra zero: `context_window = 4000000`. The only guard was a\n // `.positive().int()`, which sets no upper bound. The agent would never compact until the\n // provider refused the turn — the silent fail-OPEN that M77 exists to prevent.\n const cap = input.catalog ?? ABSOLUTE_CONTEXT_WINDOW_CAP;\n const clamped = input.override > cap;\n return { window: withMargin(clamped ? cap : input.override), source: \"override\", clamped };\n }\n\n if (input.catalog !== undefined) {\n return { window: withMargin(input.catalog), source: \"catalog\", clamped: false };\n }\n\n return { window: withMargin(input.floor ?? 0), source: \"fallback\", clamped: false };\n}\n\n/** Input to {@link shouldCompact}: an estimate, the model's window, and reserved headroom. */\nexport interface ShouldCompactInput {\n /** Estimated token count of the next request (e.g. from {@link estimateTokens}). */\n readonly estimated: number;\n /** The model's total context window, in tokens. */\n readonly contextWindow: number;\n /** Tokens to reserve as headroom (output + safety margin). */\n readonly buffer: number;\n /**\n * Tokens reserved for the model's response generation, SEPARATE from `buffer`.\n * Default 0 — omitting it preserves the legacy\n * `estimated >= contextWindow - buffer` result.\n */\n readonly maxOutput?: number;\n}\n\n/**\n * Characters per token in the tokenizer-free estimate below.\n *\n * Exported so the ratio has ONE owner. It used to be a named constant in `built-in-processors.ts`\n * and an inlined `4` here, with both `estimateTokens` functions `@public` from two different package\n * entry points — so tuning the ratio, or switching to code points instead of UTF-16 units (the\n * caveat both docblocks already carried), would have silently diverged them.\n *\n * @public\n */\nexport const CHARS_PER_TOKEN = 4;\n\n/**\n * Tokenizer-free token estimate via the conventional ~4-chars-per-token\n * heuristic: `ceil(text.length / CHARS_PER_TOKEN)`. `\"\"` → 0; any non-empty text → ≥ 1.\n * A cheap PRE-CALL gate for {@link shouldCompact} — NOT exact tokenization\n * (a consumer needing exactness supplies their own tokenizer). Uses UTF-16\n * `.length` (code units), so multibyte text is approximate.\n *\n * `built-in-processors.ts` re-exports this under the same name, so both entry points keep working\n * and there is one implementation behind them.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\n/**\n * Decide BEFORE sending whether to compact: `true` when the `estimated` token\n * count leaves less than `buffer` headroom in the `contextWindow`\n * (`estimated >= contextWindow - buffer`). A `buffer >= contextWindow`\n * (non-positive threshold) always returns `true`. Pure — the caller supplies\n * the window (e.g. from `resolveModelCapabilities`), keeping this decoupled\n * from the per-model catalog.\n */\nexport function shouldCompact(input: ShouldCompactInput): boolean {\n return input.estimated >= input.contextWindow - input.buffer - (input.maxOutput ?? 0);\n}\n\nexport type { CompressionConfig } from \"./internal/runtime/compression/compression-config.js\";\nexport { CompressionModelUnresolvedError } from \"./internal/runtime/compression/compression-model-registry.js\";\n// `CompressionFailedError` is exported from `./errors`, not here: compression-summarizer.ts\n// imports `CompressibleMessage` from THIS module, so exporting from it closes a cycle madge\n// rejects (tests/architecture/no-cycles-at-all.test.ts). The errors entry is where a consumer\n// looks for a typed error anyway.\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/compression/compression-helpers.ts","../src/internal/runtime/compression/compression-model-registry.ts","../src/compaction.ts"],"names":["TheokitAgentError","diag","redactSecrets"],"mappings":"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","file":"chunk-KX5QXLBS.cjs","sourcesContent":["/**\n * Compression helpers (T2.3, ADR D92).\n *\n * Scaffold for future compression LLM integration:\n * - `selectCompressionWindow` — splits messages into compress/preserve halves\n * - `assertCompressionReduced` — 10% reduction floor to detect \"compression placebo\"\n *\n * The compression LLM call itself is out of scope for this plan (requires\n * an auxiliary-model ADR). These helpers are used by `Agent.send` when a\n * future iteration adds compression.\n *\n * @internal\n */\n\nexport interface CompressionWindow<M> {\n toCompress: M[];\n toPreserve: M[];\n}\n\n/**\n * Split `messages` into the half to compress (older) and the half to\n * preserve verbatim (recent). When `messages.length <= preserveLast`,\n * everything is preserved.\n *\n * @internal\n */\nexport function selectCompressionWindow<M>(\n messages: readonly M[],\n preserveLast = 6,\n): CompressionWindow<M> {\n if (messages.length <= preserveLast) {\n return { toCompress: [], toPreserve: [...messages] };\n }\n return {\n toCompress: messages.slice(0, -preserveLast),\n toPreserve: messages.slice(-preserveLast),\n };\n}\n\nexport interface CompressionCheck {\n reduced: boolean;\n reductionPct: number;\n reason?: string;\n}\n\n/**\n * Check that compression actually reduced token count by at least `minPct`\n * (default 10%). Returns `{ reduced: false }` for spirals-in-formation\n * (compression LLM outputs that grow or barely shrink).\n *\n * @internal\n */\nexport function assertCompressionReduced(\n before: number,\n after: number,\n minPct = 10,\n): CompressionCheck {\n if (before <= 0) {\n return { reduced: false, reductionPct: 0, reason: \"before count was zero\" };\n }\n const reductionPct = ((before - after) / before) * 100;\n if (reductionPct >= minPct) {\n return { reduced: true, reductionPct };\n }\n return {\n reduced: false,\n reductionPct,\n reason: `compression reduced ${reductionPct.toFixed(1)}% (< ${minPct}% min). Spiral likely.`,\n };\n}\n","/**\n * T2.2 — Provider-agnostic compression-model registry (ADR D440).\n *\n * Resolves a cheaper-tier summarization model in the SAME vendor family\n * as the agent's main model. Provider-agnostic by design: a consumer\n * running on Anthropic-only / Ollama-only / Bedrock-only never needs\n * to provision a second vendor's key for the compression aux-LLM.\n *\n * Resolution algorithm:\n *\n * 1. Exact match in `EXACT_REGISTRY` → cheaper-tier id (most cases).\n * 2. Wildcard match in `WILDCARD_REGISTRY` (`*` suffix in key) →\n * swap matched suffix (Bedrock region-prefixed variants).\n * 3. Provider in `NO_AUTH_PROVIDERS` (Ollama / LM Studio / llama.cpp)\n * → return SAME model id (local — cost N/A; running the same\n * model for summarization costs only the round-trip latency,\n * acceptable in dev/local mode).\n * 4. No match → throw `CompressionModelUnresolvedError` with the\n * actionable message naming the model + override path + the\n * registry-PR remediation hint.\n *\n * Step 1 of T2.2 (compression-helpers wire). Step 2 wires this into\n * `compression-config.ts`. Step 3 builds the OTel-instrumented aux\n * client. Step 4 wires into `loop.ts` ContextWindowExceededError catch.\n *\n * @internal\n */\n\nimport { TheokitAgentError } from \"../../../errors.js\";\n\n/**\n * Exact `agent-model → compression-model` map. Most cases land here.\n * Entries MUST keep cheaper-tier model within the SAME vendor family\n * as the key (Unbreakable Rule 9 — provider-agnostic).\n */\nconst EXACT_REGISTRY: ReadonlyMap<string, string> = new Map([\n // OpenAI family\n [\"openai/gpt-4o\", \"openai/gpt-4o-mini\"],\n [\"openai/gpt-4-turbo\", \"openai/gpt-4o-mini\"],\n [\"openai/gpt-4\", \"openai/gpt-4o-mini\"],\n [\"openai/o1-preview\", \"openai/gpt-4o-mini\"],\n [\"openai/o1\", \"openai/gpt-4o-mini\"],\n [\"openai/o3\", \"openai/gpt-4o-mini\"],\n [\"openai/o3-mini\", \"openai/gpt-4o-mini\"],\n // Anthropic family\n [\"anthropic/claude-opus-4\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-sonnet-4\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-5-sonnet\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-5-sonnet-latest\", \"anthropic/claude-3-5-haiku-latest\"],\n [\"anthropic/claude-3-opus\", \"anthropic/claude-3-haiku\"],\n [\"anthropic/claude-3-sonnet\", \"anthropic/claude-3-haiku\"],\n // Vertex (Gemini + Anthropic-on-Vertex)\n [\"vertex/gemini-1.5-pro\", \"vertex/gemini-1.5-flash\"],\n [\"vertex/gemini-2.0-pro\", \"vertex/gemini-1.5-flash\"],\n [\"vertex/claude-3-5-sonnet\", \"vertex/claude-3-5-haiku\"],\n [\"vertex/claude-3-opus\", \"vertex/claude-3-haiku\"],\n // OpenRouter (preserve openrouter prefix, swap tier within same vendor)\n [\"openrouter/openai/gpt-4o\", \"openrouter/openai/gpt-4o-mini\"],\n [\"openrouter/openai/gpt-4-turbo\", \"openrouter/openai/gpt-4o-mini\"],\n [\"openrouter/anthropic/claude-3-5-sonnet\", \"openrouter/anthropic/claude-3-5-haiku\"],\n [\"openrouter/anthropic/claude-opus-4\", \"openrouter/anthropic/claude-3-5-haiku\"],\n]);\n\n/**\n * Wildcard `agent-model-prefix* → compression-model-prefix*` map.\n * Used for region-prefixed Bedrock variants where the registry can't\n * enumerate every region literal. Matching algorithm: the registry\n * key MUST end with `*`; the input MUST match the prefix; the input's\n * suffix after the prefix is appended to the resolved value (also\n * ending with `*` to mark the swap point).\n */\nconst WILDCARD_REGISTRY: ReadonlyArray<readonly [string, string]> = [\n // Bedrock Anthropic: us.anthropic.claude-sonnet-* → us.anthropic.claude-3-haiku-*\n [\"bedrock/anthropic.claude-sonnet*\", \"bedrock/anthropic.claude-3-haiku*\"],\n [\"bedrock/anthropic.claude-opus*\", \"bedrock/anthropic.claude-3-haiku*\"],\n [\"bedrock/anthropic.claude-3-5-sonnet*\", \"bedrock/anthropic.claude-3-5-haiku*\"],\n];\n\n/**\n * Providers declaring `authType: \"none\"` in their profile (D182, D188,\n * D189). For these, return the SAME model — local runtime has no\n * \"cheaper tier\" semantics, latency penalty of summarizing twice is\n * acceptable in dev/local mode. Keys are the provider prefix BEFORE\n * the slash.\n */\nconst NO_AUTH_PROVIDERS: ReadonlySet<string> = new Set([\"ollama\", \"lmstudio\", \"llamacpp\"]);\n\n/**\n * T2.2 — Typed error thrown when `resolveCompressionModel` cannot\n * find a same-family-cheaper-tier mapping for `agentModel`. Surfaces\n * the offending model id so operators can either (a) provide\n * `Agent.create({compression: {model: ...}})` explicitly or (b) open\n * a registry-PR adding the missing entry.\n *\n * @public\n */\nexport class CompressionModelUnresolvedError extends TheokitAgentError {\n override readonly name = \"CompressionModelUnresolvedError\";\n readonly agentModel: string;\n constructor(agentModel: string) {\n // Not retryable: the registry has no entry for this model until someone adds one.\n super(\n `Could not resolve a same-family-cheaper-tier compression model for \"${agentModel}\". ` +\n `Provide Agent.create({compression: {model: \"<your-cheaper-model>\"}}) ` +\n `OR add \"${agentModel}\" to the compression-model-registry (see ADR D440).`,\n { code: \"compression_model_unresolved\", isRetryable: false },\n );\n this.agentModel = agentModel;\n }\n}\n\n/**\n * T2.2 — Resolve a cheaper-tier compression model from the agent's\n * main model id. Pure function — no I/O, no mutation, deterministic.\n *\n * @throws `CompressionModelUnresolvedError` when no rule matches.\n *\n * @internal\n */\nexport function resolveCompressionModel(agentModel: string): string {\n const exact = EXACT_REGISTRY.get(agentModel);\n if (exact !== undefined) return exact;\n const wildcard = matchWildcard(agentModel);\n if (wildcard !== null) return wildcard;\n if (isNoAuthProvider(agentModel)) return agentModel;\n throw new CompressionModelUnresolvedError(agentModel);\n}\n\n/** Wildcard suffix match — Bedrock region-prefixed variants. */\nfunction matchWildcard(agentModel: string): string | null {\n for (const [pattern, replacement] of WILDCARD_REGISTRY) {\n const prefix = pattern.endsWith(\"*\") ? pattern.slice(0, -1) : pattern;\n if (!agentModel.startsWith(prefix)) continue;\n const suffix = agentModel.slice(prefix.length);\n const replPrefix = replacement.endsWith(\"*\") ? replacement.slice(0, -1) : replacement;\n return `${replPrefix}${suffix}`;\n }\n return null;\n}\n\n/** Provider declares `authType: \"none\"` — local runtime, same model. */\nfunction isNoAuthProvider(agentModel: string): boolean {\n const slashIdx = agentModel.indexOf(\"/\");\n if (slashIdx <= 0) return false;\n return NO_AUTH_PROVIDERS.has(agentModel.slice(0, slashIdx));\n}\n","/**\n * Public compaction / context-management helpers (M2-1, extended V3-3).\n *\n * Promotes the SDK's compaction capability to a public surface so consumers can\n * compact a transcript, mark/filter conversation checkpoints, and detect\n * context-overflow — without reaching into `internal/`.\n *\n * Two recent-window modes (V3-3):\n * - `keepRecent` (turn-count, default) — keeps the last N turns verbatim and\n * always preserves leading system PROMPTS; reuses the internal\n * `selectCompressionWindow` (no second algorithm).\n * - `keepTokens` (token-budget) — keeps the trailing turns whose accumulated\n * `estimateTokens` fits the budget (theocode `splitTranscript` semantics). In\n * this mode leading system prompts are NOT special-cased (D6).\n *\n * Summarization is delegated to a caller-supplied callback (which receives the\n * older window + the summary template). With `failSafe`, a thrown summarizer\n * returns the ORIGINAL transcript + a structured warn (compaction is an\n * optimization, never a cause of data loss); without it, the error propagates.\n *\n * Public from the `@theokit/sdk/compaction` sub-path.\n */\n\nimport { TheokitAgentError } from \"./errors.js\";\nimport { diag } from \"./internal/diagnostics.js\";\nimport { selectCompressionWindow } from \"./internal/runtime/compression/compression-helpers.js\";\nimport { redactSecrets } from \"./internal/security/redact.js\";\n\n/**\n * Minimal message shape for compaction/compression input. THE canonical public origin (leaf type —\n * rollup-plugin-dts cannot re-export types from internal modules into entry bundles; M42 lesson).\n *\n * @public\n */\nexport interface CompressibleMessage {\n role: \"user\" | \"assistant\" | \"system\";\n content: string;\n}\n\n/**\n * Sentinel prefix marking a conversation checkpoint turn. A visible, structured,\n * prose-unlikely token (no invisible/control bytes — safe to persist and to read\n * in source). Only {@link buildCheckpoint} should produce content beginning with it.\n */\nexport const CHECKPOINT_MARKER = \"[[theokit:checkpoint]] \";\n\n/**\n * The 7-section summary template handed to the `summarize` callback (theocode\n * parity shape). Every header is always present so the summarizer cannot silently\n * drop a category; the model is told to preserve file paths, commands, and error\n * text verbatim. Override per-call via {@link CompactTranscriptOptions.summaryTemplate}.\n */\nexport const SUMMARY_TEMPLATE = `Summarize the conversation so far into these sections (keep every header even if empty):\n\n## Goal\nWhat the user is ultimately trying to achieve.\n\n## Constraints\nHard requirements, conventions, and rules stated.\n\n## Progress\nWhat has been done so far.\n\n## Decisions\nChoices made and their rationale.\n\n## Next\nThe immediate next steps.\n\n## Critical\nAnything that MUST NOT be forgotten (preserve verbatim: error messages, exact values).\n\n## Files\nFile paths touched or referenced (verbatim).`;\n\n/** Reject an empty marker — it would match every turn via `startsWith(\"\")` (EC-3). */\nfunction assertMarker(marker: string): void {\n if (marker === \"\") {\n throw new TheokitAgentError(\"compaction marker must be non-empty\", {\n code: \"invalid_argument\",\n });\n }\n}\n\n/** True for a real system prompt — a `system` turn that is NOT a checkpoint marker. */\nfunction isSystemPrompt(message: CompressibleMessage, marker: string): boolean {\n return message.role === \"system\" && !message.content.startsWith(marker);\n}\n\n/** Carried split: leading system prompts (preserved), the older window, the verbatim tail. */\ninterface TranscriptSplit {\n systemPrompts: CompressibleMessage[];\n head: CompressibleMessage[];\n recent: CompressibleMessage[];\n}\n\n/**\n * Token-budget split (theocode `splitTranscript`): walk from the END accumulating\n * `estimateTokens` until `keepTokens` is exceeded; everything older is the head.\n * Always keeps ≥ 1 recent turn. No system-prompt special-casing (D6).\n */\nfunction selectByTokenBudget(\n messages: CompressibleMessage[],\n keepTokens: number,\n): { head: CompressibleMessage[]; recent: CompressibleMessage[] } {\n let acc = 0;\n let splitIndex = messages.length;\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n acc += estimateTokens(messages[i]?.content ?? \"\");\n if (acc > keepTokens && i < messages.length - 1) {\n splitIndex = i + 1;\n break;\n }\n splitIndex = i;\n }\n return { head: messages.slice(0, splitIndex), recent: messages.slice(splitIndex) };\n}\n\n/** Turn-count split (M2): preserve leading system prompts; window the rest by `keepRecent`. */\nfunction splitByRecent(\n messages: CompressibleMessage[],\n keepRecent: number,\n marker: string,\n): TranscriptSplit {\n const systemPrompts = messages.filter((m) => isSystemPrompt(m, marker));\n const rest = messages.filter((m) => !isSystemPrompt(m, marker));\n const { toCompress, toPreserve } = selectCompressionWindow(rest, keepRecent);\n return { systemPrompts, head: toCompress, recent: toPreserve };\n}\n\n/** Options for {@link compactTranscript}. */\nexport interface CompactTranscriptOptions {\n /** Trailing turns preserved verbatim by COUNT (default 6). Ignored when `keepTokens` is set. */\n keepRecent?: number;\n /**\n * Trailing turns preserved verbatim by TOKEN BUDGET (theocode mode). When set,\n * takes precedence over `keepRecent` and disables system-prompt preservation (D6).\n */\n keepTokens?: number;\n /** Checkpoint marker (default {@link CHECKPOINT_MARKER}). Must be non-empty. */\n marker?: string;\n /** Summary template passed to `summarize` (default {@link SUMMARY_TEMPLATE}). */\n summaryTemplate?: string;\n /** Summarize the older window into one turn; if omitted, the older window is dropped. */\n summarize?: (older: CompressibleMessage[], template: string) => Promise<CompressibleMessage>;\n /**\n * When true, a thrown `summarize` returns the ORIGINAL transcript + a structured\n * warn (compaction never loses data). Default false: the error propagates.\n */\n failSafe?: boolean;\n}\n\n/** Sentinel returned by {@link runSummarize} when fail-safe swallowed a throw. */\nconst FAILSAFE_ABORT = Symbol(\"failsafe-abort\");\n\n/** Run the summarizer; on throw, either propagate or (fail-safe) warn + signal abort. */\nasync function runSummarize(\n summarize: NonNullable<CompactTranscriptOptions[\"summarize\"]>,\n head: CompressibleMessage[],\n template: string,\n failSafe: boolean,\n): Promise<CompressibleMessage | typeof FAILSAFE_ABORT> {\n try {\n return await summarize(head, template);\n } catch (err) {\n if (!failSafe) throw err;\n // Unbreakable Rule 8 — never fail silently. The breadcrumb points at the root\n // cause when a summarizer fails every turn and context grows unchecked. The\n // summarizer is caller-supplied, so its error text is routed through\n // `redactSecrets` (ADR D68 — no unredacted output sink in src/).\n // theokit#147 — through the interceptable channel. A summarizer failing every turn is exactly\n // the breadcrumb a TUI host wants in its own panel, not smeared across its alternate screen.\n diag(\n `[compaction] summarizer failed — proceeding uncompacted: ${redactSecrets(err instanceof Error ? err.message : String(err))}\\n`,\n );\n return FAILSAFE_ABORT;\n }\n}\n\n/**\n * Compact a transcript. In `keepRecent` mode (default) the last `keepRecent` turns\n * are kept verbatim and leading system PROMPTS preserved; in `keepTokens` mode the\n * trailing turns within the token budget are kept (no system special-casing, D6).\n * The older window is summarized (via `summarize`, receiving the template) or\n * dropped. Never mutates the input.\n */\nexport async function compactTranscript(\n messages: CompressibleMessage[],\n options: CompactTranscriptOptions = {},\n): Promise<CompressibleMessage[]> {\n const marker = options.marker ?? CHECKPOINT_MARKER;\n assertMarker(marker);\n const split =\n options.keepTokens != null\n ? { systemPrompts: [], ...selectByTokenBudget(messages, options.keepTokens) }\n : splitByRecent(messages, options.keepRecent ?? 6, marker);\n if (split.head.length === 0) {\n return [...messages];\n }\n if (!options.summarize) {\n return [...split.systemPrompts, ...split.recent];\n }\n const template = options.summaryTemplate ?? SUMMARY_TEMPLATE;\n const summary = await runSummarize(\n options.summarize,\n split.head,\n template,\n options.failSafe ?? false,\n );\n if (summary === FAILSAFE_ABORT) {\n return [...messages];\n }\n return [...split.systemPrompts, summary, ...split.recent];\n}\n\n/**\n * Build a checkpoint marker turn (a `system` turn whose content starts with\n * `marker`, default {@link CHECKPOINT_MARKER}). `marker` must be non-empty.\n */\nexport function buildCheckpoint(\n label?: string,\n marker: string = CHECKPOINT_MARKER,\n): CompressibleMessage {\n assertMarker(marker);\n return { role: \"system\", content: marker + (label ?? \"\") };\n}\n\n/** Options for {@link filterFromLatestCheckpoint}. */\nexport interface FilterCheckpointOptions {\n /** Marker to scan for (default {@link CHECKPOINT_MARKER}). */\n marker?: string;\n /**\n * `'after'` (default, M2) returns turns AFTER the latest marker (exclusive);\n * `'from'` (theocode) returns turns FROM the latest marker (inclusive).\n */\n include?: \"after\" | \"from\";\n}\n\n/**\n * Return the turns relative to the most recent checkpoint marker (all turns if\n * none). `include: 'after'` (default) excludes the checkpoint; `'from'` includes\n * it (the summary stands in for the pruned head). Never mutates the input.\n */\nexport function filterFromLatestCheckpoint(\n messages: CompressibleMessage[],\n options: FilterCheckpointOptions = {},\n): CompressibleMessage[] {\n const marker = options.marker ?? CHECKPOINT_MARKER;\n const offset = options.include === \"from\" ? 0 : 1;\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.content.startsWith(marker)) {\n return messages.slice(i + offset);\n }\n }\n return [...messages];\n}\n\n/**\n * True iff `err` is a {@link TheokitAgentError} (or subclass) reporting a\n * context-window-exceeded condition (the typed `context_too_long` code). Reads\n * both `code` (set by provider mappers) and `metadata.code` (the preferred field)\n * — never a brittle message regex.\n */\nexport function isContextOverflowError(err: unknown): boolean {\n return (\n err instanceof TheokitAgentError &&\n (err.code === \"context_too_long\" || err.metadata?.code === \"context_too_long\")\n );\n}\n\n/**\n * M77 — the margin is outside `(0, 1]`. Typed, because a margin > 1 would GROW the assumed window,\n * which is the unsafe direction: `shouldCompact` is monotonically decreasing in `contextWindow`, so\n * overestimating it makes the trigger fire too late and the context overflow.\n */\nexport class ContextWindowMarginError extends TheokitAgentError {\n constructor(readonly margin: number) {\n super(\n `context-window margin must be in (0, 1], got ${String(margin)}. ` +\n `A margin above 1 grows the assumed window and delays compaction past the real limit.`,\n { code: \"invalid_context_window_margin\" },\n );\n }\n}\n\n/**\n * M77 — default safety margin on the context window.\n *\n * `0.95` is not a guess: it is the single reference's value. Codex ships\n * `effective_context_window_percent: 95` (`models-manager/src/model_info.rs:158`) and never budgets\n * against 100% of a window. Being MULTIPLICATIVE, it stays conservative at any window size, which a\n * fixed subtraction would not.\n */\nexport const CONTEXT_WINDOW_MARGIN = 0.95;\n\n/**\n * M77 — the floor used ONLY when neither the catalog nor the caller knows the window.\n *\n * Honest about its own weakness. This number has **one** source (the M77 milestone text); the search\n * for a second one failed, because the reference has no fallback at all — Codex returns `Option` and\n * its callers early-return (`core/src/session/turn_context.rs:213`, `core/src/compact_remote.rs:374`).\n *\n * It is conservative only UPWARD. For a model whose real window is larger, budgeting against 128k\n * compacts early — wasteful but safe. For a model whose real window is SMALLER (an 8k or 32k model\n * absent from the catalog), it compacts too late and the context still overflows. That residual risk\n * is real and is why {@link resolveEffectiveContextWindow} never lets this floor compete with a known\n * catalog value, and why the `compaction_fallback` event exists: a surface can show that the budget is\n * a guess instead of the user finding out when the provider rejects the request.\n */\nexport const CONTEXT_WINDOW_FLOOR = 128_000;\n\n/** Where the effective window came from — carried into the M77 structured event. */\nexport type ContextWindowSource = \"override\" | \"catalog\" | \"fallback\";\n\n/** Input to {@link resolveEffectiveContextWindow}. */\nexport interface EffectiveContextWindowInput {\n /** Caller-supplied window, in tokens. Clamped by `catalog` when both are present. */\n readonly override?: number | undefined;\n /** The model's window from the catalog, in tokens. Absent when the model is unknown. */\n readonly catalog?: number | undefined;\n /** Safety margin in `(0, 1]`. Codex uses 0.95 (`model_info.rs:158`). */\n readonly margin: number;\n /** Conservative floor used ONLY when neither `override` nor `catalog` is available. */\n readonly floor?: number | undefined;\n}\n\n/** Result of {@link resolveEffectiveContextWindow}. */\nexport interface EffectiveContextWindow {\n /** The window to budget against, after clamp and margin. */\n readonly window: number;\n /** Which input won — `\"fallback\"` means neither override nor catalog was available. */\n readonly source: ContextWindowSource;\n /** `true` when an `override` above the catalog window was clamped down to it. */\n readonly clamped: boolean;\n}\n\n/**\n * Absolute cap on a declared context window, applied when no catalog entry exists to compare against.\n *\n * **10M**, not 2M. The first version used 2M on the rationale that it sat \"comfortably above the\n * largest published window\" — adversarial review measured the opposite: Llama 4 Scout publishes\n * **10M**, and it arrives precisely via OpenRouter, the provider **without** a catalog, which is the\n * case this cap exists to cover. The user would have silently lost 80% of the declared window.\n *\n * The cap still serves what motivates it — one extra zero on 400k gives 4M, which passes; two zeros\n * give 40M, which does not. A limit that rejects legitimate configuration is worse than no limit,\n * because failing OPEN is visible when it happens and silently losing 80% is not.\n */\nexport const ABSOLUTE_CONTEXT_WINDOW_CAP = 10_000_000;\n\n/**\n * Resolve the window to budget against — the fail-SAFE replacement for reading the catalog directly.\n *\n * Today `post-run-lifecycle.ts` reads `getCatalogModelInfo(model)?.limit?.context` and, when that is\n * `undefined`, auto-compaction simply never fires. That is fail-OPEN: the context grows until the\n * provider rejects the request. (The comment there calls it \"fail-safe\" — M77 corrects it.)\n *\n * Three techniques, two of them borrowed from the single reference:\n *\n * 1. **Override, CLAMPED by the catalog** — Codex `models-manager/src/model_info.rs:26-31` lets\n * config override the window but limits it with `context_window.min(max_context_window)`.\n * Without the clamp, declaring 999k on a 200k model reopens the overflow through another door.\n * 2. **Multiplicative margin** — Codex `model_info.rs:158` (`effective_context_window_percent: 95`)\n * never budgets against 100% of the window. Being multiplicative, it is conservative for ANY\n * window size.\n * 3. **Floor, only when nothing is known** — deliberately NOT a blanket default. A fixed floor is\n * conservative only upward: applied to a small model it would inflate the assumed window and\n * reproduce the very fail-open this function exists to close. Hence the floor never competes\n * with a known catalog value.\n *\n * Pure — no catalog lookup, no I/O. The caller supplies the numbers, mirroring `shouldCompact`.\n */\nexport function resolveEffectiveContextWindow(\n input: EffectiveContextWindowInput,\n): EffectiveContextWindow {\n if (!(input.margin > 0) || input.margin > 1) {\n throw new ContextWindowMarginError(input.margin);\n }\n\n const withMargin = (raw: number): number => Math.floor(raw * input.margin);\n\n if (input.override !== undefined) {\n // The catalog is the preferred cap; without it the ABSOLUTE cap applies.\n //\n // M95 (adversarial review of M94) — the previous version clamped **only** when a catalog entry\n // existed, and the whole reason the `contextWindow` key exists is the model that has NO entry\n // (OpenRouter has zero). So the clamp was missing in exactly the case that justifies the\n // feature, while the documentation — including an already-published CHANGELOG — stated without\n // qualification that \"declaring 10M does not blow past the provider\".\n //\n // The concrete scenario is one extra zero: `context_window = 4000000`. The only guard was a\n // `.positive().int()`, which sets no upper bound. The agent would never compact until the\n // provider refused the turn — the silent fail-OPEN that M77 exists to prevent.\n const cap = input.catalog ?? ABSOLUTE_CONTEXT_WINDOW_CAP;\n const clamped = input.override > cap;\n return { window: withMargin(clamped ? cap : input.override), source: \"override\", clamped };\n }\n\n if (input.catalog !== undefined) {\n return { window: withMargin(input.catalog), source: \"catalog\", clamped: false };\n }\n\n return { window: withMargin(input.floor ?? 0), source: \"fallback\", clamped: false };\n}\n\n/** Input to {@link shouldCompact}: an estimate, the model's window, and reserved headroom. */\nexport interface ShouldCompactInput {\n /** Estimated token count of the next request (e.g. from {@link estimateTokens}). */\n readonly estimated: number;\n /** The model's total context window, in tokens. */\n readonly contextWindow: number;\n /** Tokens to reserve as headroom (output + safety margin). */\n readonly buffer: number;\n /**\n * Tokens reserved for the model's response generation, SEPARATE from `buffer`.\n * Default 0 — omitting it preserves the legacy\n * `estimated >= contextWindow - buffer` result.\n */\n readonly maxOutput?: number;\n}\n\n/**\n * Characters per token in the tokenizer-free estimate below.\n *\n * Exported so the ratio has ONE owner. It used to be a named constant in `built-in-processors.ts`\n * and an inlined `4` here, with both `estimateTokens` functions `@public` from two different package\n * entry points — so tuning the ratio, or switching to code points instead of UTF-16 units (the\n * caveat both docblocks already carried), would have silently diverged them.\n *\n * @public\n */\nexport const CHARS_PER_TOKEN = 4;\n\n/**\n * Tokenizer-free token estimate via the conventional ~4-chars-per-token\n * heuristic: `ceil(text.length / CHARS_PER_TOKEN)`. `\"\"` → 0; any non-empty text → ≥ 1.\n * A cheap PRE-CALL gate for {@link shouldCompact} — NOT exact tokenization\n * (a consumer needing exactness supplies their own tokenizer). Uses UTF-16\n * `.length` (code units), so multibyte text is approximate.\n *\n * `built-in-processors.ts` re-exports this under the same name, so both entry points keep working\n * and there is one implementation behind them.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\n/**\n * Decide BEFORE sending whether to compact: `true` when the `estimated` token\n * count leaves less than `buffer` headroom in the `contextWindow`\n * (`estimated >= contextWindow - buffer`). A `buffer >= contextWindow`\n * (non-positive threshold) always returns `true`. Pure — the caller supplies\n * the window (e.g. from `resolveModelCapabilities`), keeping this decoupled\n * from the per-model catalog.\n */\nexport function shouldCompact(input: ShouldCompactInput): boolean {\n return input.estimated >= input.contextWindow - input.buffer - (input.maxOutput ?? 0);\n}\n\nexport type { CompressionConfig } from \"./internal/runtime/compression/compression-config.js\";\nexport { CompressionModelUnresolvedError } from \"./internal/runtime/compression/compression-model-registry.js\";\n// `CompressionFailedError` is exported from `./errors`, not here: compression-summarizer.ts\n// imports `CompressibleMessage` from THIS module, so exporting from it closes a cycle madge\n// rejects (tests/architecture/no-cycles-at-all.test.ts). The errors entry is where a consumer\n// looks for a typed error anyway.\n"]}
|