@theokit/sdk 5.11.0 → 5.12.0
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 +110 -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-5DOR5T3E.cjs.map → agent-BVKIUP7F.cjs.map} +1 -1
- package/dist/{agent-XMkGzff_.d.cts → agent-CVzDy_Oj.d.ts} +17 -8
- package/dist/{agent-CmKwSyyD.d.ts → agent-Cn0sphpA.d.cts} +17 -8
- package/dist/agent-IG7BP6GN.js +62 -0
- package/dist/{agent-GWKRARAL.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-7Y23B7XX.js → chunk-4GUFNDZR.js} +5 -5
- package/dist/{chunk-7Y23B7XX.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-4PHSVR2R.js → chunk-BJ7BPLQK.js} +207 -108
- 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-7NQ7HJ3E.cjs → chunk-BRWI4HLK.cjs} +6 -6
- package/dist/{chunk-7NQ7HJ3E.cjs.map → chunk-BRWI4HLK.cjs.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-YEHQ3SIC.cjs → chunk-CGH4BCIY.cjs} +11 -11
- package/dist/{chunk-YEHQ3SIC.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-GMSRRJ53.js → chunk-EEK7S77K.js} +3 -3
- package/dist/{chunk-GMSRRJ53.js.map → chunk-EEK7S77K.js.map} +1 -1
- 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-HI3W37FY.js → chunk-H7G65PNI.js} +5 -5
- package/dist/{chunk-HI3W37FY.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-5H2GJXGC.cjs → chunk-IO4RI5ZJ.cjs} +24 -24
- package/dist/{chunk-5H2GJXGC.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-BZKF7TXX.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-7HUCU3KK.js → chunk-NHOEXFZT.js} +5 -5
- package/dist/{chunk-7HUCU3KK.js.map → chunk-NHOEXFZT.js.map} +1 -1
- package/dist/{chunk-Z5BAFHPU.js → chunk-NJBJ3CUG.js} +6 -6
- package/dist/{chunk-Z5BAFHPU.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-NP362THY.cjs +164 -0
- 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-5OHB5HJF.cjs → chunk-Q6WT5535.cjs} +521 -422
- 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-74OA5DIM.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-N7ZIPW2N.cjs → chunk-YOVZMN4P.cjs} +8 -8
- package/dist/{chunk-N7ZIPW2N.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-ZJAYFSVI.js +158 -0
- 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-F2UDGZCN.js.map → compact-session-3U2JYTVG.js.map} +1 -1
- package/dist/{compact-session-2AETONOS.cjs → compact-session-NM56BREJ.cjs} +24 -24
- package/dist/{compact-session-2AETONOS.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-W4UKACYJ.cjs → inject-session-JVTZNQF6.cjs} +9 -9
- package/dist/{inject-session-W4UKACYJ.cjs.map → inject-session-JVTZNQF6.cjs.map} +1 -1
- package/dist/{inject-session-E5UDFVLI.js → inject-session-THABTKJY.js} +7 -7
- package/dist/{inject-session-E5UDFVLI.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 +5 -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 +22 -22
- package/dist/sandbox/index.js +4 -4
- 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 +26 -22
- package/docs/harness-capability-map.md +4 -4
- package/package.json +1 -1
- package/dist/agent-5DOR5T3E.cjs +0 -71
- package/dist/agent-GWKRARAL.js +0 -62
- package/dist/agent-session-store-ODD3IZCW.js +0 -6
- package/dist/agent-session-store-SVEJHSUA.cjs +0 -23
- package/dist/chunk-4PHSVR2R.js.map +0 -1
- package/dist/chunk-5OHB5HJF.cjs.map +0 -1
- package/dist/chunk-6LHQPOMI.cjs.map +0 -1
- package/dist/chunk-74OA5DIM.js.map +0 -1
- package/dist/chunk-B5MHRQJZ.js.map +0 -1
- package/dist/chunk-BZKF7TXX.cjs.map +0 -1
- package/dist/chunk-CZJ6Q7CW.js.map +0 -1
- package/dist/chunk-SGC7LSHF.cjs +0 -18
- package/dist/chunk-TE6XVTOA.js +0 -115
- package/dist/chunk-TE6XVTOA.js.map +0 -1
- package/dist/chunk-UNFBHV46.cjs.map +0 -1
- package/dist/chunk-WPO3VCY4.cjs +0 -121
- package/dist/chunk-WPO3VCY4.cjs.map +0 -1
- package/dist/compact-session-F2UDGZCN.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,4 +1,4 @@
|
|
|
1
|
-
import { TheokitAgentError } from './chunk-
|
|
1
|
+
import { TheokitAgentError } from './chunk-TNFOU6SB.js';
|
|
2
2
|
import { existsSync, openSync, closeSync, rmSync, readFileSync, writeSync, fchmodSync } from 'fs';
|
|
3
3
|
import { hostname } from 'os';
|
|
4
4
|
|
|
@@ -103,5 +103,5 @@ function createLease(sessionPath, lockPath) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export { SessionBusyError, acquireSessionWriter, sessionHasWriter };
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
106
|
+
//# sourceMappingURL=chunk-5IRDK5BC.js.map
|
|
107
|
+
//# sourceMappingURL=chunk-5IRDK5BC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/persistence/session-writer.ts"],"names":[],"mappings":";;;;AAmDO,IAAM,gBAAA,GAAN,cAA+B,iBAAA,CAAkB;AAAA,EAGtD,YAAqB,WAAA,EAAqB;AACxC,IAAA,KAAA;AAAA,MACE,oDAAoD,WAAW,CAAA,gJAAA,CAAA;AAAA,MAG/D,EAAE,IAAA,EAAM,cAAA,EAAgB,WAAA,EAAa,KAAA;AAAM,KAC7C;AANmB,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EAOrB;AAAA,EAPqB,WAAA;AAAA,EAFH,IAAA,GAAO,kBAAA;AAU3B;AA0CO,IAAM,mBAAA,GAAsB,GAAA;AAUnC,SAAS,QAAA,GAAsB;AAC7B,EAAA,OAAO,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAU,UAAS,EAAG,KAAA,EAAO,IAAA,CAAK,GAAA,EAAI,EAAE;AACrE;AAWA,SAAS,UAAA,CAAW,UAAkB,KAAA,EAAwB;AAC5D,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,QAAA,EAAU,GAAA,EAAK,GAAK,CAAA;AACxC,EAAA,IAAI;AACF,IAAA,SAAA,CAAU,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAInC,IAAA,UAAA,CAAW,IAAI,GAAK,CAAA;AAAA,EACtB,CAAA,SAAE;AACA,IAAA,SAAA,CAAU,EAAE,CAAA;AAAA,EACd;AACF;AAGA,SAAS,UAAU,QAAA,EAAyC;AAC1D,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,YAAA,CAAa,UAAU,MAAM,CAAA;AAAA,EACrC,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,OAAQ,GAAA,CAA8B,IAAA;AAE5C,IAAA,IAAI,IAAA,KAAS,UAAU,OAAO,MAAA;AAO9B,IAAA,IAAI,IAAA,KAAS,UAAU,OAAO,MAAA;AAQ9B,IAAA,MAAM,IAAI,gBAAA,CAAiB,QAAA,CAAS,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAA;AAAA,EACpE;AACA,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACxB,IAAA,IACE,OAAO,CAAA,CAAE,GAAA,KAAQ,QAAA,IACjB,OAAO,CAAA,CAAE,QAAA,KAAa,QAAA,IACtB,OAAO,CAAA,CAAE,KAAA,KAAU,QAAA,EACnB;AACA,MAAA,OAAO,KAAA,CAAA;AAAA,IACT;AACA,IAAA,OAAO,EAAE,KAAK,CAAA,CAAE,GAAA,EAAK,UAAU,CAAA,CAAE,QAAA,EAAU,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM;AAAA,EAC5D,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAGA,SAAS,aAAa,GAAA,EAAsB;AAC1C,EAAA,IAAI;AACF,IAAA,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAC,CAAA;AACnB,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,GAAA,EAAK;AAEZ,IAAA,OAAQ,IAA8B,IAAA,KAAS,OAAA;AAAA,EACjD;AACF;AAwBA,SAAS,YAAY,KAAA,EAAuC;AAC1D,EAAA,IAAI,KAAA,KAAU,QAAW,OAAO,IAAA;AAChC,EAAA,IAAI,KAAA,CAAM,QAAA,KAAa,QAAA,EAAS,EAAG;AACjC,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA,CAAM,KAAA,GAAQ,mBAAA;AAAA,EACpC;AACA,EAAA,OAAO,CAAC,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA;AAChC;AAcO,SAAS,iBAAiB,WAAA,EAA8B;AAC7D,EAAA,MAAM,QAAA,GAAW,GAAG,WAAW,CAAA,YAAA,CAAA;AAC/B,EAAA,IAAI,CAAC,UAAA,CAAW,QAAQ,CAAA,EAAG,OAAO,KAAA;AAClC,EAAA,IAAI;AACF,IAAA,OAAO,CAAC,WAAA,CAAY,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,EACzC,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AASA,eAAsB,qBAAqB,WAAA,EAAkD;AAU3F,EAAA,MAAM,QAAA,GAAW,GAAG,WAAW,CAAA,YAAA,CAAA;AAC/B,EAAA,MAAM,OAAkB,QAAA,EAAS;AACjC,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI;AAEF,IAAA,EAAA,GAAK,QAAA,CAAS,QAAA,EAAU,IAAA,EAAM,GAAK,CAAA;AAAA,EACrC,SAAS,GAAA,EAAK;AACZ,IAAA,IAAK,GAAA,CAA8B,IAAA,KAAS,QAAA,EAAU,MAAM,GAAA;AAI5D,IAAA,IAAI,CAAC,YAAY,SAAA,CAAU,QAAQ,CAAC,CAAA,EAAG,MAAM,IAAI,gBAAA,CAAiB,WAAW,CAAA;AAC7E,IAAA,UAAA,CAAW,UAAU,IAAI,CAAA;AACzB,IAAA,OAAO,WAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC1C;AACA,EAAA,SAAA,CAAU,EAAE,CAAA;AACZ,EAAA,UAAA,CAAW,UAAU,IAAI,CAAA;AAEzB,EAAA,OAAO,WAAA,CAAY,aAAa,QAAQ,CAAA;AAC1C;AAGA,SAAS,WAAA,CAAY,aAAqB,QAAA,EAAsC;AAC9E,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,SAAS,YAA2B;AAClC,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,MAAA,CAAO,QAAA,EAAU,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,OAAO,MAAY;AAIjB,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,UAAA,CAAW,QAAA,EAAU,UAAU,CAAA;AAAA,IACjC;AAAA,GACF;AACF","file":"chunk-GTX6FEDP.js","sourcesContent":["/**\n * M81 — single-writer lease for a session transcript.\n *\n * ## The problem\n *\n * Nothing stops two processes appending to the same JSONL transcript. The concrete case: `exec\n * resume --last` can write into the TUI's live session. Two interleaved appends to an append-only\n * file produce lines that are each individually valid and whose SEQUENCE is fiction — and nothing\n * reports it, because every line parses.\n *\n * ## Why an exclusive lockfile rather than `withFileLock`\n *\n * The plan's ADR D2 said to compose `withFileLock`, and that was the right instinct — do not build a\n * second lock mechanism. It turned out not to fit the SHAPE: `withFileLock(path, fn)` is\n * scope-based — it holds the lock for the duration of a callback. A session lease is **held across\n * turns**, for as long as the process owns the session, with an explicit `release()`. Wrapping the\n * whole session lifetime in a callback would invert control of the entire agent loop.\n *\n * So this uses the same underlying primitive `withFileLock` uses (an exclusive-create lockfile,\n * `wx`) with lease semantics on top. That keeps the mechanism single — the file-existence lock —\n * while giving it the lifetime the caller needs. The deviation from D2 is recorded here because the\n * plan's rationale (no second mechanism) still holds; only its shape assumption did not.\n *\n * ## Fail fast, never wait\n *\n * A second writer that WAITED would block `exec` behind a TUI session that can last hours. The typed\n * error lets the caller choose: fork to a new id, or give up with a real diagnosis.\n *\n * @internal\n */\n\nimport {\n closeSync,\n existsSync,\n fchmodSync,\n openSync,\n readFileSync,\n rmSync,\n writeSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — another process already holds the writer lease for this session.\n *\n * Carries `sessionPath` because knowing WHICH session is busy is what lets the caller decide between\n * forking and waiting for the user to close the TUI (`rules/error-handling.md § 2` — context enough\n * to act on).\n */\nexport class SessionBusyError extends TheokitAgentError {\n override readonly name = \"SessionBusyError\";\n\n constructor(readonly sessionPath: string) {\n super(\n `another process is already writing this session: ${sessionPath}. ` +\n \"Fork it to a new id instead of appending — two writers interleave lines into a sequence \" +\n \"that parses but is not what either process wrote.\",\n { code: \"session_busy\", isRetryable: false },\n );\n }\n}\n\n/** A held writer lease. `release()` is idempotent. */\nexport interface SessionWriterLease {\n readonly sessionPath: string;\n release(): Promise<void>;\n /**\n * Re-stamp the ownership record, so a **live** owner never crosses the staleness window.\n *\n * Idempotent and cheap: one `write` of a ~80-byte JSON. Call it on the path that already writes to\n * the session — appending a turn — and the cross-host window stops being a lie about liveness.\n *\n * A no-op after `release()`: renewing a lease you no longer hold would re-create the lock file and\n * hand this process ownership it gave up. That is the one direction of this API that could\n * *create* the double-writer it exists to prevent.\n */\n renew(): void;\n}\n\n/**\n * Staleness window **between machines** — and only between them.\n *\n * 30 s from ACQUISITION. Calling it a \"heartbeat\" would be a lie: the record is written once, when\n * the lease is taken, and is **not** renewed on every write. An earlier version of this comment\n * claimed \"the owner touches the file on every acquisition, so a live process never crosses the\n * window\" — false twice over, and adversarial review measured both.\n *\n * On the **same host** this does not matter: `reclaimable` decides by `pid`, which is exact, and age\n * never enters the calculation. Across hosts it does matter, and it is a real limit: a **live**\n * remote owner loses the lease after 30 s, because there is no way to ask another machine whether\n * its process still exists.\n *\n * **The residue is now closable by the caller** (`agent-builder#118`). `SessionWriterLease.renew()`\n * re-stamps the record; calling it on the path that already writes to the session — appending a turn\n * — keeps a live owner from ever crossing the window. It costs one `write` of ~80 bytes on a file\n * that was previously written once per session.\n *\n * It is `renew()` and not an internal timer on purpose. A timer inside the lease would keep the event\n * loop alive (or need `unref` plus its own teardown), and it would renew a lease belonging to a\n * process that is hung rather than working — which is precisely the state the window exists to\n * detect. Tying the renewal to a real write means the record tracks **progress**, not mere existence.\n */\nexport const HEARTBEAT_WINDOW_MS = 30_000;\n\n/** Who holds the lock. Written as JSON into `.writer.lock`. */\ninterface LockOwner {\n pid: number;\n hostname: string;\n mtime: number;\n}\n\n/** The ownership record for THIS process, stamped now. One place, so acquire and renew cannot drift. */\nfunction ownerNow(): LockOwner {\n return { pid: process.pid, hostname: hostname(), mtime: Date.now() };\n}\n\n/**\n * Writes the owner with user-only permissions.\n *\n * `0600` because the lock is an assertion of OWNERSHIP: with the `0664` the usual umask produces,\n * another user in the same group can overwrite the file and forge ownership of the session — and\n * from then on it is the legitimate owner who starts receiving `SessionBusyError`. The content\n * (`pid`, `hostname`) is low-sensitivity; what the permission protects is the signal's\n * **integrity**, not its secrecy.\n */\nfunction writeOwner(lockPath: string, owner: LockOwner): void {\n const fd = openSync(lockPath, \"w\", 0o600);\n try {\n writeSync(fd, JSON.stringify(owner));\n // `open`'s `mode` only applies on CREATION. A `.writer.lock` inherited from an earlier version —\n // or left behind by a process with a different umask — would stay `0664` after being reclaimed,\n // and the forgery window the mode closes for new locks would remain open for old ones.\n fchmodSync(fd, 0o600);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Reads the lock's owner. `undefined` when the file vanished or the content is unreadable. */\nfunction readOwner(lockPath: string): LockOwner | undefined {\n let raw: string;\n try {\n raw = readFileSync(lockPath, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n // `ENOENT` — vanished between the `EEXIST` and the read. Benign race: the lock is gone.\n if (code === \"ENOENT\") return undefined;\n // `EISDIR` — the lock path is a DIRECTORY. No process in this library creates one; it is debris\n // from something else, and it will never become a readable lock. Failing closed here would lock\n // the session out forever. Treated as owner-less: acquisition proceeds and fails with the real\n // FS error, which says what is wrong — instead of a permanent SessionBusyError that says\n // nothing. It is NOT \"reclaimable\": the lock is not removed, and the caller proceeds without a\n // lease.\n if (code === \"EISDIR\") return undefined;\n // Any other read failure (`EACCES` in a shared directory, `EIO`) differs in kind: the lock\n // **exists** and it is we who cannot read the owner. Treating it as free would let two writers\n // coexist — precisely what the lease exists to prevent — and the `0600` that protects the lock\n // against forgery WIDENS that surface: in a shared directory, another user's lock is unreadable\n // by design.\n //\n // Not knowing who the owner is differs from there being no owner. Fail closed.\n throw new SessionBusyError(lockPath.replace(/\\.writer\\.lock$/, \"\"));\n }\n try {\n const d = JSON.parse(raw) as Partial<LockOwner>;\n if (\n typeof d.pid !== \"number\" ||\n typeof d.hostname !== \"string\" ||\n typeof d.mtime !== \"number\"\n ) {\n return undefined;\n }\n return { pid: d.pid, hostname: d.hostname, mtime: d.mtime };\n } catch {\n // Unreadable JSON: a lock nobody can interpret must not lock the session out forever. Treating\n // it as stale is the recoverable choice; the cost is the same as that of an old lock.\n return undefined;\n }\n}\n\n/** Does the process exist? `signal 0` sends nothing — it only queries permission/existence. */\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means it EXISTS and belongs to another user.\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/**\n * Can the lock be taken from whoever holds it?\n *\n * ADR-2 of the plan: reclaiming by `pid` alone has a false positive across machines — the same\n * number exists on another host, pointing at an unrelated process. So:\n *\n * - **same host:** the `pid` is authoritative, and it **alone**. Dead process => reclaimable at\n * once; live process => never, however old the lock is.\n * - **other host:** the `pid` says nothing here. Only the heartbeat window counts, because it is\n * the one signal that does not lie across machines.\n *\n * ## Why age does NOT count on the same host\n *\n * The first version did `stale || !processAlive(pid)`, and that was a serious defect: `mtime` is\n * written at **acquisition** and is not touched on each append, so any session lasting longer than\n * the window — that is, **every real session** — became stealable by another process. Two writers\n * on the same transcript is exactly what the lease exists to prevent.\n *\n * On the same host the question \"does the owner still exist?\" has an exact answer, and age adds no\n * information to it — only a way to be wrong. Keeping the window there would be heuristic layered\n * on top of a fact.\n */\nfunction reclaimable(owner: LockOwner | undefined): boolean {\n if (owner === undefined) return true; // unreadable or vanished\n if (owner.hostname !== hostname()) {\n return Date.now() - owner.mtime > HEARTBEAT_WINDOW_MS;\n }\n return !processAlive(owner.pid);\n}\n\n/**\n * Does the session have a writer **right now**? A query that does NOT take the lease.\n *\n * M95 — it exists because asking by taking creates the very contention it meant to detect: two\n * processes querying a **free** session at the same time made one of them lose, and the consumer\n * forked for no reason. Measured in adversarial review: `RACE: spurious forks = 1`.\n *\n * It is a snapshot, not a guarantee: between the query and the real acquisition someone may take\n * the session. Callers needing the guarantee use {@link acquireSessionWriter}; callers needing to\n * **decide an id before opening anything** use this, and handle the race where it shows up.\n *\n */\nexport function sessionHasWriter(sessionPath: string): boolean {\n const lockPath = `${sessionPath}.writer.lock`;\n if (!existsSync(lockPath)) return false;\n try {\n return !reclaimable(readOwner(lockPath));\n } catch {\n // `readOwner` throws when the lock exists and cannot be read — fail closed, for the same reason\n // as there: not knowing who the owner is differs from there being no owner.\n return true;\n }\n}\n\n/**\n * Take the exclusive writer lease for `sessionPath`, or reject with {@link SessionBusyError}.\n *\n * The lock is a sibling `.lock` file created with `wx` — the same file-existence primitive the\n * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across\n * processes, not just across async tasks in one process.\n */\nexport async function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease> {\n // M95 — `.writer.lock`, NOT `.lock`.\n //\n // `withFileLock(path, fn)` already uses `<path>.lock` as its companion. While the lease was called\n // from nowhere (the defect this milestone fixes) the collision was theoretical; wiring it to the\n // same file would make the long-lived lease block every short critical section on the same path.\n //\n // Two files because they are two things: `withFileLock` protects a section with a start and an\n // end; the lease is OWNERSHIP, held across turns, with an explicit `release()` — the distinction\n // the M81 docstring above already explains.\n const lockPath = `${sessionPath}.writer.lock`;\n const mine: LockOwner = ownerNow();\n let fd: number;\n try {\n // The mode applies on CREATION — the `w` in `writeOwner` does not alter an existing file.\n fd = openSync(lockPath, \"wx\", 0o600);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n // M95 — the lock exists. Until now that alone was enough to refuse, and that was the defect: a\n // TUI killed by SIGKILL locked the user out of their own session PERMANENTLY, with no documented\n // recovery path. Now the lock says who the owner is, and a dead owner yields its place.\n if (!reclaimable(readOwner(lockPath))) throw new SessionBusyError(sessionPath);\n writeOwner(lockPath, mine);\n return createLease(sessionPath, lockPath);\n }\n closeSync(fd);\n writeOwner(lockPath, mine);\n\n return createLease(sessionPath, lockPath);\n}\n\n/** The lease itself — idempotent `release()`. Extracted because acquisition has two exit paths. */\nfunction createLease(sessionPath: string, lockPath: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath,\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n rmSync(lockPath, { force: true });\n },\n renew: (): void => {\n // A released lease does not own the lock. Re-stamping here would RE-CREATE the file and give\n // this process ownership it explicitly gave up — the one way this method could manufacture the\n // double-writer the lease exists to prevent.\n if (released) return;\n writeOwner(lockPath, ownerNow());\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/session-writer.ts"],"names":[],"mappings":"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","file":"chunk-5IRDK5BC.js","sourcesContent":["/**\n * M81 — single-writer lease for a session transcript.\n *\n * ## The problem\n *\n * Nothing stops two processes appending to the same JSONL transcript. The concrete case: `exec\n * resume --last` can write into the TUI's live session. Two interleaved appends to an append-only\n * file produce lines that are each individually valid and whose SEQUENCE is fiction — and nothing\n * reports it, because every line parses.\n *\n * ## Why an exclusive lockfile rather than `withFileLock`\n *\n * The plan's ADR D2 said to compose `withFileLock`, and that was the right instinct — do not build a\n * second lock mechanism. It turned out not to fit the SHAPE: `withFileLock(path, fn)` is\n * scope-based — it holds the lock for the duration of a callback. A session lease is **held across\n * turns**, for as long as the process owns the session, with an explicit `release()`. Wrapping the\n * whole session lifetime in a callback would invert control of the entire agent loop.\n *\n * So this uses the same underlying primitive `withFileLock` uses (an exclusive-create lockfile,\n * `wx`) with lease semantics on top. That keeps the mechanism single — the file-existence lock —\n * while giving it the lifetime the caller needs. The deviation from D2 is recorded here because the\n * plan's rationale (no second mechanism) still holds; only its shape assumption did not.\n *\n * ## Fail fast, never wait\n *\n * A second writer that WAITED would block `exec` behind a TUI session that can last hours. The typed\n * error lets the caller choose: fork to a new id, or give up with a real diagnosis.\n *\n * @internal\n */\n\nimport {\n closeSync,\n existsSync,\n fchmodSync,\n openSync,\n readFileSync,\n rmSync,\n writeSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — another process already holds the writer lease for this session.\n *\n * Carries `sessionPath` because knowing WHICH session is busy is what lets the caller decide between\n * forking and waiting for the user to close the TUI (`rules/error-handling.md § 2` — context enough\n * to act on).\n */\nexport class SessionBusyError extends TheokitAgentError {\n override readonly name = \"SessionBusyError\";\n\n constructor(readonly sessionPath: string) {\n super(\n `another process is already writing this session: ${sessionPath}. ` +\n \"Fork it to a new id instead of appending — two writers interleave lines into a sequence \" +\n \"that parses but is not what either process wrote.\",\n { code: \"session_busy\", isRetryable: false },\n );\n }\n}\n\n/** A held writer lease. `release()` is idempotent. */\nexport interface SessionWriterLease {\n readonly sessionPath: string;\n release(): Promise<void>;\n /**\n * Re-stamp the ownership record, so a **live** owner never crosses the staleness window.\n *\n * Idempotent and cheap: one `write` of a ~80-byte JSON. Call it on the path that already writes to\n * the session — appending a turn — and the cross-host window stops being a lie about liveness.\n *\n * A no-op after `release()`: renewing a lease you no longer hold would re-create the lock file and\n * hand this process ownership it gave up. That is the one direction of this API that could\n * *create* the double-writer it exists to prevent.\n */\n renew(): void;\n}\n\n/**\n * Staleness window **between machines** — and only between them.\n *\n * 30 s from ACQUISITION. Calling it a \"heartbeat\" would be a lie: the record is written once, when\n * the lease is taken, and is **not** renewed on every write. An earlier version of this comment\n * claimed \"the owner touches the file on every acquisition, so a live process never crosses the\n * window\" — false twice over, and adversarial review measured both.\n *\n * On the **same host** this does not matter: `reclaimable` decides by `pid`, which is exact, and age\n * never enters the calculation. Across hosts it does matter, and it is a real limit: a **live**\n * remote owner loses the lease after 30 s, because there is no way to ask another machine whether\n * its process still exists.\n *\n * **The residue is now closable by the caller** (`agent-builder#118`). `SessionWriterLease.renew()`\n * re-stamps the record; calling it on the path that already writes to the session — appending a turn\n * — keeps a live owner from ever crossing the window. It costs one `write` of ~80 bytes on a file\n * that was previously written once per session.\n *\n * It is `renew()` and not an internal timer on purpose. A timer inside the lease would keep the event\n * loop alive (or need `unref` plus its own teardown), and it would renew a lease belonging to a\n * process that is hung rather than working — which is precisely the state the window exists to\n * detect. Tying the renewal to a real write means the record tracks **progress**, not mere existence.\n */\nexport const HEARTBEAT_WINDOW_MS = 30_000;\n\n/** Who holds the lock. Written as JSON into `.writer.lock`. */\ninterface LockOwner {\n pid: number;\n hostname: string;\n mtime: number;\n}\n\n/** The ownership record for THIS process, stamped now. One place, so acquire and renew cannot drift. */\nfunction ownerNow(): LockOwner {\n return { pid: process.pid, hostname: hostname(), mtime: Date.now() };\n}\n\n/**\n * Writes the owner with user-only permissions.\n *\n * `0600` because the lock is an assertion of OWNERSHIP: with the `0664` the usual umask produces,\n * another user in the same group can overwrite the file and forge ownership of the session — and\n * from then on it is the legitimate owner who starts receiving `SessionBusyError`. The content\n * (`pid`, `hostname`) is low-sensitivity; what the permission protects is the signal's\n * **integrity**, not its secrecy.\n */\nfunction writeOwner(lockPath: string, owner: LockOwner): void {\n const fd = openSync(lockPath, \"w\", 0o600);\n try {\n writeSync(fd, JSON.stringify(owner));\n // `open`'s `mode` only applies on CREATION. A `.writer.lock` inherited from an earlier version —\n // or left behind by a process with a different umask — would stay `0664` after being reclaimed,\n // and the forgery window the mode closes for new locks would remain open for old ones.\n fchmodSync(fd, 0o600);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Reads the lock's owner. `undefined` when the file vanished or the content is unreadable. */\nfunction readOwner(lockPath: string): LockOwner | undefined {\n let raw: string;\n try {\n raw = readFileSync(lockPath, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n // `ENOENT` — vanished between the `EEXIST` and the read. Benign race: the lock is gone.\n if (code === \"ENOENT\") return undefined;\n // `EISDIR` — the lock path is a DIRECTORY. No process in this library creates one; it is debris\n // from something else, and it will never become a readable lock. Failing closed here would lock\n // the session out forever. Treated as owner-less: acquisition proceeds and fails with the real\n // FS error, which says what is wrong — instead of a permanent SessionBusyError that says\n // nothing. It is NOT \"reclaimable\": the lock is not removed, and the caller proceeds without a\n // lease.\n if (code === \"EISDIR\") return undefined;\n // Any other read failure (`EACCES` in a shared directory, `EIO`) differs in kind: the lock\n // **exists** and it is we who cannot read the owner. Treating it as free would let two writers\n // coexist — precisely what the lease exists to prevent — and the `0600` that protects the lock\n // against forgery WIDENS that surface: in a shared directory, another user's lock is unreadable\n // by design.\n //\n // Not knowing who the owner is differs from there being no owner. Fail closed.\n throw new SessionBusyError(lockPath.replace(/\\.writer\\.lock$/, \"\"));\n }\n try {\n const d = JSON.parse(raw) as Partial<LockOwner>;\n if (\n typeof d.pid !== \"number\" ||\n typeof d.hostname !== \"string\" ||\n typeof d.mtime !== \"number\"\n ) {\n return undefined;\n }\n return { pid: d.pid, hostname: d.hostname, mtime: d.mtime };\n } catch {\n // Unreadable JSON: a lock nobody can interpret must not lock the session out forever. Treating\n // it as stale is the recoverable choice; the cost is the same as that of an old lock.\n return undefined;\n }\n}\n\n/** Does the process exist? `signal 0` sends nothing — it only queries permission/existence. */\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means it EXISTS and belongs to another user.\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/**\n * Can the lock be taken from whoever holds it?\n *\n * ADR-2 of the plan: reclaiming by `pid` alone has a false positive across machines — the same\n * number exists on another host, pointing at an unrelated process. So:\n *\n * - **same host:** the `pid` is authoritative, and it **alone**. Dead process => reclaimable at\n * once; live process => never, however old the lock is.\n * - **other host:** the `pid` says nothing here. Only the heartbeat window counts, because it is\n * the one signal that does not lie across machines.\n *\n * ## Why age does NOT count on the same host\n *\n * The first version did `stale || !processAlive(pid)`, and that was a serious defect: `mtime` is\n * written at **acquisition** and is not touched on each append, so any session lasting longer than\n * the window — that is, **every real session** — became stealable by another process. Two writers\n * on the same transcript is exactly what the lease exists to prevent.\n *\n * On the same host the question \"does the owner still exist?\" has an exact answer, and age adds no\n * information to it — only a way to be wrong. Keeping the window there would be heuristic layered\n * on top of a fact.\n */\nfunction reclaimable(owner: LockOwner | undefined): boolean {\n if (owner === undefined) return true; // unreadable or vanished\n if (owner.hostname !== hostname()) {\n return Date.now() - owner.mtime > HEARTBEAT_WINDOW_MS;\n }\n return !processAlive(owner.pid);\n}\n\n/**\n * Does the session have a writer **right now**? A query that does NOT take the lease.\n *\n * M95 — it exists because asking by taking creates the very contention it meant to detect: two\n * processes querying a **free** session at the same time made one of them lose, and the consumer\n * forked for no reason. Measured in adversarial review: `RACE: spurious forks = 1`.\n *\n * It is a snapshot, not a guarantee: between the query and the real acquisition someone may take\n * the session. Callers needing the guarantee use {@link acquireSessionWriter}; callers needing to\n * **decide an id before opening anything** use this, and handle the race where it shows up.\n *\n */\nexport function sessionHasWriter(sessionPath: string): boolean {\n const lockPath = `${sessionPath}.writer.lock`;\n if (!existsSync(lockPath)) return false;\n try {\n return !reclaimable(readOwner(lockPath));\n } catch {\n // `readOwner` throws when the lock exists and cannot be read — fail closed, for the same reason\n // as there: not knowing who the owner is differs from there being no owner.\n return true;\n }\n}\n\n/**\n * Take the exclusive writer lease for `sessionPath`, or reject with {@link SessionBusyError}.\n *\n * The lock is a sibling `.lock` file created with `wx` — the same file-existence primitive the\n * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across\n * processes, not just across async tasks in one process.\n */\nexport async function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease> {\n // M95 — `.writer.lock`, NOT `.lock`.\n //\n // `withFileLock(path, fn)` already uses `<path>.lock` as its companion. While the lease was called\n // from nowhere (the defect this milestone fixes) the collision was theoretical; wiring it to the\n // same file would make the long-lived lease block every short critical section on the same path.\n //\n // Two files because they are two things: `withFileLock` protects a section with a start and an\n // end; the lease is OWNERSHIP, held across turns, with an explicit `release()` — the distinction\n // the M81 docstring above already explains.\n const lockPath = `${sessionPath}.writer.lock`;\n const mine: LockOwner = ownerNow();\n let fd: number;\n try {\n // The mode applies on CREATION — the `w` in `writeOwner` does not alter an existing file.\n fd = openSync(lockPath, \"wx\", 0o600);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n // M95 — the lock exists. Until now that alone was enough to refuse, and that was the defect: a\n // TUI killed by SIGKILL locked the user out of their own session PERMANENTLY, with no documented\n // recovery path. Now the lock says who the owner is, and a dead owner yields its place.\n if (!reclaimable(readOwner(lockPath))) throw new SessionBusyError(sessionPath);\n writeOwner(lockPath, mine);\n return createLease(sessionPath, lockPath);\n }\n closeSync(fd);\n writeOwner(lockPath, mine);\n\n return createLease(sessionPath, lockPath);\n}\n\n/** The lease itself — idempotent `release()`. Extracted because acquisition has two exit paths. */\nfunction createLease(sessionPath: string, lockPath: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath,\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n rmSync(lockPath, { force: true });\n },\n renew: (): void => {\n // A released lease does not own the lock. Re-stamping here would RE-CREATE the file and give\n // this process ownership it explicitly gave up — the one way this method could manufacture the\n // double-writer the lease exists to prevent.\n if (released) return;\n writeOwner(lockPath, ownerNow());\n },\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TheokitAgentError } from './chunk-
|
|
1
|
+
import { TheokitAgentError } from './chunk-TNFOU6SB.js';
|
|
2
2
|
|
|
3
3
|
// src/server/auth/errors.ts
|
|
4
4
|
var AuthConfigError = class extends TheokitAgentError {
|
|
@@ -39,5 +39,5 @@ var AuthCancelledError = class extends AuthCallbackError {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
export { AuthCallbackError, AuthCancelledError, AuthConfigError, AuthProviderNotFoundError };
|
|
42
|
-
//# sourceMappingURL=chunk-
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
42
|
+
//# sourceMappingURL=chunk-5IUDICRK.js.map
|
|
43
|
+
//# sourceMappingURL=chunk-5IUDICRK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/auth/errors.ts"],"names":[],"mappings":";;;AAYO,IAAM,eAAA,GAAN,cAA8B,iBAAA,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,cAAwC,iBAAA,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,cAAgC,iBAAA,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":[],"mappings":";;;AAYO,IAAM,eAAA,GAAN,cAA8B,iBAAA,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,cAAwC,iBAAA,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,cAAgC,iBAAA,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-5IUDICRK.js","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,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
3
|
+
var chunkSDZ4XDWS_cjs = require('./chunk-SDZ4XDWS.cjs');
|
|
4
|
+
var chunkZOFRNNZD_cjs = require('./chunk-ZOFRNNZD.cjs');
|
|
5
|
+
var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
|
|
6
|
+
var chunkRNXXYF22_cjs = require('./chunk-RNXXYF22.cjs');
|
|
7
7
|
var crypto = require('crypto');
|
|
8
8
|
var promises = require('fs/promises');
|
|
9
9
|
var path = require('path');
|
|
@@ -37,7 +37,7 @@ async function appendDiaryEntry(root, entry) {
|
|
|
37
37
|
}
|
|
38
38
|
const next = `${raw.endsWith("\n") ? raw : `${raw}
|
|
39
39
|
`}${renderDiaryEntry(entry)}`;
|
|
40
|
-
await
|
|
40
|
+
await chunkZOFRNNZD_cjs.replaceFileAtomic(path, next);
|
|
41
41
|
}
|
|
42
42
|
function entryHash(entry) {
|
|
43
43
|
return crypto.createHash("sha256").update(
|
|
@@ -51,9 +51,9 @@ function entryHash(entry) {
|
|
|
51
51
|
).digest("hex");
|
|
52
52
|
}
|
|
53
53
|
async function readAllSqliteFacts(root) {
|
|
54
|
-
const dbPath =
|
|
54
|
+
const dbPath = chunkSDZ4XDWS_cjs.defaultIndexPath(root);
|
|
55
55
|
if (!fs.existsSync(dbPath)) return [];
|
|
56
|
-
const db = await
|
|
56
|
+
const db = await chunkSDZ4XDWS_cjs.openMemoryDb({ filePath: dbPath });
|
|
57
57
|
try {
|
|
58
58
|
const stmt = db.prepare("SELECT id, path, source, start_line, end_line, text FROM chunks");
|
|
59
59
|
const rows = stmt.all();
|
|
@@ -73,7 +73,7 @@ function nfcEqual(a, b) {
|
|
|
73
73
|
function assertUsableBatchSize(batchSize) {
|
|
74
74
|
if (batchSize === void 0) return;
|
|
75
75
|
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
76
|
-
throw new
|
|
76
|
+
throw new chunkCEGU5AVD_cjs.ConfigurationError(
|
|
77
77
|
`migrateSqliteToLance: batchSize must be a whole number of at least 1; received ${String(batchSize)}.`,
|
|
78
78
|
{ code: "invalid_batch_size" }
|
|
79
79
|
);
|
|
@@ -82,13 +82,13 @@ function assertUsableBatchSize(batchSize) {
|
|
|
82
82
|
async function migrateSqliteToLance(opts) {
|
|
83
83
|
assertUsableBatchSize(opts.batchSize);
|
|
84
84
|
const cwd = opts.cwd;
|
|
85
|
-
const memoryRoot =
|
|
86
|
-
const finalPath =
|
|
85
|
+
const memoryRoot = chunkSDZ4XDWS_cjs.resolveMemoryRoot(cwd, { directory: opts.directory });
|
|
86
|
+
const finalPath = chunkSDZ4XDWS_cjs.lanceStoragePath(memoryRoot);
|
|
87
87
|
const newPath = path.join(memoryRoot, "lance-new");
|
|
88
88
|
const rawLog = opts.logger ?? ((m) => console.log(m));
|
|
89
|
-
const log = (m) => rawLog(
|
|
89
|
+
const log = (m) => rawLog(chunkRNXXYF22_cjs.redactSecrets(m));
|
|
90
90
|
if (fs.existsSync(finalPath)) {
|
|
91
|
-
throw new
|
|
91
|
+
throw new chunkCEGU5AVD_cjs.ConfigurationError(
|
|
92
92
|
`Destination already exists: ${finalPath}. Remove it manually (\`rm -rf ${finalPath}\`) and re-run.`,
|
|
93
93
|
{ code: "migration_destination_exists" }
|
|
94
94
|
);
|
|
@@ -127,7 +127,7 @@ async function migrateSqliteToLance(opts) {
|
|
|
127
127
|
};
|
|
128
128
|
fs.mkdirSync(newPath, { recursive: true });
|
|
129
129
|
log(`Writing Lance index to ${newPath} ...`);
|
|
130
|
-
const lance = await
|
|
130
|
+
const lance = await chunkSDZ4XDWS_cjs.LanceIndex.open({
|
|
131
131
|
cwd,
|
|
132
132
|
embedding: placeholderEmbedding,
|
|
133
133
|
storagePath: newPath
|
|
@@ -207,5 +207,5 @@ exports.entryHash = entryHash;
|
|
|
207
207
|
exports.migrateSqliteToLance = migrateSqliteToLance;
|
|
208
208
|
exports.readAllSqliteFacts = readAllSqliteFacts;
|
|
209
209
|
exports.renderDiaryEntry = renderDiaryEntry;
|
|
210
|
-
//# sourceMappingURL=chunk-
|
|
211
|
-
//# sourceMappingURL=chunk-
|
|
210
|
+
//# sourceMappingURL=chunk-6CXRYPG7.cjs.map
|
|
211
|
+
//# sourceMappingURL=chunk-6CXRYPG7.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/memory/dreaming/diary.ts","../src/internal/memory/migrate-sqlite-to-lance.ts"],"names":["join","readFile","replaceFileAtomic","createHash","defaultIndexPath","existsSync","openMemoryDb","ConfigurationError","resolveMemoryRoot","lanceStoragePath","redactSecrets","rmSync","mkdirSync","LanceIndex","renameSync"],"mappings":"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","file":"chunk-FYEHYQ7T.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { replaceFileAtomic } from \"../../persistence/atomic-write.js\";\nimport type { MemoryRoot } from \"../storage/memory-root.js\";\n\n/**\n * Dream-diary append (ADR D7).\n *\n * Diary lives at `.theokit/memory/dream-diary.md` and grows with one entry\n * per sweep. Each entry is content-hashed so the same input produces the\n * same entry — idempotency contract.\n *\n * Writes go through `replaceFileAtomic` (EC-3 of edge-case review) so a\n * crash mid-write can never leave a half-written diary.\n *\n * Shared with `@theokit/sdk-memory`; see the memory-store barrel.\n */\n\nexport interface DiaryEntry {\n timestampMs: number;\n factsBefore: number;\n factsAfter: number;\n duplicatesRemoved: number;\n clustersCreated: number;\n notesWritten: number;\n}\n\n/** `<memory root>/dream-diary.md`. Takes the RESOLVED ROOT — see `storage/memory-root.ts` (#463). */\nexport function diaryPath(root: MemoryRoot): string {\n return join(root, \"dream-diary.md\");\n}\n\n/**\n * One diary entry as the markdown that gets appended: a timestamp heading, the short entry hash,\n * and the counts the sweep produced. The hash is what makes a re-run recognisable as the same\n * sweep rather than a new one.\n */\nexport function renderDiaryEntry(entry: DiaryEntry): string {\n const stamp = new Date(entry.timestampMs).toISOString();\n const hash = entryHash(entry).slice(0, 8);\n return [\n `## ${stamp}`,\n \"\",\n `- entry-hash: ${hash}`,\n `- facts before: ${entry.factsBefore}`,\n `- facts after: ${entry.factsAfter}`,\n `- duplicates removed: ${entry.duplicatesRemoved}`,\n `- clusters created: ${entry.clustersCreated}`,\n `- notes written: ${entry.notesWritten}`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Append one sweep's entry to `<memory root>/dream-diary.md`, creating the file with its header\n * when this is the first sweep. The diary is a human-readable record of what dreaming changed —\n * consolidations are otherwise invisible, because they alter the notes rather than announce\n * themselves.\n */\nexport async function appendDiaryEntry(root: MemoryRoot, entry: DiaryEntry): Promise<void> {\n const path = diaryPath(root);\n let raw = \"\";\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n raw = \"# Dream Diary\\n\\n\";\n }\n const next = `${raw.endsWith(\"\\n\") ? raw : `${raw}\\n`}${renderDiaryEntry(entry)}`;\n await replaceFileAtomic(path, next);\n}\n\n/**\n * A stable hash of one entry's counts, so two sweeps that did the same work read as the same work.\n * Rendered truncated in the entry; the full value is what callers compare.\n */\nexport function entryHash(entry: DiaryEntry): string {\n return createHash(\"sha256\")\n .update(\n [\n entry.factsBefore,\n entry.factsAfter,\n entry.duplicatesRemoved,\n entry.clustersCreated,\n entry.notesWritten,\n ].join(\"|\"),\n )\n .digest(\"hex\");\n}\n","import { existsSync, mkdirSync, renameSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { redactSecrets } from \"../security/index.js\";\nimport { defaultIndexPath, openMemoryDb } from \"./index-db.js\";\nimport { LanceIndex, lanceStoragePath } from \"./lance-index.js\";\nimport { type MemoryRoot, resolveMemoryRoot } from \"./storage/memory-root.js\";\n\n/**\n * Migrate Memory.index from SQLite to LanceDB (ADR D44).\n *\n * EC-3 MUST FIX: validation uses NFC unicode normalization on both sides.\n *\n * Shared with `@theokit/sdk-memory` through the memory-store barrel (#463).\n */\n\nexport interface MigrateOptions {\n cwd: string;\n /**\n * Absolute path (or `~/`-prefixed) of the memory root to migrate, when the agent that wrote it\n * set `memory.directory`. Defaults to `<cwd>/.theokit/memory` (#463).\n */\n directory?: string;\n dryRun?: boolean;\n batchSize?: number;\n /** Inject for tests; defaults to console.log. */\n logger?: (msg: string) => void;\n}\n\nexport interface MigrateResult {\n countSqlite: number;\n countLance: number;\n /** Set when validation succeeded (or dryRun); false on validation failure. */\n validated: boolean;\n /** Per-fact compare results (size = sample count, up to 10). */\n sampleComparisons: ReadonlyArray<{ id: string; match: boolean }>;\n /** Lance storage location (final, after migration). */\n lancePath: string;\n /** Was the migration committed (false for dry-run). */\n committed: boolean;\n}\n\n/**\n * One row of the SQLite memory index, as the Lance migration reads it: the chunk plus the tenant\n * columns the target table needs. Shared with `@theokit/sdk-memory`, which migrates the same table.\n */\nexport interface SqliteFactRow {\n id: string;\n path: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n start_line: number;\n end_line: number;\n text: string;\n namespace?: string | null;\n scope?: string | null;\n user_id?: string | null;\n}\n\n/**\n * Read all facts from the SQLite memory index. Returns empty array if the\n * SQLite db file does not exist (workspace never used Memory).\n *\n * Shared with `@theokit/sdk-memory` through the memory-store barrel, so it carries no visibility\n * tag — naming that tag in this block would delete the function below it (#463).\n */\nexport async function readAllSqliteFacts(root: MemoryRoot): Promise<SqliteFactRow[]> {\n const dbPath = defaultIndexPath(root);\n if (!existsSync(dbPath)) return [];\n const db = await openMemoryDb({ filePath: dbPath });\n try {\n // SQLite schema: chunks table holds the facts. Schema may vary; we\n // probe column existence and fall back to safe defaults.\n const stmt = db.prepare(\"SELECT id, path, source, start_line, end_line, text FROM chunks\");\n const rows = stmt.all() as Array<{\n id: string;\n path: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n start_line: number;\n end_line: number;\n text: string;\n }>;\n return rows.map((r) => ({\n ...r,\n namespace: \"default\",\n scope: \"agent\",\n user_id: \"default\",\n }));\n } finally {\n db.close();\n }\n}\n\n/**\n * Compare two strings via NFC normalization (EC-3 MUST FIX). Required\n * because SQLite/Lance native bindings can normalize unicode differently\n * (NFC vs NFD), producing false negatives on facts with accents/emojis.\n *\n * @internal\n */\nfunction nfcEqual(a: string, b: string): boolean {\n return a.normalize(\"NFC\") === b.normalize(\"NFC\");\n}\n\n/**\n * Refuse a `batchSize` the loop below cannot make progress with, before anything is migrated.\n *\n * The loop is `for (let i = 0; i < facts.length; i += batchSize)`, so the three ways this goes wrong\n * are all silent and all different:\n *\n * - 0 or negative: `i` never advances past the guard. The migration SPINS FOREVER, calling\n * `addFacts([])` and writing a \"Migrated x/y\" line every iteration — a hang with unbounded output,\n * which is the worst shape a failure can take because it looks like work.\n * - NaN (`Number(\"abc\")`): `i += NaN` makes the guard false on the first check, zero facts move, and\n * the caller is told \"Validation FAILED. SQLite preserved.\" A typo in an argument is reported as a\n * migration failure.\n * - a fraction: `slice` truncates and the loop still terminates, but the batch boundaries stop\n * matching the number the caller asked for.\n *\n * Validated HERE rather than only in the CLI that motivated the finding, because the CLI is a wrapper\n * over this function and a programmatic caller reaches it with no wrapper at all.\n *\n * @internal\n */\nfunction assertUsableBatchSize(batchSize: number | undefined): void {\n if (batchSize === undefined) return;\n if (!Number.isInteger(batchSize) || batchSize < 1) {\n throw new ConfigurationError(\n `migrateSqliteToLance: batchSize must be a whole number of at least 1; received ${String(batchSize)}.`,\n { code: \"invalid_batch_size\" },\n );\n }\n}\n\n/**\n * Run the migration. Writes Lance to `<cwd>/.theokit/memory/lance-new/`\n * first, validates round-trip, then renames to `lance/` on success.\n * SQLite db is preserved (CLI prompts user to delete).\n *\n * @internal\n */\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: migration is a single transaction (read → write → validate → commit-or-rollback); splitting harms atomicity reasoning.\nexport async function migrateSqliteToLance(opts: MigrateOptions): Promise<MigrateResult> {\n assertUsableBatchSize(opts.batchSize);\n const cwd = opts.cwd;\n // One resolution, reused by every path this migration touches (#463).\n const memoryRoot = resolveMemoryRoot(cwd, { directory: opts.directory });\n const finalPath = lanceStoragePath(memoryRoot);\n const newPath = join(memoryRoot, \"lance-new\");\n // T1.4 (ADR D68): wrap logger so any fact text containing secrets is\n // masked before reaching the destination (console or user-supplied sink).\n // Caller-supplied loggers cannot bypass — by design (D70).\n const rawLog = opts.logger ?? ((m: string) => console.log(m));\n const log = (m: string) => rawLog(redactSecrets(m));\n\n // EC: destination exists → typed error.\n if (existsSync(finalPath)) {\n throw new ConfigurationError(\n `Destination already exists: ${finalPath}. Remove it manually (\\`rm -rf ${finalPath}\\`) and re-run.`,\n { code: \"migration_destination_exists\" },\n );\n }\n if (existsSync(newPath)) {\n // Leftover from a previous failed/interrupted migration. Safe to delete.\n log(`Removing stale ${newPath} from a previous run...`);\n rmSync(newPath, { recursive: true, force: true });\n }\n\n log(`Reading SQLite facts from ${cwd}/.theokit/memory/index.sqlite ...`);\n const sqliteFacts = await readAllSqliteFacts(memoryRoot);\n log(`SQLite has ${sqliteFacts.length} facts.`);\n if (sqliteFacts.length === 0) {\n return {\n countSqlite: 0,\n countLance: 0,\n validated: true,\n sampleComparisons: [],\n lancePath: finalPath,\n committed: false,\n };\n }\n\n // Reuse the embedding runtime configured in the workspace? For v1.2 we\n // require the caller to pass embedding explicitly via env. The migration\n // path is documented as: \"set OPENAI_API_KEY (or similar) before running.\"\n // Here we ship a placeholder embedder that uses a deterministic\n // content-hash — sufficient for migration validation; consumers should\n // re-embed on first real query after migration if needed.\n const placeholderEmbedding = {\n id: \"migration-placeholder\",\n model: \"deterministic-hash\",\n dimension: 8,\n embed: async (texts: ReadonlyArray<string>) => {\n // Simple deterministic hash → 8-dim vector (migration validation only).\n return texts.map((t) => {\n const arr = new Array(8).fill(0);\n for (let i = 0; i < t.length; i += 1) {\n arr[i % 8] += t.charCodeAt(i);\n }\n return arr.map((x) => x / 1000);\n });\n },\n stats: () => ({ cacheHits: 0, cacheMisses: 0, httpCalls: 0, retries: 0 }),\n };\n\n mkdirSync(newPath, { recursive: true });\n log(`Writing Lance index to ${newPath} ...`);\n\n const lance = await LanceIndex.open({\n cwd,\n embedding: placeholderEmbedding,\n storagePath: newPath,\n });\n\n const batchSize = opts.batchSize ?? 100;\n for (let i = 0; i < sqliteFacts.length; i += batchSize) {\n const batch = sqliteFacts.slice(i, i + batchSize).map((f) => ({\n id: f.id,\n text: f.text,\n source: f.source,\n namespace: f.namespace ?? \"default\",\n scope: f.scope ?? \"agent\",\n user_id: f.user_id ?? \"default\",\n timestamp: 0,\n }));\n await lance.addFacts(batch);\n log(` Migrated ${Math.min(i + batchSize, sqliteFacts.length)}/${sqliteFacts.length}`);\n }\n\n // Validation: count + sample compare (10 random) with NFC normalization.\n const lanceCount = await lance.countFacts(\"default\");\n log(`Lance has ${lanceCount} facts (expected ${sqliteFacts.length}).`);\n\n const sampleSize = Math.min(10, sqliteFacts.length);\n const sampleComparisons: { id: string; match: boolean }[] = [];\n for (let i = 0; i < sampleSize; i += 1) {\n const sqliteFact = sqliteFacts[Math.floor((i * sqliteFacts.length) / sampleSize)];\n if (sqliteFact === undefined) continue;\n // Search Lance for this fact's text — best-effort round-trip.\n const hits = await lance.search(sqliteFact.text, {\n namespace: sqliteFact.namespace ?? \"default\",\n limit: 1,\n });\n // EC-3: compare with NFC normalization.\n const hit = hits[0];\n const match = hit !== undefined && nfcEqual(hit.text, sqliteFact.text);\n sampleComparisons.push({ id: sqliteFact.id, match });\n }\n\n const allMatch = sampleComparisons.every((c) => c.match);\n const countMatch = lanceCount === sqliteFacts.length;\n const validated = allMatch && countMatch;\n\n await lance.close();\n\n if (opts.dryRun === true) {\n log(\"Dry-run mode — discarding Lance staging dir.\");\n rmSync(newPath, { recursive: true, force: true });\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated,\n sampleComparisons,\n lancePath: finalPath,\n committed: false,\n };\n }\n\n if (!validated) {\n log(\"Validation FAILED — leaving SQLite intact, removing Lance staging dir.\");\n rmSync(newPath, { recursive: true, force: true });\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated: false,\n sampleComparisons,\n lancePath: finalPath,\n committed: false,\n };\n }\n\n // Atomic commit via rename.\n renameSync(newPath, finalPath);\n log(`Migration committed to ${finalPath}.`);\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated: true,\n sampleComparisons,\n lancePath: finalPath,\n committed: true,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/memory/dreaming/diary.ts","../src/internal/memory/migrate-sqlite-to-lance.ts"],"names":["join","readFile","replaceFileAtomic","createHash","defaultIndexPath","existsSync","openMemoryDb","ConfigurationError","resolveMemoryRoot","lanceStoragePath","redactSecrets","rmSync","mkdirSync","LanceIndex","renameSync"],"mappings":"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","file":"chunk-6CXRYPG7.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { replaceFileAtomic } from \"../../persistence/atomic-write.js\";\nimport type { MemoryRoot } from \"../storage/memory-root.js\";\n\n/**\n * Dream-diary append (ADR D7).\n *\n * Diary lives at `.theokit/memory/dream-diary.md` and grows with one entry\n * per sweep. Each entry is content-hashed so the same input produces the\n * same entry — idempotency contract.\n *\n * Writes go through `replaceFileAtomic` (EC-3 of edge-case review) so a\n * crash mid-write can never leave a half-written diary.\n *\n * Shared with `@theokit/sdk-memory`; see the memory-store barrel.\n */\n\nexport interface DiaryEntry {\n timestampMs: number;\n factsBefore: number;\n factsAfter: number;\n duplicatesRemoved: number;\n clustersCreated: number;\n notesWritten: number;\n}\n\n/** `<memory root>/dream-diary.md`. Takes the RESOLVED ROOT — see `storage/memory-root.ts` (#463). */\nexport function diaryPath(root: MemoryRoot): string {\n return join(root, \"dream-diary.md\");\n}\n\n/**\n * One diary entry as the markdown that gets appended: a timestamp heading, the short entry hash,\n * and the counts the sweep produced. The hash is what makes a re-run recognisable as the same\n * sweep rather than a new one.\n */\nexport function renderDiaryEntry(entry: DiaryEntry): string {\n const stamp = new Date(entry.timestampMs).toISOString();\n const hash = entryHash(entry).slice(0, 8);\n return [\n `## ${stamp}`,\n \"\",\n `- entry-hash: ${hash}`,\n `- facts before: ${entry.factsBefore}`,\n `- facts after: ${entry.factsAfter}`,\n `- duplicates removed: ${entry.duplicatesRemoved}`,\n `- clusters created: ${entry.clustersCreated}`,\n `- notes written: ${entry.notesWritten}`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Append one sweep's entry to `<memory root>/dream-diary.md`, creating the file with its header\n * when this is the first sweep. The diary is a human-readable record of what dreaming changed —\n * consolidations are otherwise invisible, because they alter the notes rather than announce\n * themselves.\n */\nexport async function appendDiaryEntry(root: MemoryRoot, entry: DiaryEntry): Promise<void> {\n const path = diaryPath(root);\n let raw = \"\";\n try {\n raw = await readFile(path, \"utf8\");\n } catch {\n raw = \"# Dream Diary\\n\\n\";\n }\n const next = `${raw.endsWith(\"\\n\") ? raw : `${raw}\\n`}${renderDiaryEntry(entry)}`;\n await replaceFileAtomic(path, next);\n}\n\n/**\n * A stable hash of one entry's counts, so two sweeps that did the same work read as the same work.\n * Rendered truncated in the entry; the full value is what callers compare.\n */\nexport function entryHash(entry: DiaryEntry): string {\n return createHash(\"sha256\")\n .update(\n [\n entry.factsBefore,\n entry.factsAfter,\n entry.duplicatesRemoved,\n entry.clustersCreated,\n entry.notesWritten,\n ].join(\"|\"),\n )\n .digest(\"hex\");\n}\n","import { existsSync, mkdirSync, renameSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { redactSecrets } from \"../security/index.js\";\nimport { defaultIndexPath, openMemoryDb } from \"./index-db.js\";\nimport { LanceIndex, lanceStoragePath } from \"./lance-index.js\";\nimport { type MemoryRoot, resolveMemoryRoot } from \"./storage/memory-root.js\";\n\n/**\n * Migrate Memory.index from SQLite to LanceDB (ADR D44).\n *\n * EC-3 MUST FIX: validation uses NFC unicode normalization on both sides.\n *\n * Shared with `@theokit/sdk-memory` through the memory-store barrel (#463).\n */\n\nexport interface MigrateOptions {\n cwd: string;\n /**\n * Absolute path (or `~/`-prefixed) of the memory root to migrate, when the agent that wrote it\n * set `memory.directory`. Defaults to `<cwd>/.theokit/memory` (#463).\n */\n directory?: string;\n dryRun?: boolean;\n batchSize?: number;\n /** Inject for tests; defaults to console.log. */\n logger?: (msg: string) => void;\n}\n\nexport interface MigrateResult {\n countSqlite: number;\n countLance: number;\n /** Set when validation succeeded (or dryRun); false on validation failure. */\n validated: boolean;\n /** Per-fact compare results (size = sample count, up to 10). */\n sampleComparisons: ReadonlyArray<{ id: string; match: boolean }>;\n /** Lance storage location (final, after migration). */\n lancePath: string;\n /** Was the migration committed (false for dry-run). */\n committed: boolean;\n}\n\n/**\n * One row of the SQLite memory index, as the Lance migration reads it: the chunk plus the tenant\n * columns the target table needs. Shared with `@theokit/sdk-memory`, which migrates the same table.\n */\nexport interface SqliteFactRow {\n id: string;\n path: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n start_line: number;\n end_line: number;\n text: string;\n namespace?: string | null;\n scope?: string | null;\n user_id?: string | null;\n}\n\n/**\n * Read all facts from the SQLite memory index. Returns empty array if the\n * SQLite db file does not exist (workspace never used Memory).\n *\n * Shared with `@theokit/sdk-memory` through the memory-store barrel, so it carries no visibility\n * tag — naming that tag in this block would delete the function below it (#463).\n */\nexport async function readAllSqliteFacts(root: MemoryRoot): Promise<SqliteFactRow[]> {\n const dbPath = defaultIndexPath(root);\n if (!existsSync(dbPath)) return [];\n const db = await openMemoryDb({ filePath: dbPath });\n try {\n // SQLite schema: chunks table holds the facts. Schema may vary; we\n // probe column existence and fall back to safe defaults.\n const stmt = db.prepare(\"SELECT id, path, source, start_line, end_line, text FROM chunks\");\n const rows = stmt.all() as Array<{\n id: string;\n path: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n start_line: number;\n end_line: number;\n text: string;\n }>;\n return rows.map((r) => ({\n ...r,\n namespace: \"default\",\n scope: \"agent\",\n user_id: \"default\",\n }));\n } finally {\n db.close();\n }\n}\n\n/**\n * Compare two strings via NFC normalization (EC-3 MUST FIX). Required\n * because SQLite/Lance native bindings can normalize unicode differently\n * (NFC vs NFD), producing false negatives on facts with accents/emojis.\n *\n * @internal\n */\nfunction nfcEqual(a: string, b: string): boolean {\n return a.normalize(\"NFC\") === b.normalize(\"NFC\");\n}\n\n/**\n * Refuse a `batchSize` the loop below cannot make progress with, before anything is migrated.\n *\n * The loop is `for (let i = 0; i < facts.length; i += batchSize)`, so the three ways this goes wrong\n * are all silent and all different:\n *\n * - 0 or negative: `i` never advances past the guard. The migration SPINS FOREVER, calling\n * `addFacts([])` and writing a \"Migrated x/y\" line every iteration — a hang with unbounded output,\n * which is the worst shape a failure can take because it looks like work.\n * - NaN (`Number(\"abc\")`): `i += NaN` makes the guard false on the first check, zero facts move, and\n * the caller is told \"Validation FAILED. SQLite preserved.\" A typo in an argument is reported as a\n * migration failure.\n * - a fraction: `slice` truncates and the loop still terminates, but the batch boundaries stop\n * matching the number the caller asked for.\n *\n * Validated HERE rather than only in the CLI that motivated the finding, because the CLI is a wrapper\n * over this function and a programmatic caller reaches it with no wrapper at all.\n *\n * @internal\n */\nfunction assertUsableBatchSize(batchSize: number | undefined): void {\n if (batchSize === undefined) return;\n if (!Number.isInteger(batchSize) || batchSize < 1) {\n throw new ConfigurationError(\n `migrateSqliteToLance: batchSize must be a whole number of at least 1; received ${String(batchSize)}.`,\n { code: \"invalid_batch_size\" },\n );\n }\n}\n\n/**\n * Run the migration. Writes Lance to `<cwd>/.theokit/memory/lance-new/`\n * first, validates round-trip, then renames to `lance/` on success.\n * SQLite db is preserved (CLI prompts user to delete).\n *\n * @internal\n */\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: migration is a single transaction (read → write → validate → commit-or-rollback); splitting harms atomicity reasoning.\nexport async function migrateSqliteToLance(opts: MigrateOptions): Promise<MigrateResult> {\n assertUsableBatchSize(opts.batchSize);\n const cwd = opts.cwd;\n // One resolution, reused by every path this migration touches (#463).\n const memoryRoot = resolveMemoryRoot(cwd, { directory: opts.directory });\n const finalPath = lanceStoragePath(memoryRoot);\n const newPath = join(memoryRoot, \"lance-new\");\n // T1.4 (ADR D68): wrap logger so any fact text containing secrets is\n // masked before reaching the destination (console or user-supplied sink).\n // Caller-supplied loggers cannot bypass — by design (D70).\n const rawLog = opts.logger ?? ((m: string) => console.log(m));\n const log = (m: string) => rawLog(redactSecrets(m));\n\n // EC: destination exists → typed error.\n if (existsSync(finalPath)) {\n throw new ConfigurationError(\n `Destination already exists: ${finalPath}. Remove it manually (\\`rm -rf ${finalPath}\\`) and re-run.`,\n { code: \"migration_destination_exists\" },\n );\n }\n if (existsSync(newPath)) {\n // Leftover from a previous failed/interrupted migration. Safe to delete.\n log(`Removing stale ${newPath} from a previous run...`);\n rmSync(newPath, { recursive: true, force: true });\n }\n\n log(`Reading SQLite facts from ${cwd}/.theokit/memory/index.sqlite ...`);\n const sqliteFacts = await readAllSqliteFacts(memoryRoot);\n log(`SQLite has ${sqliteFacts.length} facts.`);\n if (sqliteFacts.length === 0) {\n return {\n countSqlite: 0,\n countLance: 0,\n validated: true,\n sampleComparisons: [],\n lancePath: finalPath,\n committed: false,\n };\n }\n\n // Reuse the embedding runtime configured in the workspace? For v1.2 we\n // require the caller to pass embedding explicitly via env. The migration\n // path is documented as: \"set OPENAI_API_KEY (or similar) before running.\"\n // Here we ship a placeholder embedder that uses a deterministic\n // content-hash — sufficient for migration validation; consumers should\n // re-embed on first real query after migration if needed.\n const placeholderEmbedding = {\n id: \"migration-placeholder\",\n model: \"deterministic-hash\",\n dimension: 8,\n embed: async (texts: ReadonlyArray<string>) => {\n // Simple deterministic hash → 8-dim vector (migration validation only).\n return texts.map((t) => {\n const arr = new Array(8).fill(0);\n for (let i = 0; i < t.length; i += 1) {\n arr[i % 8] += t.charCodeAt(i);\n }\n return arr.map((x) => x / 1000);\n });\n },\n stats: () => ({ cacheHits: 0, cacheMisses: 0, httpCalls: 0, retries: 0 }),\n };\n\n mkdirSync(newPath, { recursive: true });\n log(`Writing Lance index to ${newPath} ...`);\n\n const lance = await LanceIndex.open({\n cwd,\n embedding: placeholderEmbedding,\n storagePath: newPath,\n });\n\n const batchSize = opts.batchSize ?? 100;\n for (let i = 0; i < sqliteFacts.length; i += batchSize) {\n const batch = sqliteFacts.slice(i, i + batchSize).map((f) => ({\n id: f.id,\n text: f.text,\n source: f.source,\n namespace: f.namespace ?? \"default\",\n scope: f.scope ?? \"agent\",\n user_id: f.user_id ?? \"default\",\n timestamp: 0,\n }));\n await lance.addFacts(batch);\n log(` Migrated ${Math.min(i + batchSize, sqliteFacts.length)}/${sqliteFacts.length}`);\n }\n\n // Validation: count + sample compare (10 random) with NFC normalization.\n const lanceCount = await lance.countFacts(\"default\");\n log(`Lance has ${lanceCount} facts (expected ${sqliteFacts.length}).`);\n\n const sampleSize = Math.min(10, sqliteFacts.length);\n const sampleComparisons: { id: string; match: boolean }[] = [];\n for (let i = 0; i < sampleSize; i += 1) {\n const sqliteFact = sqliteFacts[Math.floor((i * sqliteFacts.length) / sampleSize)];\n if (sqliteFact === undefined) continue;\n // Search Lance for this fact's text — best-effort round-trip.\n const hits = await lance.search(sqliteFact.text, {\n namespace: sqliteFact.namespace ?? \"default\",\n limit: 1,\n });\n // EC-3: compare with NFC normalization.\n const hit = hits[0];\n const match = hit !== undefined && nfcEqual(hit.text, sqliteFact.text);\n sampleComparisons.push({ id: sqliteFact.id, match });\n }\n\n const allMatch = sampleComparisons.every((c) => c.match);\n const countMatch = lanceCount === sqliteFacts.length;\n const validated = allMatch && countMatch;\n\n await lance.close();\n\n if (opts.dryRun === true) {\n log(\"Dry-run mode — discarding Lance staging dir.\");\n rmSync(newPath, { recursive: true, force: true });\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated,\n sampleComparisons,\n lancePath: finalPath,\n committed: false,\n };\n }\n\n if (!validated) {\n log(\"Validation FAILED — leaving SQLite intact, removing Lance staging dir.\");\n rmSync(newPath, { recursive: true, force: true });\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated: false,\n sampleComparisons,\n lancePath: finalPath,\n committed: false,\n };\n }\n\n // Atomic commit via rename.\n renameSync(newPath, finalPath);\n log(`Migration committed to ${finalPath}.`);\n return {\n countSqlite: sqliteFacts.length,\n countLance: lanceCount,\n validated: true,\n sampleComparisons,\n lancePath: finalPath,\n committed: true,\n };\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
3
|
+
var chunkEQ5IDL6W_cjs = require('./chunk-EQ5IDL6W.cjs');
|
|
4
|
+
var chunk3WKWBYPO_cjs = require('./chunk-3WKWBYPO.cjs');
|
|
5
|
+
var chunkPOBZXULR_cjs = require('./chunk-POBZXULR.cjs');
|
|
6
|
+
var chunkG7XBVM3T_cjs = require('./chunk-G7XBVM3T.cjs');
|
|
7
7
|
var fs = require('fs');
|
|
8
8
|
var promises = require('fs/promises');
|
|
9
9
|
var path = require('path');
|
|
@@ -15,7 +15,7 @@ async function acquireShared(path) {
|
|
|
15
15
|
existing.refs++;
|
|
16
16
|
return createProxy(path);
|
|
17
17
|
}
|
|
18
|
-
const lease = await
|
|
18
|
+
const lease = await chunkEQ5IDL6W_cjs.acquireSessionWriter(path);
|
|
19
19
|
sharedLeases.set(path, { lease, refs: 1 });
|
|
20
20
|
return createProxy(path);
|
|
21
21
|
}
|
|
@@ -67,21 +67,21 @@ var FsSessionStore = class {
|
|
|
67
67
|
#pathFor(agentId) {
|
|
68
68
|
const memoized = this.#paths.get(agentId);
|
|
69
69
|
if (memoized !== void 0) return memoized;
|
|
70
|
-
const canonical =
|
|
71
|
-
const legacy =
|
|
70
|
+
const canonical = chunk3WKWBYPO_cjs.transcriptPath(this.#baseDir, this.#cwd, agentId);
|
|
71
|
+
const legacy = chunk3WKWBYPO_cjs.legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);
|
|
72
72
|
const resolved = legacy !== canonical && fs.existsSync(legacy) ? legacy : canonical;
|
|
73
73
|
this.#paths.set(agentId, resolved);
|
|
74
74
|
return resolved;
|
|
75
75
|
}
|
|
76
76
|
async readRecords(agentId) {
|
|
77
|
-
return
|
|
77
|
+
return chunk3WKWBYPO_cjs.readTranscript(this.#pathFor(agentId));
|
|
78
78
|
}
|
|
79
79
|
async appendRecords(agentId, records) {
|
|
80
80
|
if (records.length === 0) return;
|
|
81
81
|
const path$1 = this.#pathFor(agentId);
|
|
82
82
|
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
83
|
-
await
|
|
84
|
-
for (const record of records)
|
|
83
|
+
await chunkG7XBVM3T_cjs.withFileLock(path$1, async () => {
|
|
84
|
+
for (const record of records) chunkPOBZXULR_cjs.appendJsonl(path$1, record);
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
@@ -132,5 +132,5 @@ var FsSessionStore = class {
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
exports.FsSessionStore = FsSessionStore;
|
|
135
|
-
//# sourceMappingURL=chunk-
|
|
136
|
-
//# sourceMappingURL=chunk-
|
|
135
|
+
//# sourceMappingURL=chunk-6OCJWGVD.cjs.map
|
|
136
|
+
//# sourceMappingURL=chunk-6OCJWGVD.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/persistence/fs-session-store.ts"],"names":["acquireSessionWriter","transcriptPath","legacyTranscriptPath","existsSync","readTranscript","path","mkdir","dirname","withFileLock","appendJsonl"],"mappings":"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","file":"chunk-NQVWSSX2.cjs","sourcesContent":["/**\n * SE41 — `FsSessionStore`, the DEFAULT reference implementation of the pluggable\n * {@link SessionStore} seam. It reads and append-writes the native Claude-shaped\n * `.jsonl` transcript at `<baseDir>/projects/<encoded-cwd>/<agentId>.jsonl` — the\n * exact on-disk format SE40 introduced (the file the Claude Code CLI can\n * `--continue`). Omitting `local.sessionStore` resolves to this store, so the\n * default persistence path is byte-identical to SE40 behavior.\n *\n * `readRecords` is `readTranscript(transcriptPath(...))` (a missing session →\n * `[]`, not an error — a fresh agent has no history). `appendRecords` is a TRUE\n * append: it reads the prior records, concatenates the new-turn delta, and\n * rewrites the whole line set atomically under the SE40 cross-process file lock\n * (`writeTranscript` never shrinks — the native format is an append-only\n * `parentUuid` DAG). The parent dir is created BEFORE acquiring the lock because\n * the lock's companion `<path>.lock` file needs an existing parent dir (the SE40\n * `mkdir(dirname)`-before-lock fix).\n *\n * @internal\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { withFileLock } from \"./file-lock.js\";\nimport { appendJsonl } from \"./jsonl.js\";\nimport {\n legacyTranscriptPath,\n readTranscript,\n type SessionRecord,\n transcriptPath,\n} from \"./session-transcript.js\";\nimport { acquireSessionWriter, type SessionWriterLease } from \"./session-writer.js\";\n\n/** Options identifying the on-disk transcript location for the FS default store. */\nexport interface FsSessionStoreOptions {\n /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */\n baseDir: string;\n /** The workspace cwd whose encoded form is the transcript project dir. */\n cwd: string;\n}\n\n/**\n * Reference-counted leases, shared per path.\n *\n * `acquireSessionWriter` is STRICT on purpose: two concurrent acquisitions of the same path, and\n * exactly one wins — \"a lease that let both through would be decorative\" (the M81 test says exactly\n * that, and it is right). That is the primitive's contract, and it does not change.\n *\n * But within ONE process it is normal to have more than one store over the same session: the golden\n * compaction tests and the concurrent-send tests do exactly that, and they record real runtime\n * behavior. Applying the raw primitive there would turn a legitimate pattern into a\n * `SessionBusyError`.\n *\n * The reconciliation belongs here, in the primitive's consumer: the process takes ONE lease per path\n * and counts how many stores use it. The last to release actually frees it. Cross-process stays\n * strict — which is the problem M81 states (\"`exec resume --last` can write into the TUI's live\n * session\").\n */\nconst sharedLeases = new Map<string, { lease: SessionWriterLease; refs: number }>();\n\nasync function acquireShared(path: string): Promise<SessionWriterLease> {\n const existing = sharedLeases.get(path);\n if (existing !== undefined) {\n existing.refs++;\n return createProxy(path);\n }\n const lease = await acquireSessionWriter(path);\n sharedLeases.set(path, { lease, refs: 1 });\n return createProxy(path);\n}\n\n/** A handle that decrements the count; the last to release frees the real lease. */\nfunction createProxy(path: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath: path,\n // The proxy forwards the renewal to the ONE real lease this path shares. Renewing through any\n // holder is correct and is the point: the record must say \"someone in this process is still\n // writing\", and the refcount already guarantees they are all the same owner.\n renew: (): void => {\n if (released) return;\n sharedLeases.get(path)?.lease.renew();\n },\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n const entry = sharedLeases.get(path);\n if (entry === undefined) return;\n entry.refs--;\n if (entry.refs > 0) return;\n sharedLeases.delete(path);\n await entry.lease.release();\n },\n };\n}\n\n/**\n * The default `SessionStore` — reads/append-writes the native `.jsonl` transcript.\n */\nexport class FsSessionStore implements SessionStore {\n readonly #baseDir: string;\n readonly #cwd: string;\n /** One lease per `agentId` — a store serves more than one session over the process lifetime. */\n readonly #leases = new Map<string, SessionWriterLease>();\n /** Memoized per agent so read, append and lease can never disagree about which file is the session. */\n readonly #paths = new Map<string, string>();\n\n constructor(options: FsSessionStoreOptions) {\n this.#baseDir = options.baseDir;\n this.#cwd = options.cwd;\n }\n\n /**\n * The file that IS this agent's session.\n *\n * #400 made transcript filenames UUIDs so `claude --continue` can find them. A transcript written\n * before that lives under the old name, and switching to the new one would not lose the file but\n * would abandon it: the agent would start a second, empty history beside its real one. So an\n * existing legacy file wins, and only a session with no file yet gets the new name.\n *\n * Resolved ONCE per agent. Re-deciding per call would let `acquire` lock one path while\n * `appendRecords` wrote another — the lease would be guarding a file nobody was writing.\n */\n #pathFor(agentId: string): string {\n const memoized = this.#paths.get(agentId);\n if (memoized !== undefined) return memoized;\n const canonical = transcriptPath(this.#baseDir, this.#cwd, agentId);\n const legacy = legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);\n const resolved = legacy !== canonical && existsSync(legacy) ? legacy : canonical;\n this.#paths.set(agentId, resolved);\n return resolved;\n }\n\n async readRecords(agentId: string): Promise<SessionRecord[]> {\n return readTranscript(this.#pathFor(agentId));\n }\n\n async appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void> {\n // Empty delta → nothing to persist (avoids an unnecessary lock + rewrite).\n if (records.length === 0) return;\n const path = this.#pathFor(agentId);\n // mkdir BEFORE the lock: withFileLock's companion `<path>.lock` needs the parent dir.\n await mkdir(dirname(path), { recursive: true });\n\n await withFileLock(path, async () => {\n // M93 — appends the DELTA instead of rewriting the whole file.\n //\n // Before: `readTranscript` + `writeTranscript` of everything, per turn. O(n) of I/O **and** of\n // parsing on every turn, O(n^2) per session — the consumer note in\n // `agents/lib/session/backtrack.ts` records 1.4 MB / 3000 lines over 200 turns.\n //\n // Correct because the format **is already append-only**: the `parentUuid` DAG does not depend\n // on line order, and every record carries its own parent. `appendJsonl` **already existed in\n // the package** and had a single caller (`eval/runner.ts`) — the primitive was there, it was\n // the store that ignored it (rung 4).\n //\n // `withFileLock` stays — but the earlier claim that \"it is what serializes two concurrent\n // `appendRecords`\" was too strong, and M93's adversarial review measured it: removing it fails\n // no test. The reason is the paragraph above — the `parentUuid` DAG does not depend on line\n // order, so two interleaved batches reconstruct identically. The work the lock was doing\n // (protecting a read-modify-write) disappeared along with the rewrite.\n //\n // What it still covers is the TOCTOU window in `needsLineBreakBefore` (read the last byte,\n // then write): without it, two processes can both conclude \"a \\n is missing\" and produce a\n // blank line — which the reader discards, i.e. benign. It stays as a **declared, not\n // mechanized** defense (the discipline in `error-handling.md` § 4: enumerate the residue\n // rather than let a missing test pass for coverage).\n //\n // `writeTranscript` still exists for **compaction**, the one operation that legitimately\n // rewrites the file.\n for (const record of records) appendJsonl(path, record);\n });\n }\n\n /**\n * Takes the session's writer lease. Throws `SessionBusyError` when another process holds it.\n *\n * **Explicit, and NOT inside `appendRecords`** — M95's adversarial review measured why that\n * matters: the `SessionStore` contract states that \"an `appendRecords` rejection is logged to\n * stderr, NOT thrown to the caller (best-effort write)\". Acquiring there made the\n * `SessionBusyError` get **swallowed**, and the result was worse than the original problem:\n * instead of two writers interleaving lines, the loser **lost the turn silently** — nothing on\n * disk, a stderr warning invisible under the TUI, and no way for the caller to react.\n *\n * At init the error reaches someone who can act: `exec` forks to a new id, which is exactly what\n * the error message itself prescribes. It is the difference between failing where a decision is\n * possible and failing where only loss is.\n */\n async acquire(agentId: string): Promise<void> {\n if (this.#leases.has(agentId)) return;\n const path = this.#pathFor(agentId);\n await mkdir(dirname(path), { recursive: true });\n this.#leases.set(agentId, await acquireShared(path));\n }\n\n /**\n * Releases ONE agent's lease.\n *\n * It exists because `dispose()` releases **every** lease the store holds, and a store injected by\n * the consumer may serve several agents: an init that fails for agent B must not free agent A's\n * lease, which is still live and writing.\n */\n async release(agentId: string): Promise<void> {\n const lease = this.#leases.get(agentId);\n if (lease === undefined) return;\n this.#leases.delete(agentId);\n await lease.release();\n }\n\n /**\n * Releases every lease this store holds.\n *\n * Without it the `.writer.lock` outlives the process and the next open would have to wait out the\n * heartbeat window — recoverable, but 30 s of waiting after a CLEAN shutdown would be an avoidable\n * defect. Idempotent: calling it twice is not an error.\n */\n async dispose(): Promise<void> {\n const leases = [...this.#leases.values()];\n this.#leases.clear();\n for (const lease of leases) await lease.release();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/persistence/fs-session-store.ts"],"names":["acquireSessionWriter","transcriptPath","legacyTranscriptPath","existsSync","readTranscript","path","mkdir","dirname","withFileLock","appendJsonl"],"mappings":"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","file":"chunk-6OCJWGVD.cjs","sourcesContent":["/**\n * SE41 — `FsSessionStore`, the DEFAULT reference implementation of the pluggable\n * {@link SessionStore} seam. It reads and append-writes the native Claude-shaped\n * `.jsonl` transcript at `<baseDir>/projects/<encoded-cwd>/<agentId>.jsonl` — the\n * exact on-disk format SE40 introduced (the file the Claude Code CLI can\n * `--continue`). Omitting `local.sessionStore` resolves to this store, so the\n * default persistence path is byte-identical to SE40 behavior.\n *\n * `readRecords` is `readTranscript(transcriptPath(...))` (a missing session →\n * `[]`, not an error — a fresh agent has no history). `appendRecords` is a TRUE\n * append: it reads the prior records, concatenates the new-turn delta, and\n * rewrites the whole line set atomically under the SE40 cross-process file lock\n * (`writeTranscript` never shrinks — the native format is an append-only\n * `parentUuid` DAG). The parent dir is created BEFORE acquiring the lock because\n * the lock's companion `<path>.lock` file needs an existing parent dir (the SE40\n * `mkdir(dirname)`-before-lock fix).\n *\n * @internal\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { withFileLock } from \"./file-lock.js\";\nimport { appendJsonl } from \"./jsonl.js\";\nimport {\n legacyTranscriptPath,\n readTranscript,\n type SessionRecord,\n transcriptPath,\n} from \"./session-transcript.js\";\nimport { acquireSessionWriter, type SessionWriterLease } from \"./session-writer.js\";\n\n/** Options identifying the on-disk transcript location for the FS default store. */\nexport interface FsSessionStoreOptions {\n /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */\n baseDir: string;\n /** The workspace cwd whose encoded form is the transcript project dir. */\n cwd: string;\n}\n\n/**\n * Reference-counted leases, shared per path.\n *\n * `acquireSessionWriter` is STRICT on purpose: two concurrent acquisitions of the same path, and\n * exactly one wins — \"a lease that let both through would be decorative\" (the M81 test says exactly\n * that, and it is right). That is the primitive's contract, and it does not change.\n *\n * But within ONE process it is normal to have more than one store over the same session: the golden\n * compaction tests and the concurrent-send tests do exactly that, and they record real runtime\n * behavior. Applying the raw primitive there would turn a legitimate pattern into a\n * `SessionBusyError`.\n *\n * The reconciliation belongs here, in the primitive's consumer: the process takes ONE lease per path\n * and counts how many stores use it. The last to release actually frees it. Cross-process stays\n * strict — which is the problem M81 states (\"`exec resume --last` can write into the TUI's live\n * session\").\n */\nconst sharedLeases = new Map<string, { lease: SessionWriterLease; refs: number }>();\n\nasync function acquireShared(path: string): Promise<SessionWriterLease> {\n const existing = sharedLeases.get(path);\n if (existing !== undefined) {\n existing.refs++;\n return createProxy(path);\n }\n const lease = await acquireSessionWriter(path);\n sharedLeases.set(path, { lease, refs: 1 });\n return createProxy(path);\n}\n\n/** A handle that decrements the count; the last to release frees the real lease. */\nfunction createProxy(path: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath: path,\n // The proxy forwards the renewal to the ONE real lease this path shares. Renewing through any\n // holder is correct and is the point: the record must say \"someone in this process is still\n // writing\", and the refcount already guarantees they are all the same owner.\n renew: (): void => {\n if (released) return;\n sharedLeases.get(path)?.lease.renew();\n },\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n const entry = sharedLeases.get(path);\n if (entry === undefined) return;\n entry.refs--;\n if (entry.refs > 0) return;\n sharedLeases.delete(path);\n await entry.lease.release();\n },\n };\n}\n\n/**\n * The default `SessionStore` — reads/append-writes the native `.jsonl` transcript.\n */\nexport class FsSessionStore implements SessionStore {\n readonly #baseDir: string;\n readonly #cwd: string;\n /** One lease per `agentId` — a store serves more than one session over the process lifetime. */\n readonly #leases = new Map<string, SessionWriterLease>();\n /** Memoized per agent so read, append and lease can never disagree about which file is the session. */\n readonly #paths = new Map<string, string>();\n\n constructor(options: FsSessionStoreOptions) {\n this.#baseDir = options.baseDir;\n this.#cwd = options.cwd;\n }\n\n /**\n * The file that IS this agent's session.\n *\n * #400 made transcript filenames UUIDs so `claude --continue` can find them. A transcript written\n * before that lives under the old name, and switching to the new one would not lose the file but\n * would abandon it: the agent would start a second, empty history beside its real one. So an\n * existing legacy file wins, and only a session with no file yet gets the new name.\n *\n * Resolved ONCE per agent. Re-deciding per call would let `acquire` lock one path while\n * `appendRecords` wrote another — the lease would be guarding a file nobody was writing.\n */\n #pathFor(agentId: string): string {\n const memoized = this.#paths.get(agentId);\n if (memoized !== undefined) return memoized;\n const canonical = transcriptPath(this.#baseDir, this.#cwd, agentId);\n const legacy = legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);\n const resolved = legacy !== canonical && existsSync(legacy) ? legacy : canonical;\n this.#paths.set(agentId, resolved);\n return resolved;\n }\n\n async readRecords(agentId: string): Promise<SessionRecord[]> {\n return readTranscript(this.#pathFor(agentId));\n }\n\n async appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void> {\n // Empty delta → nothing to persist (avoids an unnecessary lock + rewrite).\n if (records.length === 0) return;\n const path = this.#pathFor(agentId);\n // mkdir BEFORE the lock: withFileLock's companion `<path>.lock` needs the parent dir.\n await mkdir(dirname(path), { recursive: true });\n\n await withFileLock(path, async () => {\n // M93 — appends the DELTA instead of rewriting the whole file.\n //\n // Before: `readTranscript` + `writeTranscript` of everything, per turn. O(n) of I/O **and** of\n // parsing on every turn, O(n^2) per session — the consumer note in\n // `agents/lib/session/backtrack.ts` records 1.4 MB / 3000 lines over 200 turns.\n //\n // Correct because the format **is already append-only**: the `parentUuid` DAG does not depend\n // on line order, and every record carries its own parent. `appendJsonl` **already existed in\n // the package** and had a single caller (`eval/runner.ts`) — the primitive was there, it was\n // the store that ignored it (rung 4).\n //\n // `withFileLock` stays — but the earlier claim that \"it is what serializes two concurrent\n // `appendRecords`\" was too strong, and M93's adversarial review measured it: removing it fails\n // no test. The reason is the paragraph above — the `parentUuid` DAG does not depend on line\n // order, so two interleaved batches reconstruct identically. The work the lock was doing\n // (protecting a read-modify-write) disappeared along with the rewrite.\n //\n // What it still covers is the TOCTOU window in `needsLineBreakBefore` (read the last byte,\n // then write): without it, two processes can both conclude \"a \\n is missing\" and produce a\n // blank line — which the reader discards, i.e. benign. It stays as a **declared, not\n // mechanized** defense (the discipline in `error-handling.md` § 4: enumerate the residue\n // rather than let a missing test pass for coverage).\n //\n // `writeTranscript` still exists for **compaction**, the one operation that legitimately\n // rewrites the file.\n for (const record of records) appendJsonl(path, record);\n });\n }\n\n /**\n * Takes the session's writer lease. Throws `SessionBusyError` when another process holds it.\n *\n * **Explicit, and NOT inside `appendRecords`** — M95's adversarial review measured why that\n * matters: the `SessionStore` contract states that \"an `appendRecords` rejection is logged to\n * stderr, NOT thrown to the caller (best-effort write)\". Acquiring there made the\n * `SessionBusyError` get **swallowed**, and the result was worse than the original problem:\n * instead of two writers interleaving lines, the loser **lost the turn silently** — nothing on\n * disk, a stderr warning invisible under the TUI, and no way for the caller to react.\n *\n * At init the error reaches someone who can act: `exec` forks to a new id, which is exactly what\n * the error message itself prescribes. It is the difference between failing where a decision is\n * possible and failing where only loss is.\n */\n async acquire(agentId: string): Promise<void> {\n if (this.#leases.has(agentId)) return;\n const path = this.#pathFor(agentId);\n await mkdir(dirname(path), { recursive: true });\n this.#leases.set(agentId, await acquireShared(path));\n }\n\n /**\n * Releases ONE agent's lease.\n *\n * It exists because `dispose()` releases **every** lease the store holds, and a store injected by\n * the consumer may serve several agents: an init that fails for agent B must not free agent A's\n * lease, which is still live and writing.\n */\n async release(agentId: string): Promise<void> {\n const lease = this.#leases.get(agentId);\n if (lease === undefined) return;\n this.#leases.delete(agentId);\n await lease.release();\n }\n\n /**\n * Releases every lease this store holds.\n *\n * Without it the `.writer.lock` outlives the process and the next open would have to wait out the\n * heartbeat window — recoverable, but 30 s of waiting after a CLEAN shutdown would be an avoidable\n * defect. Idempotent: calling it twice is not an error.\n */\n async dispose(): Promise<void> {\n const leases = [...this.#leases.values()];\n this.#leases.clear();\n for (const lease of leases) await lease.release();\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
|
|
4
4
|
var promises = require('fs/promises');
|
|
5
5
|
|
|
6
6
|
async function readWorkspaceDir(root, errorCode, describe) {
|
|
@@ -9,7 +9,7 @@ async function readWorkspaceDir(root, errorCode, describe) {
|
|
|
9
9
|
} catch (cause) {
|
|
10
10
|
const err = cause;
|
|
11
11
|
if (err.code === "ENOENT") return [];
|
|
12
|
-
throw new
|
|
12
|
+
throw new chunkCEGU5AVD_cjs.ConfigurationError(`Failed to read ${describe}: ${root}`, {
|
|
13
13
|
code: errorCode,
|
|
14
14
|
cause
|
|
15
15
|
});
|
|
@@ -65,5 +65,5 @@ function coerce(raw) {
|
|
|
65
65
|
|
|
66
66
|
exports.parseSimpleYaml = parseSimpleYaml;
|
|
67
67
|
exports.readWorkspaceDir = readWorkspaceDir;
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
68
|
+
//# sourceMappingURL=chunk-6XQLTQB6.cjs.map
|
|
69
|
+
//# sourceMappingURL=chunk-6XQLTQB6.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/config/workspace-dir.ts","../src/internal/runtime/context/yaml-frontmatter.ts"],"names":["readdir","ConfigurationError"],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/config/workspace-dir.ts","../src/internal/runtime/context/yaml-frontmatter.ts"],"names":["readdir","ConfigurationError"],"mappings":"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","file":"chunk-6XQLTQB6.cjs","sourcesContent":["import { readdir } from \"node:fs/promises\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\n\n/**\n * Entry returned by `readWorkspaceDir`. Mirrors the subset of\n * `fs.Dirent` the file-based loaders use.\n */\nexport interface WorkspaceDirEntry {\n name: string;\n isDirectory(): boolean;\n isFile(): boolean;\n}\n\n/**\n * Read a workspace subdirectory and return its entries. When the directory\n * does not exist (`ENOENT`), returns an empty array — the file-based loaders\n * (skills, plugins, agents) treat a missing directory as \"no entries\"\n * rather than an error.\n *\n * Any other I/O failure is wrapped as `ConfigurationError` so callers can\n * surface a stable error code.\n *\n * @internal\n */\nexport async function readWorkspaceDir(\n root: string,\n errorCode: string,\n describe: string,\n): Promise<WorkspaceDirEntry[]> {\n try {\n return (await readdir(root, { withFileTypes: true })) as WorkspaceDirEntry[];\n } catch (cause) {\n const err = cause as NodeJS.ErrnoException;\n if (err.code === \"ENOENT\") return [];\n throw new ConfigurationError(`Failed to read ${describe}: ${root}`, {\n code: errorCode,\n cause,\n });\n }\n}\n","/**\n * Tiny YAML-frontmatter parser shared by the file-based loaders (skills,\n * subagents, hooks, context, plugins). Supports four scalar shapes:\n *\n * key: bar → \"bar\" (string)\n * key: 42 → 42 (number)\n * key: true → true (boolean)\n * key: [a, b, c] → [\"a\",\"b\",\"c\"](string[])\n * key: → undefined (caller's Zod default kicks in)\n * - a → [\"a\",\"b\"] (block list, when `- ` items follow a bare key)\n * - b\n *\n * Limitations (intentional — keep parser tiny, no dep):\n * - No nested objects (use flat keys like `providerId` not `provider.id`).\n * - No quoted strings — `match: \"1\"` becomes the literal 3-char string `\"1\"`.\n * - Inline list values cannot contain a literal comma inside an element; the\n * `tags: [a,b, c]` splitter is greedy on `,`. Use the block form above or\n * reword if you need this — and note that the block form was, for a while,\n * what this line recommended and the parser could not read.\n *\n * @internal\n */\n\nexport type FrontmatterValue = string | number | boolean | string[];\n\nexport function parseSimpleYaml(text: string): Record<string, FrontmatterValue | undefined> {\n const fields: Record<string, FrontmatterValue | undefined> = {};\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i += 1) {\n const entry = splitEntry(lines[i] ?? \"\");\n if (entry === undefined) continue;\n const { key, raw } = entry;\n\n // `key:` with nothing after it may open a block list. Collect first, then decide: items found →\n // a list; none → `undefined`, which is the long-standing meaning of a bare key and what a\n // caller's Zod default relies on.\n const block = raw.length === 0 ? collectBlockList(lines, i + 1) : undefined;\n if (block !== undefined && block.items.length > 0) {\n fields[key] = block.items;\n // `end` is the first line that is NOT part of this list, and the loop's own `i += 1` steps\n // past it — so stop one short, or the key after a block list is swallowed.\n i = block.end - 1;\n continue;\n }\n\n fields[key] = coerce(raw);\n }\n return fields;\n}\n\n/**\n * Split one line into its key and its raw value, or report that it carries neither.\n *\n * A line with no `:` is not an entry (a `- item` continuation is the case that matters here), and\n * neither is one whose key is blank. Both were `continue`s inside the loop; they are the same\n * question — \"is this line an entry?\" — and asking it in one place is what keeps the loop under the\n * repository's cognitive-complexity limit.\n */\nfunction splitEntry(line: string): { key: string; raw: string } | undefined {\n const colonIndex = line.indexOf(\":\");\n if (colonIndex === -1) return undefined;\n const key = line.slice(0, colonIndex).trim();\n if (key.length === 0) return undefined;\n return { key, raw: line.slice(colonIndex + 1).trim() };\n}\n\n/**\n * Read the `- item` lines that follow a bare `key:`, and report where they stop.\n *\n * Line-oriented parsing skipped these outright — a `- item` has no colon — so the key coerced to\n * `undefined` and the values went nowhere. A reader saw the key on disk and found no behaviour,\n * with nothing to grep for. The docblock above made it sharper by advising \"use multi-line lists\"\n * for a limitation of the inline form; multi-line was the one shape this parser could not read.\n *\n * Extracted rather than inlined because inlining it put `parseSimpleYaml` at a cognitive complexity\n * of 24 against a limit of 10 — measured, not assumed: the pre-change function raised no such\n * diagnostic on the same file path.\n *\n * Blank and `#` lines do not end a list; anything else does. `end` is the index of that first\n * non-item line, so the caller resumes there rather than re-reading what it consumed.\n */\nfunction collectBlockList(lines: string[], start: number): { items: string[]; end: number } {\n const items: string[] = [];\n let j = start;\n for (; j < lines.length; j += 1) {\n const next = (lines[j] ?? \"\").trim();\n if (next === \"\" || next.startsWith(\"#\")) continue;\n if (!next.startsWith(\"- \")) break;\n items.push(next.slice(2).trim());\n }\n return { items, end: j };\n}\n\nfunction coerce(raw: string): FrontmatterValue | undefined {\n // EC-3: empty value → undefined so Zod `.optional().default(...)` applies.\n if (raw.length === 0) return undefined;\n if (raw.startsWith(\"[\") && raw.endsWith(\"]\")) {\n return raw\n .slice(1, -1)\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n if (raw === \"true\" || raw === \"false\") return raw === \"true\";\n const n = Number(raw);\n if (Number.isFinite(n) && raw === String(n)) return n;\n return raw;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
|
|
4
4
|
|
|
5
5
|
// src/internal/retry/with-retry.ts
|
|
6
6
|
function defaultSleep(ms, signal) {
|
|
@@ -23,14 +23,14 @@ function defaultSleep(ms, signal) {
|
|
|
23
23
|
function resolveRetryOptions(options) {
|
|
24
24
|
const retries = options?.retries ?? 3;
|
|
25
25
|
if (!Number.isInteger(retries) || retries < 0) {
|
|
26
|
-
throw new
|
|
26
|
+
throw new chunkCEGU5AVD_cjs.ConfigurationError(
|
|
27
27
|
`withRetry: retries must be a non-negative integer, got ${retries}`,
|
|
28
28
|
{ code: "invalid_retry_config" }
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
32
32
|
retries,
|
|
33
|
-
isRetryable: options?.isRetryable ??
|
|
33
|
+
isRetryable: options?.isRetryable ?? chunkCEGU5AVD_cjs.isTransientError,
|
|
34
34
|
initialDelayMs: options?.initialDelayMs ?? 100,
|
|
35
35
|
maxDelayMs: options?.maxDelayMs ?? 3e4,
|
|
36
36
|
backoffMultiplier: options?.backoffMultiplier ?? 2,
|
|
@@ -75,5 +75,5 @@ var Retry = class _Retry {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
exports.Retry = Retry;
|
|
78
|
-
//# sourceMappingURL=chunk-
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
78
|
+
//# sourceMappingURL=chunk-7INPXUJI.cjs.map
|
|
79
|
+
//# sourceMappingURL=chunk-7INPXUJI.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/retry/with-retry.ts","../src/retry.ts"],"names":["ConfigurationError","isTransientError"],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/internal/retry/with-retry.ts","../src/retry.ts"],"names":["ConfigurationError","isTransientError"],"mappings":"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","file":"chunk-7INPXUJI.cjs","sourcesContent":["/**\n * Generic retry wrapper (plan m0-foundation-expose-primitives, M0-3).\n *\n * Exponential backoff with full jitter, deterministically testable via an\n * injectable `sleep` and `rng` (no real timers in unit tests, per the repo\n * testing rule). The default `isRetryable` predicate is {@link isTransientError}\n * so SDK errors retry exactly as the SDK classifies them. The workflow-internal\n * `withRetry` (RetryPolicy-coupled) is intentionally separate (ADR-M0-3).\n *\n * @internal — public via `@theokit/sdk/retry`\n */\n\nimport { ConfigurationError, isTransientError } from \"../../errors.js\";\n\n/** Options for {@link withRetry}. All fields optional; sensible defaults applied. */\nexport interface RetryOptions {\n /** Number of retries after the first attempt (total attempts = retries + 1). Default 3. */\n retries?: number;\n /** Predicate deciding whether a thrown error is worth retrying. Default {@link isTransientError}. */\n isRetryable?: (err: unknown) => boolean;\n /** Base backoff in ms for the first retry. Default 100. */\n initialDelayMs?: number;\n /** Upper bound for a single backoff sleep. Default 30_000. */\n maxDelayMs?: number;\n /** Exponential multiplier applied per retry. Default 2. */\n backoffMultiplier?: number;\n /** [0, 1) source for full-jitter. Default `Math.random`. Inject for deterministic tests. */\n rng?: () => number;\n /** Sleep function. Default a `setTimeout`-based abortable sleep. Inject for deterministic tests. */\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n /** Abort signal; once aborted, the abortable default sleep rejects and the loop stops. */\n signal?: AbortSignal;\n}\n\nfunction defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason instanceof Error ? signal.reason : new Error(\"withRetry: aborted\"));\n return;\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n function onAbort(): void {\n clearTimeout(timer);\n reject(signal?.reason instanceof Error ? signal.reason : new Error(\"withRetry: aborted\"));\n }\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\ninterface ResolvedRetry {\n retries: number;\n isRetryable: (err: unknown) => boolean;\n initialDelayMs: number;\n maxDelayMs: number;\n backoffMultiplier: number;\n rng: () => number;\n sleep: (ms: number, signal?: AbortSignal) => Promise<void>;\n signal?: AbortSignal;\n}\n\nfunction resolveRetryOptions(options?: RetryOptions): ResolvedRetry {\n const retries = options?.retries ?? 3;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new ConfigurationError(\n `withRetry: retries must be a non-negative integer, got ${retries}`,\n { code: \"invalid_retry_config\" },\n );\n }\n return {\n retries,\n isRetryable: options?.isRetryable ?? isTransientError,\n initialDelayMs: options?.initialDelayMs ?? 100,\n maxDelayMs: options?.maxDelayMs ?? 30_000,\n backoffMultiplier: options?.backoffMultiplier ?? 2,\n rng: options?.rng ?? Math.random,\n sleep: options?.sleep ?? defaultSleep,\n signal: options?.signal,\n };\n}\n\n/** Full-jitter backoff for the given (0-indexed) retry attempt. */\nfunction backoffMs(cfg: ResolvedRetry, attempt: number): number {\n const ceiling = Math.min(cfg.maxDelayMs, cfg.initialDelayMs * cfg.backoffMultiplier ** attempt);\n return Math.floor(cfg.rng() * ceiling);\n}\n\n/**\n * Run `fn`, retrying transient failures with exponential backoff + full jitter.\n *\n * @returns the resolved value of the first successful `fn()` call\n * @throws the last error when retries are exhausted or the error is not retryable\n *\n * @example\n * const data = await withRetry(() => fetchJson(url)); // retries rate-limit/network\n */\nexport async function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T> {\n const cfg = resolveRetryOptions(options);\n let attempt = 0;\n for (;;) {\n try {\n return await fn();\n } catch (err) {\n if (attempt >= cfg.retries || !cfg.isRetryable(err)) throw err;\n await cfg.sleep(backoffMs(cfg, attempt), cfg.signal);\n attempt += 1;\n }\n }\n}\n","/**\n * Public generic retry primitive (plan m0-foundation-expose-primitives, M0-3).\n *\n * Split into its own top-level module so `tsup` builds a dedicated\n * `@theokit/sdk/retry` sub-path entry, mirroring the `path-safety` pattern.\n * The default retry predicate is `isTransientError`, so retries follow the\n * SDK's own error classification.\n */\n\nimport { type RetryOptions, withRetry } from \"./internal/retry/with-retry.js\";\n\nexport type { RetryOptions };\n\n/**\n * SE36 — replaces `withRetry` (ADR 0015 / ADR-P2).\n *\n * `Retry` is a static NAMESPACE, not a factory: the uniformity mandate spells every public entry\n * point `X.create(...)` so callers learn one verb, and twelve classes in this package follow it.\n *\n * This is the one place the mandate produced a false name, and it is fixed here rather than by\n * unwinding the mandate — consistency across a published API is worth more than the indirection.\n * `withRetry` is an EXECUTOR: it RUNS `fn` and resolves to `fn`'s result, so `create` named a\n * construction that never happens. `run` is the honest spelling and `create` is a deprecated alias,\n * because removing a published member is a major-version decision and the alias is what carries the\n * correction to a caller's editor in the meantime.\n *\n * @public\n */\nexport class Retry {\n private constructor() {}\n\n /** Run `fn`, retrying per `options`. Resolves to `fn`'s result. */\n static run<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T> {\n return withRetry(fn, options);\n }\n\n /**\n * @deprecated Renamed to {@link Retry.run}. This never created anything — it runs `fn` and\n * resolves to its result — and the name said otherwise. Still honoured; removed in the next major.\n */\n static create<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T> {\n return Retry.run(fn, options);\n }\n}\n"]}
|