@theokit/sdk 4.63.0 → 4.63.2
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 +56 -0
- package/dist/a2a/index.cjs +2 -2
- package/dist/a2a/index.js +1 -1
- package/dist/agent-2COLCVOJ.js +57 -0
- package/dist/{agent-536OOESZ.js.map → agent-2COLCVOJ.js.map} +1 -1
- package/dist/agent-EXAN5EYJ.cjs +66 -0
- package/dist/{agent-5KYMG63I.cjs.map → agent-EXAN5EYJ.cjs.map} +1 -1
- package/dist/agent-session-store-6EL5NPA4.cjs +24 -0
- package/dist/{agent-session-store-WAKLOKWK.cjs.map → agent-session-store-6EL5NPA4.cjs.map} +1 -1
- package/dist/agent-session-store-OPGRB4XM.js +7 -0
- package/dist/{agent-session-store-G2BMUZQV.js.map → agent-session-store-OPGRB4XM.js.map} +1 -1
- package/dist/auth/index.cjs +18 -18
- package/dist/auth/index.js +6 -6
- package/dist/{batch-ND32UKZS.cjs → batch-BXYTW2DP.cjs} +27 -27
- package/dist/{batch-ND32UKZS.cjs.map → batch-BXYTW2DP.cjs.map} +1 -1
- package/dist/{batch-2TGJMNCJ.js → batch-L4ZXS46M.js} +10 -10
- package/dist/{batch-2TGJMNCJ.js.map → batch-L4ZXS46M.js.map} +1 -1
- package/dist/{chunk-VF7EWVDG.js → chunk-2GUYQR5P.js} +3 -3
- package/dist/{chunk-VF7EWVDG.js.map → chunk-2GUYQR5P.js.map} +1 -1
- package/dist/{chunk-J2UROOIG.cjs → chunk-2HVF2WJY.cjs} +8 -8
- package/dist/{chunk-J2UROOIG.cjs.map → chunk-2HVF2WJY.cjs.map} +1 -1
- package/dist/{chunk-44MBDIHG.js → chunk-2QDYBS2A.js} +3 -3
- package/dist/{chunk-44MBDIHG.js.map → chunk-2QDYBS2A.js.map} +1 -1
- package/dist/{chunk-NGESVVJN.cjs → chunk-3FFAEAUJ.cjs} +17 -17
- package/dist/{chunk-NGESVVJN.cjs.map → chunk-3FFAEAUJ.cjs.map} +1 -1
- package/dist/{chunk-GWC3HADL.cjs → chunk-3NSS2OIZ.cjs} +4 -4
- package/dist/{chunk-GWC3HADL.cjs.map → chunk-3NSS2OIZ.cjs.map} +1 -1
- package/dist/{chunk-CV7XMBHP.js → chunk-3O42QOMJ.js} +3 -3
- package/dist/{chunk-CV7XMBHP.js.map → chunk-3O42QOMJ.js.map} +1 -1
- package/dist/{chunk-IWBGCBR6.cjs → chunk-4CLOXP6H.cjs} +4 -4
- package/dist/{chunk-IWBGCBR6.cjs.map → chunk-4CLOXP6H.cjs.map} +1 -1
- package/dist/{chunk-YEXA3PGR.js → chunk-4FFBAUFS.js} +5 -5
- package/dist/{chunk-YEXA3PGR.js.map → chunk-4FFBAUFS.js.map} +1 -1
- package/dist/{chunk-HY57ULY2.cjs → chunk-4PYZ5AX3.cjs} +5 -5
- package/dist/{chunk-HY57ULY2.cjs.map → chunk-4PYZ5AX3.cjs.map} +1 -1
- package/dist/{chunk-OC4NTGMN.js → chunk-4QZSA2HJ.js} +3 -3
- package/dist/{chunk-OC4NTGMN.js.map → chunk-4QZSA2HJ.js.map} +1 -1
- package/dist/{chunk-K3FW2XZD.cjs → chunk-4ZHTIJNE.cjs} +9 -9
- package/dist/{chunk-K3FW2XZD.cjs.map → chunk-4ZHTIJNE.cjs.map} +1 -1
- package/dist/{chunk-BUIK7GUA.cjs → chunk-5HEG4CQH.cjs} +8 -8
- package/dist/{chunk-BUIK7GUA.cjs.map → chunk-5HEG4CQH.cjs.map} +1 -1
- package/dist/{chunk-NXH4GPAQ.cjs → chunk-5VB22SYX.cjs} +7 -7
- package/dist/{chunk-NXH4GPAQ.cjs.map → chunk-5VB22SYX.cjs.map} +1 -1
- package/dist/{chunk-5USCYPPI.cjs → chunk-6HKGLTJI.cjs} +8 -8
- package/dist/{chunk-5USCYPPI.cjs.map → chunk-6HKGLTJI.cjs.map} +1 -1
- package/dist/{chunk-EHJEZAOO.cjs → chunk-6RKUO7AW.cjs} +32 -32
- package/dist/{chunk-EHJEZAOO.cjs.map → chunk-6RKUO7AW.cjs.map} +1 -1
- package/dist/{chunk-5JLFFPCH.js → chunk-7YGCPMGB.js} +4 -4
- package/dist/{chunk-5JLFFPCH.js.map → chunk-7YGCPMGB.js.map} +1 -1
- package/dist/{chunk-SUKXXLWD.js → chunk-A46SWMIF.js} +6 -6
- package/dist/{chunk-SUKXXLWD.js.map → chunk-A46SWMIF.js.map} +1 -1
- package/dist/{chunk-F7AQV62G.js → chunk-A4JVILQU.js} +5 -5
- package/dist/{chunk-F7AQV62G.js.map → chunk-A4JVILQU.js.map} +1 -1
- package/dist/{chunk-44JAAH4X.js → chunk-B3IMEBOA.js} +3 -3
- package/dist/{chunk-44JAAH4X.js.map → chunk-B3IMEBOA.js.map} +1 -1
- package/dist/{chunk-QDED6YO6.cjs → chunk-B4EOJWOY.cjs} +5 -5
- package/dist/{chunk-QDED6YO6.cjs.map → chunk-B4EOJWOY.cjs.map} +1 -1
- package/dist/{chunk-YJDH3CKG.js → chunk-BP46RJ5X.js} +5 -5
- package/dist/{chunk-YJDH3CKG.js.map → chunk-BP46RJ5X.js.map} +1 -1
- package/dist/{chunk-QRSUA2CV.js → chunk-BU4KFWXF.js} +3 -3
- package/dist/{chunk-QRSUA2CV.js.map → chunk-BU4KFWXF.js.map} +1 -1
- package/dist/{chunk-AQLGBKNT.js → chunk-BZRFXJHA.js} +3 -3
- package/dist/{chunk-AQLGBKNT.js.map → chunk-BZRFXJHA.js.map} +1 -1
- package/dist/{chunk-UQGQFBRL.js → chunk-C3NT3PVE.js} +3 -3
- package/dist/{chunk-UQGQFBRL.js.map → chunk-C3NT3PVE.js.map} +1 -1
- package/dist/{chunk-UC3HT2S4.js → chunk-C72AZW7J.js} +3 -3
- package/dist/{chunk-UC3HT2S4.js.map → chunk-C72AZW7J.js.map} +1 -1
- package/dist/{chunk-WNTAPVU5.cjs → chunk-CQBXOJ6U.cjs} +13 -13
- package/dist/{chunk-WNTAPVU5.cjs.map → chunk-CQBXOJ6U.cjs.map} +1 -1
- package/dist/{chunk-T7XEKOVW.js → chunk-D4CUGAKZ.js} +3 -3
- package/dist/{chunk-T7XEKOVW.js.map → chunk-D4CUGAKZ.js.map} +1 -1
- package/dist/{chunk-R6TA2DRJ.js → chunk-DKD3MH6A.js} +6 -6
- package/dist/{chunk-R6TA2DRJ.js.map → chunk-DKD3MH6A.js.map} +1 -1
- package/dist/{chunk-NLXOGBRJ.cjs → chunk-DKK5QY4C.cjs} +358 -326
- package/dist/chunk-DKK5QY4C.cjs.map +1 -0
- package/dist/{chunk-YXGAW7BB.js → chunk-DN2PACRI.js} +10 -10
- package/dist/{chunk-YXGAW7BB.js.map → chunk-DN2PACRI.js.map} +1 -1
- package/dist/{chunk-XSX2UU6Y.cjs → chunk-DXGZ2NUG.cjs} +10 -10
- package/dist/{chunk-XSX2UU6Y.cjs.map → chunk-DXGZ2NUG.cjs.map} +1 -1
- package/dist/{chunk-NUKRL3I6.cjs → chunk-EG4QM7ZJ.cjs} +18 -2
- package/dist/chunk-EG4QM7ZJ.cjs.map +1 -0
- package/dist/{chunk-C7QPML3L.js → chunk-GEEXBYFL.js} +9 -9
- package/dist/{chunk-C7QPML3L.js.map → chunk-GEEXBYFL.js.map} +1 -1
- package/dist/{chunk-A4VHSE56.cjs → chunk-GKEFRLU7.cjs} +6 -6
- package/dist/{chunk-A4VHSE56.cjs.map → chunk-GKEFRLU7.cjs.map} +1 -1
- package/dist/{chunk-NIRE5CNO.cjs → chunk-HCZD23VS.cjs} +13 -13
- package/dist/{chunk-NIRE5CNO.cjs.map → chunk-HCZD23VS.cjs.map} +1 -1
- package/dist/{chunk-IRCJ7EHV.cjs → chunk-HOXVQ5FR.cjs} +14 -14
- package/dist/{chunk-IRCJ7EHV.cjs.map → chunk-HOXVQ5FR.cjs.map} +1 -1
- package/dist/{chunk-QARJGQSA.js → chunk-HS7AVXTF.js} +3 -3
- package/dist/{chunk-QARJGQSA.js.map → chunk-HS7AVXTF.js.map} +1 -1
- package/dist/{chunk-4I55V454.cjs → chunk-I6HGWHPU.cjs} +4 -4
- package/dist/{chunk-4I55V454.cjs.map → chunk-I6HGWHPU.cjs.map} +1 -1
- package/dist/{chunk-T73NA43R.js → chunk-IPYYKCRC.js} +3 -3
- package/dist/{chunk-T73NA43R.js.map → chunk-IPYYKCRC.js.map} +1 -1
- package/dist/{chunk-UUP3MUZ6.cjs → chunk-ITYQ6VBP.cjs} +18 -18
- package/dist/{chunk-UUP3MUZ6.cjs.map → chunk-ITYQ6VBP.cjs.map} +1 -1
- package/dist/chunk-JPBKYL3B.cjs +18 -0
- package/dist/{chunk-6HTXPPHK.cjs.map → chunk-JPBKYL3B.cjs.map} +1 -1
- package/dist/{chunk-MZ2FGBLZ.js → chunk-K5OJUYSZ.js} +4 -4
- package/dist/{chunk-MZ2FGBLZ.js.map → chunk-K5OJUYSZ.js.map} +1 -1
- package/dist/{chunk-EI2Q7SJ5.cjs → chunk-KQ7TS7WW.cjs} +10 -10
- package/dist/{chunk-EI2Q7SJ5.cjs.map → chunk-KQ7TS7WW.cjs.map} +1 -1
- package/dist/{chunk-RUDY2GTT.js → chunk-KQRW2GPZ.js} +5 -5
- package/dist/{chunk-RUDY2GTT.js.map → chunk-KQRW2GPZ.js.map} +1 -1
- package/dist/{chunk-P5LCASTC.cjs → chunk-KYAJINUI.cjs} +125 -125
- package/dist/{chunk-P5LCASTC.cjs.map → chunk-KYAJINUI.cjs.map} +1 -1
- package/dist/{chunk-AMFXSENK.js → chunk-LWGSI6M3.js} +4 -4
- package/dist/{chunk-AMFXSENK.js.map → chunk-LWGSI6M3.js.map} +1 -1
- package/dist/{chunk-ZHPWGIUX.js → chunk-LXI4XZEC.js} +5 -5
- package/dist/{chunk-ZHPWGIUX.js.map → chunk-LXI4XZEC.js.map} +1 -1
- package/dist/{chunk-AG5JPQIY.js → chunk-LXQ6HKPS.js} +3 -3
- package/dist/{chunk-AG5JPQIY.js.map → chunk-LXQ6HKPS.js.map} +1 -1
- package/dist/{chunk-ROYPRJH4.cjs → chunk-LYRDF5TQ.cjs} +9 -9
- package/dist/{chunk-ROYPRJH4.cjs.map → chunk-LYRDF5TQ.cjs.map} +1 -1
- package/dist/{chunk-G24KZRSA.js → chunk-N2U4AZGB.js} +3 -3
- package/dist/{chunk-G24KZRSA.js.map → chunk-N2U4AZGB.js.map} +1 -1
- package/dist/{chunk-4ERPXIVB.js → chunk-O73J46PV.js} +5 -5
- package/dist/{chunk-4ERPXIVB.js.map → chunk-O73J46PV.js.map} +1 -1
- package/dist/{chunk-F3YZMOAU.cjs → chunk-O7DK5PBJ.cjs} +4 -4
- package/dist/{chunk-F3YZMOAU.cjs.map → chunk-O7DK5PBJ.cjs.map} +1 -1
- package/dist/{chunk-Z4BCLVTI.cjs → chunk-O7LZFG6O.cjs} +5 -5
- package/dist/{chunk-Z4BCLVTI.cjs.map → chunk-O7LZFG6O.cjs.map} +1 -1
- package/dist/{chunk-3YJNUKYN.cjs → chunk-OHLDPWTA.cjs} +15 -15
- package/dist/{chunk-3YJNUKYN.cjs.map → chunk-OHLDPWTA.cjs.map} +1 -1
- package/dist/{chunk-FUL2I7G5.js → chunk-OSEG74VJ.js} +8 -8
- package/dist/{chunk-FUL2I7G5.js.map → chunk-OSEG74VJ.js.map} +1 -1
- package/dist/{chunk-UCBJBJ27.js → chunk-PUAEFLYK.js} +4 -4
- package/dist/{chunk-UCBJBJ27.js.map → chunk-PUAEFLYK.js.map} +1 -1
- package/dist/{chunk-2C72DXQF.cjs → chunk-RFMBZPOW.cjs} +9 -9
- package/dist/{chunk-2C72DXQF.cjs.map → chunk-RFMBZPOW.cjs.map} +1 -1
- package/dist/{chunk-NLTVXLGT.js → chunk-RFXWMRFC.js} +3 -3
- package/dist/{chunk-NLTVXLGT.js.map → chunk-RFXWMRFC.js.map} +1 -1
- package/dist/{chunk-MG3SOM3M.js → chunk-RMIWHYC7.js} +6 -6
- package/dist/{chunk-MG3SOM3M.js.map → chunk-RMIWHYC7.js.map} +1 -1
- package/dist/{chunk-2DG7KW4L.cjs → chunk-RMWTPV3M.cjs} +8 -8
- package/dist/{chunk-2DG7KW4L.cjs.map → chunk-RMWTPV3M.cjs.map} +1 -1
- package/dist/{chunk-P6A3M6VD.cjs → chunk-SZVR77EJ.cjs} +10 -10
- package/dist/{chunk-P6A3M6VD.cjs.map → chunk-SZVR77EJ.cjs.map} +1 -1
- package/dist/{chunk-3E77SX4H.js → chunk-TAZVKYWJ.js} +3 -3
- package/dist/{chunk-3E77SX4H.js.map → chunk-TAZVKYWJ.js.map} +1 -1
- package/dist/{chunk-WKRSH2VR.js → chunk-U6IFHFWK.js} +3 -3
- package/dist/{chunk-WKRSH2VR.js.map → chunk-U6IFHFWK.js.map} +1 -1
- package/dist/{chunk-I6TGFUCO.cjs → chunk-UALX2J2Q.cjs} +4 -4
- package/dist/{chunk-I6TGFUCO.cjs.map → chunk-UALX2J2Q.cjs.map} +1 -1
- package/dist/{chunk-XN7NOENA.js → chunk-UBDMVMBZ.js} +5 -5
- package/dist/{chunk-XN7NOENA.js.map → chunk-UBDMVMBZ.js.map} +1 -1
- package/dist/{chunk-YTR3RBUZ.js → chunk-UEXIJTLC.js} +4 -4
- package/dist/{chunk-YTR3RBUZ.js.map → chunk-UEXIJTLC.js.map} +1 -1
- package/dist/{chunk-DQKERTND.cjs → chunk-UGT7GK7I.cjs} +11 -11
- package/dist/{chunk-DQKERTND.cjs.map → chunk-UGT7GK7I.cjs.map} +1 -1
- package/dist/{chunk-FKMUFNQE.js → chunk-UMP6OJIM.js} +4 -4
- package/dist/{chunk-FKMUFNQE.js.map → chunk-UMP6OJIM.js.map} +1 -1
- package/dist/{chunk-AEOZTXVW.js → chunk-UNYXQ4M5.js} +3 -3
- package/dist/{chunk-AEOZTXVW.js.map → chunk-UNYXQ4M5.js.map} +1 -1
- package/dist/{chunk-2BDH744Z.js → chunk-UTSRIDKZ.js} +3 -3
- package/dist/{chunk-2BDH744Z.js.map → chunk-UTSRIDKZ.js.map} +1 -1
- package/dist/{chunk-2ADR2GSO.cjs → chunk-UU36SHFY.cjs} +5 -5
- package/dist/{chunk-2ADR2GSO.cjs.map → chunk-UU36SHFY.cjs.map} +1 -1
- package/dist/{chunk-R3UPQFKK.cjs → chunk-V2BOS4WF.cjs} +9 -9
- package/dist/{chunk-R3UPQFKK.cjs.map → chunk-V2BOS4WF.cjs.map} +1 -1
- package/dist/{chunk-CASUEY42.cjs → chunk-VX4SQ2LY.cjs} +24 -24
- package/dist/{chunk-CASUEY42.cjs.map → chunk-VX4SQ2LY.cjs.map} +1 -1
- package/dist/{chunk-CHKH5452.js → chunk-WHFUZMIC.js} +80 -48
- package/dist/chunk-WHFUZMIC.js.map +1 -0
- package/dist/{chunk-Q4CMI2LP.js → chunk-WLQNMXQE.js} +5 -5
- package/dist/{chunk-Q4CMI2LP.js.map → chunk-WLQNMXQE.js.map} +1 -1
- package/dist/{chunk-XWL6O3SW.cjs → chunk-WNO3C3N2.cjs} +4 -4
- package/dist/{chunk-XWL6O3SW.cjs.map → chunk-WNO3C3N2.cjs.map} +1 -1
- package/dist/{chunk-T7O6K6PX.js → chunk-XEQ3TM66.js} +18 -3
- package/dist/chunk-XEQ3TM66.js.map +1 -0
- package/dist/{chunk-BVZW2B5V.cjs → chunk-XJCXEKMD.cjs} +4 -4
- package/dist/{chunk-BVZW2B5V.cjs.map → chunk-XJCXEKMD.cjs.map} +1 -1
- package/dist/{chunk-JTB5Q42C.cjs → chunk-XL7Q7HB2.cjs} +4 -4
- package/dist/{chunk-JTB5Q42C.cjs.map → chunk-XL7Q7HB2.cjs.map} +1 -1
- package/dist/{chunk-LW7G5DYW.js → chunk-XO4SOZN6.js} +4 -4
- package/dist/{chunk-LW7G5DYW.js.map → chunk-XO4SOZN6.js.map} +1 -1
- package/dist/{chunk-CQGYNZ3K.cjs → chunk-XWTLIGO3.cjs} +4 -4
- package/dist/{chunk-CQGYNZ3K.cjs.map → chunk-XWTLIGO3.cjs.map} +1 -1
- package/dist/{chunk-6SBW4QR2.cjs → chunk-YML4ME27.cjs} +11 -11
- package/dist/{chunk-6SBW4QR2.cjs.map → chunk-YML4ME27.cjs.map} +1 -1
- package/dist/{chunk-P6H23T43.cjs → chunk-ZCIRZE37.cjs} +11 -11
- package/dist/{chunk-P6H23T43.cjs.map → chunk-ZCIRZE37.cjs.map} +1 -1
- package/dist/{chunk-QI7WE46I.cjs → chunk-ZLQSWSJ2.cjs} +11 -11
- package/dist/{chunk-QI7WE46I.cjs.map → chunk-ZLQSWSJ2.cjs.map} +1 -1
- package/dist/{chunk-ILCGLTSA.cjs → chunk-ZSWPZ23K.cjs} +4 -4
- package/dist/{chunk-ILCGLTSA.cjs.map → chunk-ZSWPZ23K.cjs.map} +1 -1
- package/dist/{chunk-IDCKSLYH.js → chunk-ZZMZWBR6.js} +3 -3
- package/dist/{chunk-IDCKSLYH.js.map → chunk-ZZMZWBR6.js.map} +1 -1
- package/dist/compact-session-QV57KR66.cjs +59 -0
- package/dist/{compact-session-GJXJD73F.cjs.map → compact-session-QV57KR66.cjs.map} +1 -1
- package/dist/compact-session-RIGOWC4Y.js +22 -0
- package/dist/{compact-session-TSMQOIHO.js.map → compact-session-RIGOWC4Y.js.map} +1 -1
- package/dist/compaction.cjs +17 -17
- package/dist/compaction.js +4 -4
- package/dist/concurrency.cjs +7 -7
- package/dist/concurrency.js +5 -5
- package/dist/context-AGQ3DIZU.cjs +24 -0
- package/dist/{context-LCKNH2XI.cjs.map → context-AGQ3DIZU.cjs.map} +1 -1
- package/dist/context-GQRFBCLP.js +7 -0
- package/dist/{context-MZSKGKYR.js.map → context-GQRFBCLP.js.map} +1 -1
- package/dist/cron.cjs +36 -36
- package/dist/cron.js +35 -35
- package/dist/errors.cjs +22 -22
- package/dist/errors.js +3 -3
- package/dist/eval.cjs +41 -41
- package/dist/eval.js +35 -35
- package/dist/{executor-ZERJ3DGN.cjs → executor-6CYCA7MI.cjs} +25 -25
- package/dist/{executor-ZERJ3DGN.cjs.map → executor-6CYCA7MI.cjs.map} +1 -1
- package/dist/{executor-BMEHFOXZ.js → executor-QSEC3MRH.js} +8 -8
- package/dist/{executor-BMEHFOXZ.js.map → executor-QSEC3MRH.js.map} +1 -1
- package/dist/filesystem/index.cjs +8 -8
- package/dist/filesystem/index.js +4 -4
- package/dist/fs-session-store-4PIHT5UX.cjs +20 -0
- package/dist/{fs-session-store-XZ7ZTB3A.cjs.map → fs-session-store-4PIHT5UX.cjs.map} +1 -1
- package/dist/fs-session-store-NM6YSTE2.js +11 -0
- package/dist/{fs-session-store-4BHGIWRE.js.map → fs-session-store-NM6YSTE2.js.map} +1 -1
- package/dist/generate-object-GUNAUVBX.js +8 -0
- package/dist/{generate-object-N5MDZUJI.js.map → generate-object-GUNAUVBX.js.map} +1 -1
- package/dist/generate-object-R7HTMW7F.cjs +21 -0
- package/dist/{generate-object-PGSP5U7N.cjs.map → generate-object-R7HTMW7F.cjs.map} +1 -1
- package/dist/index-manager-RBDHP3PM.js +17 -0
- package/dist/{index-manager-ACEQG3IJ.js.map → index-manager-RBDHP3PM.js.map} +1 -1
- package/dist/index-manager-UZBF22BJ.cjs +26 -0
- package/dist/{index-manager-6TEEIWM7.cjs.map → index-manager-UZBF22BJ.cjs.map} +1 -1
- package/dist/index.cjs +139 -139
- package/dist/index.js +50 -50
- package/dist/{inject-session-ZCQUB3IT.js → inject-session-DAR7OUCW.js} +8 -8
- package/dist/{inject-session-ZCQUB3IT.js.map → inject-session-DAR7OUCW.js.map} +1 -1
- package/dist/{inject-session-PPSO2IDD.cjs → inject-session-WQKMC3GR.cjs} +10 -10
- package/dist/{inject-session-PPSO2IDD.cjs.map → inject-session-WQKMC3GR.cjs.map} +1 -1
- package/dist/internal/memory/adapters/index.cjs +9 -9
- package/dist/internal/memory/adapters/index.js +8 -8
- package/dist/internal/memory/storage/index.cjs +39 -39
- package/dist/internal/memory/storage/index.js +10 -10
- package/dist/internal/persistence/index.cjs +21 -21
- package/dist/internal/persistence/index.js +7 -7
- package/dist/internal/security/index.cjs +14 -14
- package/dist/internal/security/index.js +4 -4
- package/dist/judge-call-LAHMBJ57.cjs +23 -0
- package/dist/{judge-call-EGYRC2RE.cjs.map → judge-call-LAHMBJ57.cjs.map} +1 -1
- package/dist/judge-call-LVXABPS3.js +6 -0
- package/dist/{judge-call-DWHAJATE.js.map → judge-call-LVXABPS3.js.map} +1 -1
- package/dist/mcp-auth.cjs +15 -15
- package/dist/mcp-auth.js +5 -5
- package/dist/models.cjs +22 -22
- package/dist/models.js +7 -7
- package/dist/path-safety.cjs +11 -11
- package/dist/path-safety.js +4 -4
- package/dist/persistence.cjs +28 -28
- package/dist/persistence.js +9 -9
- package/dist/project.cjs +6 -6
- package/dist/project.js +4 -4
- package/dist/providers.cjs +9 -9
- package/dist/providers.js +7 -7
- package/dist/registry-75W33OGL.js +10 -0
- package/dist/{registry-VFP3WWQP.js.map → registry-75W33OGL.js.map} +1 -1
- package/dist/registry-V7BDQ6QB.cjs +47 -0
- package/dist/{registry-VFKX7WOP.cjs.map → registry-V7BDQ6QB.cjs.map} +1 -1
- package/dist/retry.cjs +5 -5
- package/dist/retry.js +4 -4
- package/dist/sandbox/index.cjs +7 -7
- package/dist/sandbox/index.js +3 -3
- package/dist/server/errors-envelope.cjs +15 -15
- package/dist/server/errors-envelope.js +4 -4
- package/dist/session-transcript-6WNITKRE.cjs +63 -0
- package/dist/{session-transcript-HF5GEU5M.cjs.map → session-transcript-6WNITKRE.cjs.map} +1 -1
- package/dist/session-transcript-XAEKW6P7.js +6 -0
- package/dist/{session-transcript-P462ZRHX.js.map → session-transcript-XAEKW6P7.js.map} +1 -1
- package/dist/skills.cjs +8 -8
- package/dist/skills.js +6 -6
- package/dist/stream-object-UF4QRQRU.js +8 -0
- package/dist/{stream-object-L57K4OFS.js.map → stream-object-UF4QRQRU.js.map} +1 -1
- package/dist/stream-object-ZJTQNKY3.cjs +21 -0
- package/dist/{stream-object-BUCF5PHO.cjs.map → stream-object-ZJTQNKY3.cjs.map} +1 -1
- package/dist/subagents-loader-CRT5LLL3.js +8 -0
- package/dist/{subagents-loader-6ATCDEKN.js.map → subagents-loader-CRT5LLL3.js.map} +1 -1
- package/dist/subagents-loader-YEUFFPAL.cjs +17 -0
- package/dist/{subagents-loader-X5QKODCL.cjs.map → subagents-loader-YEUFFPAL.cjs.map} +1 -1
- package/dist/subagents-loader.cjs +7 -7
- package/dist/subagents-loader.js +5 -5
- package/dist/subscription/index.cjs +4 -4
- package/dist/subscription/index.js +3 -3
- package/dist/task-store.cjs +8 -8
- package/dist/task-store.js +5 -5
- package/dist/workflow.cjs +28 -28
- package/dist/workflow.js +7 -7
- package/package.json +1 -1
- package/dist/agent-536OOESZ.js +0 -57
- package/dist/agent-5KYMG63I.cjs +0 -66
- package/dist/agent-session-store-G2BMUZQV.js +0 -7
- package/dist/agent-session-store-WAKLOKWK.cjs +0 -24
- package/dist/chunk-6HTXPPHK.cjs +0 -18
- package/dist/chunk-CHKH5452.js.map +0 -1
- package/dist/chunk-NLXOGBRJ.cjs.map +0 -1
- package/dist/chunk-NUKRL3I6.cjs.map +0 -1
- package/dist/chunk-T7O6K6PX.js.map +0 -1
- package/dist/compact-session-GJXJD73F.cjs +0 -59
- package/dist/compact-session-TSMQOIHO.js +0 -22
- package/dist/context-LCKNH2XI.cjs +0 -24
- package/dist/context-MZSKGKYR.js +0 -7
- package/dist/fs-session-store-4BHGIWRE.js +0 -11
- package/dist/fs-session-store-XZ7ZTB3A.cjs +0 -20
- package/dist/generate-object-N5MDZUJI.js +0 -8
- package/dist/generate-object-PGSP5U7N.cjs +0 -21
- package/dist/index-manager-6TEEIWM7.cjs +0 -26
- package/dist/index-manager-ACEQG3IJ.js +0 -17
- package/dist/judge-call-DWHAJATE.js +0 -6
- package/dist/judge-call-EGYRC2RE.cjs +0 -23
- package/dist/registry-VFKX7WOP.cjs +0 -47
- package/dist/registry-VFP3WWQP.js +0 -10
- package/dist/session-transcript-HF5GEU5M.cjs +0 -63
- package/dist/session-transcript-P462ZRHX.js +0 -6
- package/dist/stream-object-BUCF5PHO.cjs +0 -21
- package/dist/stream-object-L57K4OFS.js +0 -8
- package/dist/subagents-loader-6ATCDEKN.js +0 -8
- package/dist/subagents-loader-X5QKODCL.cjs +0 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/memory/adapters/openai-compatible.ts"],"names":["AuthenticationError","globalEmbeddingCache","ConfigurationError","mapWithConcurrency","mapOpenAICompatibleError","NetworkError","createHash"],"mappings":"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","file":"chunk-EI2Q7SJ5.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { AuthenticationError, ConfigurationError, NetworkError } from \"../../../errors.js\";\nimport { mapOpenAICompatibleError } from \"../../error-mappers/openai-compatible.js\";\nimport { mapWithConcurrency } from \"../../runtime/concurrency/map-with-concurrency.js\";\nimport type {\n CreateAdapterOptions,\n EmbeddingRuntime,\n EmbeddingRuntimeStats,\n} from \"../embedding-adapter.js\";\nimport { globalEmbeddingCache } from \"../embedding-cache.js\";\n\n/**\n * Shared factory for OpenAI-compatible embedding providers (OpenAI, Mistral,\n * DeepInfra, and anything else exposing `POST /v1/embeddings` with the\n * `{ model, input }` request shape and `{ data: [{ embedding }] }` response).\n *\n * Mirrors the inner mechanics of peer-project's batch embedding runtime.\n *\n * @internal\n */\n\nconst MAX_BATCH = 100;\nconst MAX_RETRIES = 2;\n\n/**\n * What one provider adapter tells {@link createOpenAiCompatibleRuntime} about\n * its wire: where to POST, which environment variables carry the key and the\n * base URL, which model to use by default, and how wide each model's vectors\n * are.\n *\n * `id` is the provider id that ends up on errors and on\n * `EmbeddingRuntime.id`, and is also the identity the SQLite vector index stores\n * — changing it invalidates the vectors already on disk.\n *\n * `apiKeyEnv` is required, `baseUrlEnv` is not: a provider that declares no\n * base-URL variable can only be redirected by passing `baseUrl` explicitly.\n * Leave `dialect` unset for anything that speaks the OpenAI embedding wire.\n */\nexport interface OpenAiCompatibleConfig {\n id: string;\n defaultBaseUrl: string;\n apiKeyEnv: string;\n baseUrlEnv?: string;\n defaultModel: string;\n /**\n * Dimension hint by model id. The chosen model MUST be in this table —\n * unknown models throw `ConfigurationError(code: \"embedding_unknown_model\")`\n * (EC-4 fix). This prevents vec0 virtual-table dimension mismatches that\n * surface as cryptic errors downstream.\n */\n dimensionByModel: Record<string, number>;\n /**\n * Path component appended to `baseUrl` for the embeddings endpoint.\n * Default `\"/v1/embeddings\"` (OpenAI canonical). REPLACES the default —\n * does NOT concatenate. Used by DeepInfra (`\"/v1/openai/embeddings\"`),\n * etc. (EC-2 fix).\n */\n embeddingsPath?: string;\n /**\n * theokit#159 — per-provider deviations from the OpenAI wire.\n *\n * The runtime was rigidly OpenAI-shaped: one auth header, one body, one response shape. Three of\n * the ten catalog providers do not speak it, so every call they made was rejected — Azure\n * authenticates with an `api-key` header and puts the deployment in the path, Cohere's `/v2/embed`\n * takes `texts` and answers `{embeddings:{float}}`. Advertising a provider that cannot work is\n * worse than not advertising it.\n *\n * Every hook is optional and its default is exactly the previous behaviour, so the seven\n * providers that DO speak OpenAI are untouched.\n */\n dialect?: EmbeddingDialect;\n}\n\n/**\n * theokit#159 — the three points where a provider may deviate from the OpenAI embedding wire.\n *\n * Deliberately three narrow hooks rather than a general \"transform the request\" seam: these are the\n * only axes on which the supported providers actually differ, and a wider seam would invite\n * per-provider logic that belongs in the adapter.\n */\nexport interface EmbeddingDialect {\n /** Replaces `authorization: Bearer <key>`. */\n authHeaders?: (apiKey: string) => Record<string, string>;\n /** Replaces the `{ model, input }` request body. */\n body?: (model: string, inputs: ReadonlyArray<string>) => Record<string, unknown>;\n /** Replaces reading vectors from `json.data[].embedding`. Return `undefined` when unparseable. */\n vectors?: (json: unknown) => number[][] | undefined;\n}\n\n/**\n * Build an {@link EmbeddingRuntime} for a provider that speaks the OpenAI\n * embedding wire — or, through `cfg.dialect`, one that deviates from it in a\n * known way. Every adapter in the memory catalog is a thin call to this.\n *\n * Resolution order for each setting is explicit option, then environment\n * variable, then the config default. Two things are checked before any request\n * is sent, so a misconfiguration fails at creation rather than at first search:\n * a missing or empty key rejects with an `AuthenticationError` carrying\n * `embedding_missing_api_key`, and a model absent from `dimensionByModel`\n * rejects with a `ConfigurationError` carrying `embedding_unknown_model`. The\n * second check exists because the vector index is created at a fixed width — an\n * unknown dimension would surface much later as a vec0 mismatch.\n *\n * `{model}` in `embeddingsPath` is replaced with the URL-encoded model, which is\n * how Azure addresses a deployment in the path. `embeddingsPath` replaces the\n * default `/v1/embeddings` rather than being appended to it.\n *\n * The returned `embed` batches at 100 texts per request and runs at most 3\n * requests concurrently, preserving input order. Whitespace-only inputs never\n * reach the network and come back as an all-zero vector. Results are cached by\n * model and text; the cache defaults to a process-wide LRU of 5000 entries, so\n * pass `options.cache` when runtimes must not share entries.\n *\n * Failed requests retry up to twice on 429 and 5xx with a 50ms-per-attempt\n * linear backoff. Anything else, and a retry budget that runs out, throws the\n * typed error the OpenAI-compatible mapper builds from the status and body. A\n * response that parses but carries no vectors throws a `NetworkError` with\n * `embedding_invalid_response`. There is no request timeout here — cancellation\n * is the caller's to impose.\n */\nexport async function createOpenAiCompatibleRuntime(\n cfg: OpenAiCompatibleConfig,\n options: CreateAdapterOptions,\n): Promise<EmbeddingRuntime> {\n const model = options.model ?? cfg.defaultModel;\n const apiKey = options.apiKey ?? process.env[cfg.apiKeyEnv];\n if (apiKey === undefined || apiKey.length === 0) {\n throw new AuthenticationError(`${cfg.apiKeyEnv} missing`, {\n code: \"embedding_missing_api_key\",\n });\n }\n const envBaseUrl = cfg.baseUrlEnv !== undefined ? process.env[cfg.baseUrlEnv] : undefined;\n const baseUrl = options.baseUrl ?? envBaseUrl ?? cfg.defaultBaseUrl;\n const fetchImpl = options.fetch ?? fetch;\n // T4.4 — default to process-wide singleton (was per-adapter instance).\n const cache = options.cache ?? globalEmbeddingCache;\n // EC-4: refuse unknown models to prevent vec0 dimension mismatches downstream.\n const dimension = cfg.dimensionByModel[model];\n if (dimension === undefined) {\n throw new ConfigurationError(\n `${cfg.id} adapter: model \"${model}\" is not in the dimension table. Use one of: ${Object.keys(cfg.dimensionByModel).join(\", \")}. To add it, update dimensionByModel in the adapter source.`,\n { code: \"embedding_unknown_model\" },\n );\n }\n // theokit#128: `{model}` is substituted, because Azure OpenAI addresses the deployment IN THE\n // PATH (`/openai/deployments/{model}/embeddings`) rather than in the body. Without this the URL\n // carried the literal placeholder and every Azure request 404'd. A path with no placeholder —\n // every other provider — is passed through untouched.\n const embeddingsPath = (cfg.embeddingsPath ?? \"/v1/embeddings\").replace(\n \"{model}\",\n encodeURIComponent(model),\n );\n\n const stats: EmbeddingRuntimeStats = {\n cacheHits: 0,\n cacheMisses: 0,\n httpCalls: 0,\n retries: 0,\n };\n\n return {\n id: cfg.id,\n model,\n dimension,\n stats: () => ({ ...stats }),\n embed: (texts) =>\n embedTexts({\n texts,\n cache,\n stats,\n model,\n dimension,\n apiKey,\n baseUrl,\n embeddingsPath,\n fetchImpl,\n providerId: cfg.id,\n dialect: cfg.dialect ?? {},\n }),\n };\n}\n\ninterface EmbedTextsInput {\n texts: ReadonlyArray<string>;\n cache: NonNullable<CreateAdapterOptions[\"cache\"]>;\n stats: EmbeddingRuntimeStats;\n model: string;\n dimension: number;\n apiKey: string;\n baseUrl: string;\n embeddingsPath: string;\n fetchImpl: typeof fetch;\n providerId: string;\n dialect: EmbeddingDialect;\n}\n\nasync function embedTexts(input: EmbedTextsInput): Promise<number[][]> {\n const { texts, cache, stats, model, dimension } = input;\n const results = new Array<number[] | undefined>(texts.length);\n const pending: Array<{ index: number; text: string; key: string }> = [];\n for (let i = 0; i < texts.length; i++) {\n classifyEntry({\n index: i,\n text: texts[i] ?? \"\",\n model,\n dimension,\n cache,\n stats,\n results,\n pending,\n });\n }\n await embedInBoundedBatches(input, pending, results);\n return results.map((v) => v ?? new Array(dimension).fill(0));\n}\n\ninterface ClassifyEntryArgs {\n index: number;\n text: string;\n model: string;\n dimension: number;\n cache: NonNullable<CreateAdapterOptions[\"cache\"]>;\n stats: EmbeddingRuntimeStats;\n results: Array<number[] | undefined>;\n pending: Array<{ index: number; text: string; key: string }>;\n}\n\nfunction classifyEntry(args: ClassifyEntryArgs): void {\n if (args.text.trim().length === 0) {\n args.results[args.index] = new Array(args.dimension).fill(0);\n return;\n }\n const key = hashKey(args.model, args.text);\n const cached = args.cache.get(key);\n if (cached !== undefined) {\n args.stats.cacheHits += 1;\n args.results[args.index] = cached;\n return;\n }\n args.stats.cacheMisses += 1;\n args.pending.push({ index: args.index, text: args.text, key });\n}\n\n// T4.3 — max concurrent embedding API requests per embed() call.\n// 3 is enough to saturate most embedding APIs without triggering\n// rate limits (each batch is already up to MAX_BATCH=100 texts).\nconst MAX_CONCURRENT_BATCHES = 3;\n\n/**\n * T4.3 — parallel embed batches (DR4 finding #3).\n *\n * Pre-T4.3 this was a serial `for` loop — each batch of 100 texts\n * awaited before the next HTTP request started. With 500 texts the\n * serial path took 5 × RTT; parallel takes max(RTT) × ceil(5/3).\n *\n * Uses bounded-concurrency `Promise.all` capped at 3 concurrent\n * HTTP requests to avoid overwhelming the embedding API.\n */\nasync function embedInBoundedBatches(\n input: EmbedTextsInput,\n pending: ReadonlyArray<{ index: number; text: string; key: string }>,\n results: Array<number[] | undefined>,\n): Promise<void> {\n const batches: Array<ReadonlyArray<{ index: number; text: string; key: string }>> = [];\n for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {\n batches.push(pending.slice(offset, offset + MAX_BATCH));\n }\n // M0-2: bounded parallel via the shared ordered pool (was an inline\n // acquire/release clone — see plan m0-foundation-expose-primitives).\n await mapWithConcurrency(batches, MAX_CONCURRENT_BATCHES, (batch) =>\n processBatch(input, batch, results),\n );\n}\n\nasync function processBatch(\n input: EmbedTextsInput,\n batch: ReadonlyArray<{ index: number; text: string; key: string }>,\n results: Array<number[] | undefined>,\n): Promise<void> {\n const vectors = await embedBatch({\n apiKey: input.apiKey,\n baseUrl: input.baseUrl,\n embeddingsPath: input.embeddingsPath,\n model: input.model,\n inputs: batch.map((b) => b.text),\n fetchImpl: input.fetchImpl,\n stats: input.stats,\n providerId: input.providerId,\n dialect: input.dialect,\n });\n for (let j = 0; j < batch.length; j++) {\n const slot = batch[j];\n const vector = vectors[j];\n if (slot === undefined || vector === undefined) continue;\n results[slot.index] = vector;\n input.cache.set(slot.key, vector);\n }\n}\n\ninterface BatchOptions {\n apiKey: string;\n baseUrl: string;\n embeddingsPath: string;\n model: string;\n inputs: ReadonlyArray<string>;\n fetchImpl: typeof fetch;\n stats: EmbeddingRuntimeStats;\n providerId: string;\n dialect: EmbeddingDialect;\n}\n\nasync function embedBatch(opts: BatchOptions): Promise<number[][]> {\n // EC-2: embeddingsPath REPLACES the suffix; never concatenates.\n const url = `${opts.baseUrl.replace(/\\/$/, \"\")}${opts.embeddingsPath}`;\n let attempt = 0;\n while (true) {\n opts.stats.httpCalls += 1;\n const response = await postEmbedRequest(opts, url);\n if (response.ok)\n return await parseEmbedResponse(response, opts.providerId, opts.embeddingsPath, opts.dialect);\n if (isRetryable(response.status) && attempt < MAX_RETRIES) {\n attempt += 1;\n opts.stats.retries += 1;\n await sleep(linearBackoffMs(attempt));\n continue;\n }\n // Read body (best-effort) so the mapper has access to provider error code.\n const text = await response.text().catch(() => \"\");\n let body: unknown = text;\n try {\n body = JSON.parse(text);\n } catch {\n // not JSON — keep as string\n }\n throw mapOpenAICompatibleError({\n providerId: opts.providerId,\n status: response.status,\n body,\n headers: response.headers,\n endpoint: opts.embeddingsPath,\n });\n }\n}\n\nasync function postEmbedRequest(opts: BatchOptions, url: string): Promise<Response> {\n return opts.fetchImpl(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(opts.dialect.authHeaders?.(opts.apiKey) ?? { authorization: `Bearer ${opts.apiKey}` }),\n },\n body: JSON.stringify(\n opts.dialect.body?.(opts.model, opts.inputs) ?? { model: opts.model, input: opts.inputs },\n ),\n });\n}\n\nasync function parseEmbedResponse(\n response: Response,\n providerId: string,\n endpoint: string,\n dialect?: EmbeddingDialect,\n): Promise<number[][]> {\n const json = (await response.json()) as { data?: Array<{ embedding: number[] }> };\n // theokit#159 — a provider whose response is not OpenAI-shaped supplies its own reader.\n const viaDialect = dialect?.vectors?.(json);\n if (viaDialect !== undefined) return viaDialect;\n if (!Array.isArray(json.data)) {\n throw new NetworkError(`${providerId} ${endpoint} returned no data`, {\n code: \"embedding_invalid_response\",\n metadata: {\n provider: providerId,\n endpoint,\n code: \"invalid_request\",\n raw: json,\n },\n });\n }\n return json.data.map((d) => d.embedding);\n}\n\nfunction isRetryable(status: number): boolean {\n return status === 429 || (status >= 500 && status < 600);\n}\n\nfunction hashKey(model: string, text: string): string {\n return createHash(\"sha256\").update(`${model} ${text}`).digest(\"hex\");\n}\n\nfunction linearBackoffMs(attempt: number): number {\n return 50 * attempt;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/memory/adapters/openai-compatible.ts"],"names":["AuthenticationError","globalEmbeddingCache","ConfigurationError","mapWithConcurrency","mapOpenAICompatibleError","NetworkError","createHash"],"mappings":"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","file":"chunk-KQ7TS7WW.cjs","sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { AuthenticationError, ConfigurationError, NetworkError } from \"../../../errors.js\";\nimport { mapOpenAICompatibleError } from \"../../error-mappers/openai-compatible.js\";\nimport { mapWithConcurrency } from \"../../runtime/concurrency/map-with-concurrency.js\";\nimport type {\n CreateAdapterOptions,\n EmbeddingRuntime,\n EmbeddingRuntimeStats,\n} from \"../embedding-adapter.js\";\nimport { globalEmbeddingCache } from \"../embedding-cache.js\";\n\n/**\n * Shared factory for OpenAI-compatible embedding providers (OpenAI, Mistral,\n * DeepInfra, and anything else exposing `POST /v1/embeddings` with the\n * `{ model, input }` request shape and `{ data: [{ embedding }] }` response).\n *\n * Mirrors the inner mechanics of peer-project's batch embedding runtime.\n *\n * @internal\n */\n\nconst MAX_BATCH = 100;\nconst MAX_RETRIES = 2;\n\n/**\n * What one provider adapter tells {@link createOpenAiCompatibleRuntime} about\n * its wire: where to POST, which environment variables carry the key and the\n * base URL, which model to use by default, and how wide each model's vectors\n * are.\n *\n * `id` is the provider id that ends up on errors and on\n * `EmbeddingRuntime.id`, and is also the identity the SQLite vector index stores\n * — changing it invalidates the vectors already on disk.\n *\n * `apiKeyEnv` is required, `baseUrlEnv` is not: a provider that declares no\n * base-URL variable can only be redirected by passing `baseUrl` explicitly.\n * Leave `dialect` unset for anything that speaks the OpenAI embedding wire.\n */\nexport interface OpenAiCompatibleConfig {\n id: string;\n defaultBaseUrl: string;\n apiKeyEnv: string;\n baseUrlEnv?: string;\n defaultModel: string;\n /**\n * Dimension hint by model id. The chosen model MUST be in this table —\n * unknown models throw `ConfigurationError(code: \"embedding_unknown_model\")`\n * (EC-4 fix). This prevents vec0 virtual-table dimension mismatches that\n * surface as cryptic errors downstream.\n */\n dimensionByModel: Record<string, number>;\n /**\n * Path component appended to `baseUrl` for the embeddings endpoint.\n * Default `\"/v1/embeddings\"` (OpenAI canonical). REPLACES the default —\n * does NOT concatenate. Used by DeepInfra (`\"/v1/openai/embeddings\"`),\n * etc. (EC-2 fix).\n */\n embeddingsPath?: string;\n /**\n * theokit#159 — per-provider deviations from the OpenAI wire.\n *\n * The runtime was rigidly OpenAI-shaped: one auth header, one body, one response shape. Three of\n * the ten catalog providers do not speak it, so every call they made was rejected — Azure\n * authenticates with an `api-key` header and puts the deployment in the path, Cohere's `/v2/embed`\n * takes `texts` and answers `{embeddings:{float}}`. Advertising a provider that cannot work is\n * worse than not advertising it.\n *\n * Every hook is optional and its default is exactly the previous behaviour, so the seven\n * providers that DO speak OpenAI are untouched.\n */\n dialect?: EmbeddingDialect;\n}\n\n/**\n * theokit#159 — the three points where a provider may deviate from the OpenAI embedding wire.\n *\n * Deliberately three narrow hooks rather than a general \"transform the request\" seam: these are the\n * only axes on which the supported providers actually differ, and a wider seam would invite\n * per-provider logic that belongs in the adapter.\n */\nexport interface EmbeddingDialect {\n /** Replaces `authorization: Bearer <key>`. */\n authHeaders?: (apiKey: string) => Record<string, string>;\n /** Replaces the `{ model, input }` request body. */\n body?: (model: string, inputs: ReadonlyArray<string>) => Record<string, unknown>;\n /** Replaces reading vectors from `json.data[].embedding`. Return `undefined` when unparseable. */\n vectors?: (json: unknown) => number[][] | undefined;\n}\n\n/**\n * Build an {@link EmbeddingRuntime} for a provider that speaks the OpenAI\n * embedding wire — or, through `cfg.dialect`, one that deviates from it in a\n * known way. Every adapter in the memory catalog is a thin call to this.\n *\n * Resolution order for each setting is explicit option, then environment\n * variable, then the config default. Two things are checked before any request\n * is sent, so a misconfiguration fails at creation rather than at first search:\n * a missing or empty key rejects with an `AuthenticationError` carrying\n * `embedding_missing_api_key`, and a model absent from `dimensionByModel`\n * rejects with a `ConfigurationError` carrying `embedding_unknown_model`. The\n * second check exists because the vector index is created at a fixed width — an\n * unknown dimension would surface much later as a vec0 mismatch.\n *\n * `{model}` in `embeddingsPath` is replaced with the URL-encoded model, which is\n * how Azure addresses a deployment in the path. `embeddingsPath` replaces the\n * default `/v1/embeddings` rather than being appended to it.\n *\n * The returned `embed` batches at 100 texts per request and runs at most 3\n * requests concurrently, preserving input order. Whitespace-only inputs never\n * reach the network and come back as an all-zero vector. Results are cached by\n * model and text; the cache defaults to a process-wide LRU of 5000 entries, so\n * pass `options.cache` when runtimes must not share entries.\n *\n * Failed requests retry up to twice on 429 and 5xx with a 50ms-per-attempt\n * linear backoff. Anything else, and a retry budget that runs out, throws the\n * typed error the OpenAI-compatible mapper builds from the status and body. A\n * response that parses but carries no vectors throws a `NetworkError` with\n * `embedding_invalid_response`. There is no request timeout here — cancellation\n * is the caller's to impose.\n */\nexport async function createOpenAiCompatibleRuntime(\n cfg: OpenAiCompatibleConfig,\n options: CreateAdapterOptions,\n): Promise<EmbeddingRuntime> {\n const model = options.model ?? cfg.defaultModel;\n const apiKey = options.apiKey ?? process.env[cfg.apiKeyEnv];\n if (apiKey === undefined || apiKey.length === 0) {\n throw new AuthenticationError(`${cfg.apiKeyEnv} missing`, {\n code: \"embedding_missing_api_key\",\n });\n }\n const envBaseUrl = cfg.baseUrlEnv !== undefined ? process.env[cfg.baseUrlEnv] : undefined;\n const baseUrl = options.baseUrl ?? envBaseUrl ?? cfg.defaultBaseUrl;\n const fetchImpl = options.fetch ?? fetch;\n // T4.4 — default to process-wide singleton (was per-adapter instance).\n const cache = options.cache ?? globalEmbeddingCache;\n // EC-4: refuse unknown models to prevent vec0 dimension mismatches downstream.\n const dimension = cfg.dimensionByModel[model];\n if (dimension === undefined) {\n throw new ConfigurationError(\n `${cfg.id} adapter: model \"${model}\" is not in the dimension table. Use one of: ${Object.keys(cfg.dimensionByModel).join(\", \")}. To add it, update dimensionByModel in the adapter source.`,\n { code: \"embedding_unknown_model\" },\n );\n }\n // theokit#128: `{model}` is substituted, because Azure OpenAI addresses the deployment IN THE\n // PATH (`/openai/deployments/{model}/embeddings`) rather than in the body. Without this the URL\n // carried the literal placeholder and every Azure request 404'd. A path with no placeholder —\n // every other provider — is passed through untouched.\n const embeddingsPath = (cfg.embeddingsPath ?? \"/v1/embeddings\").replace(\n \"{model}\",\n encodeURIComponent(model),\n );\n\n const stats: EmbeddingRuntimeStats = {\n cacheHits: 0,\n cacheMisses: 0,\n httpCalls: 0,\n retries: 0,\n };\n\n return {\n id: cfg.id,\n model,\n dimension,\n stats: () => ({ ...stats }),\n embed: (texts) =>\n embedTexts({\n texts,\n cache,\n stats,\n model,\n dimension,\n apiKey,\n baseUrl,\n embeddingsPath,\n fetchImpl,\n providerId: cfg.id,\n dialect: cfg.dialect ?? {},\n }),\n };\n}\n\ninterface EmbedTextsInput {\n texts: ReadonlyArray<string>;\n cache: NonNullable<CreateAdapterOptions[\"cache\"]>;\n stats: EmbeddingRuntimeStats;\n model: string;\n dimension: number;\n apiKey: string;\n baseUrl: string;\n embeddingsPath: string;\n fetchImpl: typeof fetch;\n providerId: string;\n dialect: EmbeddingDialect;\n}\n\nasync function embedTexts(input: EmbedTextsInput): Promise<number[][]> {\n const { texts, cache, stats, model, dimension } = input;\n const results = new Array<number[] | undefined>(texts.length);\n const pending: Array<{ index: number; text: string; key: string }> = [];\n for (let i = 0; i < texts.length; i++) {\n classifyEntry({\n index: i,\n text: texts[i] ?? \"\",\n model,\n dimension,\n cache,\n stats,\n results,\n pending,\n });\n }\n await embedInBoundedBatches(input, pending, results);\n return results.map((v) => v ?? new Array(dimension).fill(0));\n}\n\ninterface ClassifyEntryArgs {\n index: number;\n text: string;\n model: string;\n dimension: number;\n cache: NonNullable<CreateAdapterOptions[\"cache\"]>;\n stats: EmbeddingRuntimeStats;\n results: Array<number[] | undefined>;\n pending: Array<{ index: number; text: string; key: string }>;\n}\n\nfunction classifyEntry(args: ClassifyEntryArgs): void {\n if (args.text.trim().length === 0) {\n args.results[args.index] = new Array(args.dimension).fill(0);\n return;\n }\n const key = hashKey(args.model, args.text);\n const cached = args.cache.get(key);\n if (cached !== undefined) {\n args.stats.cacheHits += 1;\n args.results[args.index] = cached;\n return;\n }\n args.stats.cacheMisses += 1;\n args.pending.push({ index: args.index, text: args.text, key });\n}\n\n// T4.3 — max concurrent embedding API requests per embed() call.\n// 3 is enough to saturate most embedding APIs without triggering\n// rate limits (each batch is already up to MAX_BATCH=100 texts).\nconst MAX_CONCURRENT_BATCHES = 3;\n\n/**\n * T4.3 — parallel embed batches (DR4 finding #3).\n *\n * Pre-T4.3 this was a serial `for` loop — each batch of 100 texts\n * awaited before the next HTTP request started. With 500 texts the\n * serial path took 5 × RTT; parallel takes max(RTT) × ceil(5/3).\n *\n * Uses bounded-concurrency `Promise.all` capped at 3 concurrent\n * HTTP requests to avoid overwhelming the embedding API.\n */\nasync function embedInBoundedBatches(\n input: EmbedTextsInput,\n pending: ReadonlyArray<{ index: number; text: string; key: string }>,\n results: Array<number[] | undefined>,\n): Promise<void> {\n const batches: Array<ReadonlyArray<{ index: number; text: string; key: string }>> = [];\n for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {\n batches.push(pending.slice(offset, offset + MAX_BATCH));\n }\n // M0-2: bounded parallel via the shared ordered pool (was an inline\n // acquire/release clone — see plan m0-foundation-expose-primitives).\n await mapWithConcurrency(batches, MAX_CONCURRENT_BATCHES, (batch) =>\n processBatch(input, batch, results),\n );\n}\n\nasync function processBatch(\n input: EmbedTextsInput,\n batch: ReadonlyArray<{ index: number; text: string; key: string }>,\n results: Array<number[] | undefined>,\n): Promise<void> {\n const vectors = await embedBatch({\n apiKey: input.apiKey,\n baseUrl: input.baseUrl,\n embeddingsPath: input.embeddingsPath,\n model: input.model,\n inputs: batch.map((b) => b.text),\n fetchImpl: input.fetchImpl,\n stats: input.stats,\n providerId: input.providerId,\n dialect: input.dialect,\n });\n for (let j = 0; j < batch.length; j++) {\n const slot = batch[j];\n const vector = vectors[j];\n if (slot === undefined || vector === undefined) continue;\n results[slot.index] = vector;\n input.cache.set(slot.key, vector);\n }\n}\n\ninterface BatchOptions {\n apiKey: string;\n baseUrl: string;\n embeddingsPath: string;\n model: string;\n inputs: ReadonlyArray<string>;\n fetchImpl: typeof fetch;\n stats: EmbeddingRuntimeStats;\n providerId: string;\n dialect: EmbeddingDialect;\n}\n\nasync function embedBatch(opts: BatchOptions): Promise<number[][]> {\n // EC-2: embeddingsPath REPLACES the suffix; never concatenates.\n const url = `${opts.baseUrl.replace(/\\/$/, \"\")}${opts.embeddingsPath}`;\n let attempt = 0;\n while (true) {\n opts.stats.httpCalls += 1;\n const response = await postEmbedRequest(opts, url);\n if (response.ok)\n return await parseEmbedResponse(response, opts.providerId, opts.embeddingsPath, opts.dialect);\n if (isRetryable(response.status) && attempt < MAX_RETRIES) {\n attempt += 1;\n opts.stats.retries += 1;\n await sleep(linearBackoffMs(attempt));\n continue;\n }\n // Read body (best-effort) so the mapper has access to provider error code.\n const text = await response.text().catch(() => \"\");\n let body: unknown = text;\n try {\n body = JSON.parse(text);\n } catch {\n // not JSON — keep as string\n }\n throw mapOpenAICompatibleError({\n providerId: opts.providerId,\n status: response.status,\n body,\n headers: response.headers,\n endpoint: opts.embeddingsPath,\n });\n }\n}\n\nasync function postEmbedRequest(opts: BatchOptions, url: string): Promise<Response> {\n return opts.fetchImpl(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(opts.dialect.authHeaders?.(opts.apiKey) ?? { authorization: `Bearer ${opts.apiKey}` }),\n },\n body: JSON.stringify(\n opts.dialect.body?.(opts.model, opts.inputs) ?? { model: opts.model, input: opts.inputs },\n ),\n });\n}\n\nasync function parseEmbedResponse(\n response: Response,\n providerId: string,\n endpoint: string,\n dialect?: EmbeddingDialect,\n): Promise<number[][]> {\n const json = (await response.json()) as { data?: Array<{ embedding: number[] }> };\n // theokit#159 — a provider whose response is not OpenAI-shaped supplies its own reader.\n const viaDialect = dialect?.vectors?.(json);\n if (viaDialect !== undefined) return viaDialect;\n if (!Array.isArray(json.data)) {\n throw new NetworkError(`${providerId} ${endpoint} returned no data`, {\n code: \"embedding_invalid_response\",\n metadata: {\n provider: providerId,\n endpoint,\n code: \"invalid_request\",\n raw: json,\n },\n });\n }\n return json.data.map((d) => d.embedding);\n}\n\nfunction isRetryable(status: number): boolean {\n return status === 429 || (status >= 500 && status < 600);\n}\n\nfunction hashKey(model: string, text: string): string {\n return createHash(\"sha256\").update(`${model} ${text}`).digest(\"hex\");\n}\n\nfunction linearBackoffMs(attempt: number): number {\n return 50 * attempt;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { atomicWriteText } from './chunk-
|
|
2
|
-
import { InvalidTaskIdError } from './chunk-
|
|
3
|
-
import { diag } from './chunk-
|
|
1
|
+
import { atomicWriteText } from './chunk-2GUYQR5P.js';
|
|
2
|
+
import { InvalidTaskIdError } from './chunk-ZZMZWBR6.js';
|
|
3
|
+
import { diag } from './chunk-XEQ3TM66.js';
|
|
4
4
|
import { mkdirSync, readdirSync } from 'fs';
|
|
5
5
|
import { readFile, unlink } from 'fs/promises';
|
|
6
6
|
import { join } from 'path';
|
|
@@ -225,5 +225,5 @@ function getTaskStoreFor(options) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
export { InMemoryTaskStore, JsonFileTaskStore, TASK_RESERVED_PREFIXES, getTaskStoreFor, isValidTaskId };
|
|
228
|
-
//# sourceMappingURL=chunk-
|
|
229
|
-
//# sourceMappingURL=chunk-
|
|
228
|
+
//# sourceMappingURL=chunk-KQRW2GPZ.js.map
|
|
229
|
+
//# sourceMappingURL=chunk-KQRW2GPZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types/task.ts","../src/internal/task/store.ts"],"names":[],"mappings":"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","file":"chunk-RUDY2GTT.js","sourcesContent":["/**\n * Public type contract for the Task observability registry (ADRs D361-D374).\n *\n * Tasks are an opt-in observability layer over async work in the SDK\n * (`Agent.send`, `Agent.batch`, `Workflow.run`, `Cron` fires). They have\n * a closed 5-state lifecycle (D362), discriminated events (D366), and a\n * pluggable store (D364). The `Task` facade in `task.ts` is the public\n * surface; consumers import these types from `@theokit/sdk`.\n *\n * @public\n */\n\n/**\n * Closed enum of the 5 lifecycle states (D362).\n *\n * Transitions are acyclic:\n * queued → running → (finished | error | cancelled)\n * queued → cancelled (direct, no run started)\n */\nexport type TaskState = \"queued\" | \"running\" | \"finished\" | \"error\" | \"cancelled\";\n\n/** Discriminator of the runtime that produced a task (D374). */\nexport type TaskKind = \"run\" | \"batch\" | \"workflow\" | \"cron\" | \"custom\";\n\n/** Discriminated union of task lifecycle events (D366). */\nexport type TaskEvent =\n | {\n readonly type: \"submitted\";\n readonly taskId: string;\n readonly kind: TaskKind;\n readonly submittedAt: number;\n readonly meta?: Record<string, unknown>;\n /** D372 — flag set on the first yielded event when the ring buffer was at cap. */\n readonly truncated?: boolean;\n }\n | { readonly type: \"started\"; readonly taskId: string; readonly startedAt: number }\n | {\n readonly type: \"progress\";\n readonly taskId: string;\n readonly at: number;\n readonly payload: unknown;\n }\n | {\n readonly type: \"finished\";\n readonly taskId: string;\n readonly finishedAt: number;\n readonly result: unknown;\n }\n | {\n readonly type: \"errored\";\n readonly taskId: string;\n readonly erroredAt: number;\n readonly error: { readonly code: string; readonly message: string };\n }\n | {\n readonly type: \"cancelled\";\n readonly taskId: string;\n readonly cancelledAt: number;\n readonly reason?: string;\n };\n\n/** Public read-only view of a task entry in the registry. */\nexport interface TaskHandle {\n readonly id: string;\n readonly kind: TaskKind;\n readonly state: TaskState;\n readonly submittedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly cancelledAt?: number;\n readonly erroredAt?: number;\n readonly result?: unknown;\n readonly error?: { readonly code: string; readonly message: string };\n readonly meta?: Record<string, unknown>;\n /**\n * EC-7 — cross-process best-effort cancel flag. Set by CLI\n * `theokit tasks cancel` via JsonFileTaskStore. The owning process\n * polls at checkpoints and honors via AbortController. Always\n * `undefined` for in-process cancel paths (which go directly through\n * AbortController).\n */\n readonly cancelRequested?: boolean;\n /**\n * Why the task was cancelled, when whoever cancelled it said. Free text, written alongside\n * `cancelledAt` for a `queued` task and alongside `cancelRequested` for a `running` one.\n *\n * Absent when no reason was given — an empty string is not a reason, and a default like\n * \"cancelled\" would put words in the operator's mouth. `theokit tasks cancel --reason` is what\n * writes it (#351); nothing in the runtime reads it, because it exists for the human reading the\n * registry later.\n */\n readonly cancelReason?: string;\n}\n\n/** Query filter for `Task.list`. */\nexport interface TaskFilter {\n readonly state?: TaskState | readonly TaskState[];\n readonly kind?: TaskKind | readonly TaskKind[];\n readonly submittedAfter?: number;\n readonly submittedBefore?: number;\n /**\n * Defaults to 100. `JsonFileTaskStore` returns the newest matching tasks first and reads its\n * whole directory to find them (256 files at a time); page past this limit with\n * `submittedBefore` (#362).\n */\n readonly limit?: number;\n}\n\n/** Options for `Task.submit`. */\nexport interface TaskSubmitOptions {\n /**\n * Optional user-supplied ID. Must match grammar `^[a-z0-9][a-z0-9_-]*$`\n * and MUST NOT start with the reserved prefixes `wf-` / `b-` / `cron-`\n * (D368, EC-5). When omitted, `crypto.randomUUID()` is used.\n */\n readonly id?: string;\n readonly meta?: Record<string, unknown>;\n /**\n * Optional caller-provided AbortSignal. If already aborted at submit\n * time, the registry short-circuits to `cancelled` without acquiring\n * a semaphore slot (EC-4).\n */\n readonly signal?: AbortSignal;\n /**\n * SE2 — an opt-in {@link import(\"./run-events.js\").RunEventSink}. The task's\n * lifecycle is forwarded to it as typed `task_started` / `task_updated` /\n * `task_completed` RunEvents, so a caller that spawned the task can observe it\n * through the same `onRunEvent` channel as the rest of the run. Best-effort (a\n * throwing sink never breaks the task). The dedicated `Task.subscribe(id)`\n * channel is unaffected.\n */\n readonly onRunEvent?: import(\"./run-events.js\").RunEventSink;\n}\n\n/** Options shape for `TaskStore` factory (D364). */\nexport type TaskStoreOptions =\n | { readonly backend: \"memory\" }\n | { readonly backend: \"json\"; readonly dir: string };\n\n/** Result of `Task.cancel` (D365 — idempotent). */\nexport interface TaskCancelResult {\n readonly cancelled: boolean;\n readonly alreadyTerminal: boolean;\n}\n\n/**\n * Grammar for user-supplied task IDs (D368).\n * `crypto.randomUUID()` outputs do NOT match this (UUIDs have dashes\n * AND uppercase letters in their canonical form on some Node versions),\n * but the registry normalizes auto-generated IDs to lowercase before\n * insertion, so they pass the same regex.\n */\nconst TASK_ID_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\n/** Reserved prefixes for adapter-generated IDs (D368, EC-5). */\nconst RESERVED_PREFIXES = [\"wf-\", \"b-\", \"cron-\"] as const;\n\n/**\n * Validates a task ID against the public grammar + reserved prefixes.\n * Throws `InvalidTaskIdError` from `../errors.js` on rejection.\n *\n * Adapter callers (workflow/batch/cron) MUST set `allowReserved: true`\n * to register their own IDs; user-facing surfaces (`Task.submit`,\n * `agent.send({ task: { id } })`) leave it false.\n */\nexport function isValidTaskId(id: string, allowReserved: boolean): boolean {\n if (!TASK_ID_GRAMMAR.test(id)) return false;\n if (allowReserved) return true;\n for (const prefix of RESERVED_PREFIXES) {\n if (id.startsWith(prefix)) return false;\n }\n return true;\n}\n\n/** Re-exported for adapter implementations + tests. */\nexport const TASK_RESERVED_PREFIXES: readonly string[] = RESERVED_PREFIXES;\n","/**\n * `TaskStore` interface + 2 implementations (ADR D364).\n *\n * - `InMemoryTaskStore` — default, transient, single-process.\n * - `JsonFileTaskStore` — opt-in, one JSON file per task under a\n * dedicated dir; single-process invariant (EC-15 documented).\n *\n * Edge cases absorbed:\n * - EC-1: constructor auto-creates dir (mkdirSync recursive idempotent).\n * - EC-2: every method validates the task id against the public\n * grammar (D368) BEFORE doing any path I/O — path-traversal defense.\n * - EC-6: `list()` returns `[]` on ENOENT (fresh install path).\n * - EC-8: `list()` skips `.tmp.*` orphan files left by interrupted\n * atomic writes.\n * - EC-14 (DOCUMENT): `list()` JSDoc documents the 256-row hard cap.\n * It no longer advertises `submittedBefore` as a way to page past it:\n * the cap is applied to the raw directory listing before any filter, so\n * paging beyond 256 files is not reachable through this interface.\n * - EC-15 (DOCUMENT): `JsonFileTaskStore` JSDoc documents the\n * single-process invariant; v0.2 SQLite covers cross-process.\n *\n * @internal\n */\n\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { InvalidTaskIdError } from \"../../errors.js\";\nimport {\n isValidTaskId,\n type TaskFilter,\n type TaskHandle,\n type TaskState,\n type TaskStoreOptions,\n} from \"../../types/task.js\";\nimport { diag } from \"../diagnostics.js\";\nimport { atomicWriteText } from \"../persistence/atomic-write.js\";\n\nconst JSON_LOAD_CAP = 256;\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Storage interface used by `TaskRegistry`. */\nexport interface TaskStore {\n insert(handle: TaskHandle): Promise<void>;\n update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined>;\n get(id: string): Promise<TaskHandle | undefined>;\n /**\n * Returns at most `filter.limit ?? 100` matching handles.\n *\n * `JsonFileTaskStore` returns them newest-first and considers every task in its\n * directory, reading at most 256 files concurrently (#362 — that number bounds\n * I/O, not which tasks are visible). Walk past `limit` by passing the oldest\n * `submittedAt` of a page as the next call's `submittedBefore`; the ordering is\n * what makes that a cursor rather than a re-roll of an arbitrary subset.\n * `InMemoryTaskStore` applies the same filters but does not order its result.\n */\n list(filter: TaskFilter): Promise<TaskHandle[]>;\n delete(id: string): Promise<boolean>;\n /** Removes terminal handles whose terminal-timestamp is older than `epochMs`. */\n evictTerminalOlderThan(epochMs: number): Promise<number>;\n}\n\nfunction assertValidIdForStore(id: string): void {\n if (!isValidTaskId(id, /* allowReserved */ true)) {\n throw new InvalidTaskIdError(`store rejects invalid task id: ${id}`, id);\n }\n}\n\nfunction terminalTimestamp(h: TaskHandle): number | undefined {\n if (h.state === \"finished\") return h.finishedAt;\n if (h.state === \"error\") return h.erroredAt;\n if (h.state === \"cancelled\") return h.cancelledAt;\n return undefined;\n}\n\nfunction isTerminal(state: TaskState): boolean {\n return state === \"finished\" || state === \"error\" || state === \"cancelled\";\n}\n\nfunction matchesState(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.state === undefined) return true;\n const states = Array.isArray(filter.state) ? filter.state : [filter.state];\n return states.includes(h.state);\n}\nfunction matchesKind(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.kind === undefined) return true;\n const kinds = Array.isArray(filter.kind) ? filter.kind : [filter.kind];\n return kinds.includes(h.kind);\n}\nfunction matchesTime(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.submittedAfter !== undefined && h.submittedAt <= filter.submittedAfter) return false;\n if (filter.submittedBefore !== undefined && h.submittedAt >= filter.submittedBefore) return false;\n return true;\n}\nfunction matchesFilter(h: TaskHandle, filter: TaskFilter): boolean {\n return matchesState(h, filter) && matchesKind(h, filter) && matchesTime(h, filter);\n}\n\n/** The states `evictTerminalOlderThan` considers terminal. */\nconst TERMINAL_STATES: readonly TaskState[] = [\"finished\", \"error\", \"cancelled\"];\n\n/**\n * Where `submittedAt` belongs in a newest-first list — the first index whose entry is older.\n *\n * Linear rather than binary: the list is bounded by the caller's `limit` (100 by default) and the\n * scan runs once per MATCHING handle, so the comparison count is dwarfed by the file read that\n * produced the handle. A binary search here would trade a real cost for an imagined one.\n */\nfunction insertionIndex(newestFirst: readonly TaskHandle[], submittedAt: number): number {\n for (let i = 0; i < newestFirst.length; i++) {\n const entry = newestFirst[i];\n if (entry !== undefined && entry.submittedAt < submittedAt) return i;\n }\n return newestFirst.length;\n}\n\nfunction applyFilter(values: Iterable<TaskHandle>, filter: TaskFilter): TaskHandle[] {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n const out: TaskHandle[] = [];\n for (const h of values) {\n if (matchesFilter(h, filter)) {\n out.push(h);\n if (out.length >= limit) break;\n }\n }\n return out;\n}\n\n/* ─── InMemory ─── */\n\n/**\n * Default `TaskStore` — a plain `Map` living in the current process.\n *\n * Nothing reaches disk and nothing survives the process, so a task recorded here\n * is invisible to a reader in another process (`theokit tasks`). Select it with\n * `getTaskStoreFor({ backend: \"memory\" })`.\n *\n * How it fails: every id-taking method validates against the task-id grammar\n * (`^[a-z0-9][a-z0-9_-]*$`; reserved `wf-`/`b-`/`cron-` prefixes are permitted\n * here) and throws `InvalidTaskIdError` before touching the map. A missing id is\n * reported by returning `undefined` (`get`, `update`) or `false` (`delete`), never\n * by throwing.\n *\n * Traps:\n * - `insert()` is a `Map.set`: an existing id is overwritten silently, not\n * rejected.\n * - `list()` walks in INSERTION order and stops as soon as `filter.limit ?? 100`\n * matches are collected. It never sorts, so the limit truncates an\n * oldest-first scan rather than returning the most recent tasks.\n * - Nothing evicts on its own; terminal handles accumulate until a caller invokes\n * `evictTerminalOlderThan`.\n */\nexport class InMemoryTaskStore implements TaskStore {\n private readonly map = new Map<string, TaskHandle>();\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n this.map.set(handle.id, handle);\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = this.map.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n this.map.set(id, next);\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n return this.map.get(id);\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n return applyFilter(this.map.values(), filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n return this.map.delete(id);\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n let count = 0;\n for (const [id, h] of this.map.entries()) {\n if (!isTerminal(h.state)) continue;\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n this.map.delete(id);\n count++;\n }\n }\n return count;\n }\n}\n\n/* ─── JsonFile (opt-in) ───\n *\n * IMPORTANT (EC-15): `JsonFileTaskStore` is **single-process**.\n * Concurrent writers from multiple Node processes against the same\n * directory may corrupt entries (atomic-write protects per-file, but\n * the `list()` scan + `update()` read-modify-write cycle is not\n * serialised across processes). Use only when the `TaskRegistry`\n * runs in exactly one process. v0.2 will add a SQLite backend with\n * the same interface for cross-process scenarios (D364 + D61).\n */\n/**\n * Opt-in `TaskStore` that keeps one JSON file per task under `dir`, so another\n * process can read the registry off disk.\n *\n * const store = new JsonFileTaskStore(\"/var/lib/theokit/tasks\");\n *\n * Two calls are synchronous, and both are deliberate: the constructor creates `dir`\n * recursively (`mkdirSync`), and `list()` scans it with `readdirSync` before awaiting\n * anything. Every other operation is async — `get` uses `readFile`, `delete` uses\n * `unlink`, and every write goes through an atomic temp-file-plus-rename.\n *\n * SINGLE-PROCESS ONLY — see the note above. Per-file atomicity does not make\n * `update()` safe: it is a read-modify-write, so two writers that both `get`\n * before either writes lose one of the two mutations. That is true within one\n * process as well as across processes.\n *\n * How it fails:\n * - An invalid id throws `InvalidTaskIdError` BEFORE any path is constructed.\n * That ordering is the path-traversal defence, and it is why ids are validated\n * rather than escaped.\n * - A missing task is `undefined` from `get` and `false` from `delete`.\n * - A file that exists but does not parse is NOT surfaced as an error: `get`\n * writes to the diagnostics channel and returns `undefined`, so a corrupted\n * task also silently vanishes from `list()`.\n * - `list()` returns `[]` when `dir` does not exist; any other `readdir` failure\n * (for example a permission error) throws.\n *\n * Traps:\n * - `list()` reads every task file in the directory (256 at a time) to apply the\n * filter, because `state`, `kind` and `submittedAt` live inside the files. On a\n * large directory that is a lot of I/O for one call; the memory it holds is\n * bounded by `limit`, the time is not.\n * - `list()` is `async`, but its directory scan is not: `readdirSync` blocks the event\n * loop for the whole listing before the first `await`. Only the per-file reads that\n * follow it are concurrent.\n * - Files whose name contains `.tmp` are skipped as orphans of an interrupted\n * write, and nothing ever removes them.\n */\nexport class JsonFileTaskStore implements TaskStore {\n constructor(private readonly dir: string) {\n // EC-1: idempotent mkdir; ignore EEXIST.\n try {\n mkdirSync(dir, { recursive: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n\n private filePath(id: string): string {\n return join(this.dir, `${id}.json`);\n }\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n await atomicWriteText(this.filePath(handle.id), JSON.stringify(handle));\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = await this.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n await atomicWriteText(this.filePath(id), JSON.stringify(next));\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n try {\n const raw = await readFile(this.filePath(id), \"utf8\");\n return JSON.parse(raw) as TaskHandle;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return undefined;\n // Corrupt JSON or other I/O error — log + degrade gracefully (D50/EC-7 cache pattern).\n diag(`[task-store] failed to read ${id}: ${(err as Error).message}\\n`);\n return undefined;\n }\n }\n\n /**\n * Task ids backed by a file in `dir`, in readdir order.\n *\n * EC-8: orphan `.tmp` files left by an interrupted atomic write are skipped, as is any name\n * outside the id grammar. EC-6: a missing directory lists as empty; any other `readdir` failure\n * (a permission error, say) throws rather than being reported as \"no tasks\".\n */\n private candidateIds(): string[] {\n let entries: string[];\n try {\n entries = readdirSync(this.dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return []; // EC-6\n throw err;\n }\n return entries\n .filter((name) => name.endsWith(\".json\") && !name.includes(\".tmp\"))\n .map((name) => name.slice(0, -\".json\".length))\n .filter((id) => isValidTaskId(id, true));\n }\n\n /**\n * Read every task in `ids`, handing each loaded handle to `visit`, at most `JSON_LOAD_CAP` file\n * reads in flight.\n *\n * #362 — the cap used to be applied to the directory listing, which made it a bound on WHICH\n * tasks were visible rather than on how much I/O ran at once. Every filter then ran on that\n * arbitrary readdir-ordered prefix. As a concurrency bound it does the job it was named for\n * without deciding what the caller gets to see; the caller's own bound on memory is whatever\n * `visit` chooses to retain.\n */\n private async forEachHandle(\n ids: readonly string[],\n visit: (handle: TaskHandle) => void | Promise<void>,\n ): Promise<void> {\n for (let start = 0; start < ids.length; start += JSON_LOAD_CAP) {\n const batch = ids.slice(start, start + JSON_LOAD_CAP);\n const loaded = await Promise.all(batch.map(async (id) => this.get(id)));\n for (const handle of loaded) {\n if (handle !== undefined) await visit(handle);\n }\n }\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n // Newest-first, keeping at most `limit` handles in memory regardless of how many match. The\n // order is what makes `submittedBefore` a real cursor: with the previous readdir order, a page\n // was an arbitrary subset, so a caller walking the timeline skipped every task that happened\n // to fall outside its window and could never come back for them.\n const newest: TaskHandle[] = [];\n await this.forEachHandle(this.candidateIds(), (handle) => {\n if (!matchesFilter(handle, filter)) return;\n const at = insertionIndex(newest, handle.submittedAt);\n if (at >= limit) return; // older than everything already held, and the page is full\n newest.splice(at, 0, handle);\n if (newest.length > limit) newest.pop();\n });\n return newest;\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n try {\n await unlink(this.filePath(id));\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n // #362 — eviction scans the directory itself rather than borrowing `list()`. Sharing that path\n // made one call incomplete twice over: it saw only a 256-file window, and it wanted the OLDEST\n // handles while `list()` is ordered newest-first — so on a large directory it could delete\n // nothing while thousands of eligible handles sat just outside the page. Sweeping every\n // candidate is what lets one call mean \"everything eligible is gone\".\n let count = 0;\n await this.forEachHandle(this.candidateIds(), async (handle) => {\n if (!TERMINAL_STATES.includes(handle.state)) return;\n const ts = terminalTimestamp(handle);\n if (ts === undefined || ts >= epochMs) return;\n if (await this.delete(handle.id)) count++;\n });\n return count;\n }\n}\n\n/** Factory used by `TaskRegistry.configure` (D364). */\nexport function getTaskStoreFor(options: TaskStoreOptions): TaskStore {\n if (options.backend === \"memory\") return new InMemoryTaskStore();\n return new JsonFileTaskStore(options.dir);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types/task.ts","../src/internal/task/store.ts"],"names":[],"mappings":"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","file":"chunk-KQRW2GPZ.js","sourcesContent":["/**\n * Public type contract for the Task observability registry (ADRs D361-D374).\n *\n * Tasks are an opt-in observability layer over async work in the SDK\n * (`Agent.send`, `Agent.batch`, `Workflow.run`, `Cron` fires). They have\n * a closed 5-state lifecycle (D362), discriminated events (D366), and a\n * pluggable store (D364). The `Task` facade in `task.ts` is the public\n * surface; consumers import these types from `@theokit/sdk`.\n *\n * @public\n */\n\n/**\n * Closed enum of the 5 lifecycle states (D362).\n *\n * Transitions are acyclic:\n * queued → running → (finished | error | cancelled)\n * queued → cancelled (direct, no run started)\n */\nexport type TaskState = \"queued\" | \"running\" | \"finished\" | \"error\" | \"cancelled\";\n\n/** Discriminator of the runtime that produced a task (D374). */\nexport type TaskKind = \"run\" | \"batch\" | \"workflow\" | \"cron\" | \"custom\";\n\n/** Discriminated union of task lifecycle events (D366). */\nexport type TaskEvent =\n | {\n readonly type: \"submitted\";\n readonly taskId: string;\n readonly kind: TaskKind;\n readonly submittedAt: number;\n readonly meta?: Record<string, unknown>;\n /** D372 — flag set on the first yielded event when the ring buffer was at cap. */\n readonly truncated?: boolean;\n }\n | { readonly type: \"started\"; readonly taskId: string; readonly startedAt: number }\n | {\n readonly type: \"progress\";\n readonly taskId: string;\n readonly at: number;\n readonly payload: unknown;\n }\n | {\n readonly type: \"finished\";\n readonly taskId: string;\n readonly finishedAt: number;\n readonly result: unknown;\n }\n | {\n readonly type: \"errored\";\n readonly taskId: string;\n readonly erroredAt: number;\n readonly error: { readonly code: string; readonly message: string };\n }\n | {\n readonly type: \"cancelled\";\n readonly taskId: string;\n readonly cancelledAt: number;\n readonly reason?: string;\n };\n\n/** Public read-only view of a task entry in the registry. */\nexport interface TaskHandle {\n readonly id: string;\n readonly kind: TaskKind;\n readonly state: TaskState;\n readonly submittedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly cancelledAt?: number;\n readonly erroredAt?: number;\n readonly result?: unknown;\n readonly error?: { readonly code: string; readonly message: string };\n readonly meta?: Record<string, unknown>;\n /**\n * EC-7 — cross-process best-effort cancel flag. Set by CLI\n * `theokit tasks cancel` via JsonFileTaskStore. The owning process\n * polls at checkpoints and honors via AbortController. Always\n * `undefined` for in-process cancel paths (which go directly through\n * AbortController).\n */\n readonly cancelRequested?: boolean;\n /**\n * Why the task was cancelled, when whoever cancelled it said. Free text, written alongside\n * `cancelledAt` for a `queued` task and alongside `cancelRequested` for a `running` one.\n *\n * Absent when no reason was given — an empty string is not a reason, and a default like\n * \"cancelled\" would put words in the operator's mouth. `theokit tasks cancel --reason` is what\n * writes it (#351); nothing in the runtime reads it, because it exists for the human reading the\n * registry later.\n */\n readonly cancelReason?: string;\n}\n\n/** Query filter for `Task.list`. */\nexport interface TaskFilter {\n readonly state?: TaskState | readonly TaskState[];\n readonly kind?: TaskKind | readonly TaskKind[];\n readonly submittedAfter?: number;\n readonly submittedBefore?: number;\n /**\n * Defaults to 100. `JsonFileTaskStore` returns the newest matching tasks first and reads its\n * whole directory to find them (256 files at a time); page past this limit with\n * `submittedBefore` (#362).\n */\n readonly limit?: number;\n}\n\n/** Options for `Task.submit`. */\nexport interface TaskSubmitOptions {\n /**\n * Optional user-supplied ID. Must match grammar `^[a-z0-9][a-z0-9_-]*$`\n * and MUST NOT start with the reserved prefixes `wf-` / `b-` / `cron-`\n * (D368, EC-5). When omitted, `crypto.randomUUID()` is used.\n */\n readonly id?: string;\n readonly meta?: Record<string, unknown>;\n /**\n * Optional caller-provided AbortSignal. If already aborted at submit\n * time, the registry short-circuits to `cancelled` without acquiring\n * a semaphore slot (EC-4).\n */\n readonly signal?: AbortSignal;\n /**\n * SE2 — an opt-in {@link import(\"./run-events.js\").RunEventSink}. The task's\n * lifecycle is forwarded to it as typed `task_started` / `task_updated` /\n * `task_completed` RunEvents, so a caller that spawned the task can observe it\n * through the same `onRunEvent` channel as the rest of the run. Best-effort (a\n * throwing sink never breaks the task). The dedicated `Task.subscribe(id)`\n * channel is unaffected.\n */\n readonly onRunEvent?: import(\"./run-events.js\").RunEventSink;\n}\n\n/** Options shape for `TaskStore` factory (D364). */\nexport type TaskStoreOptions =\n | { readonly backend: \"memory\" }\n | { readonly backend: \"json\"; readonly dir: string };\n\n/** Result of `Task.cancel` (D365 — idempotent). */\nexport interface TaskCancelResult {\n readonly cancelled: boolean;\n readonly alreadyTerminal: boolean;\n}\n\n/**\n * Grammar for user-supplied task IDs (D368).\n * `crypto.randomUUID()` outputs do NOT match this (UUIDs have dashes\n * AND uppercase letters in their canonical form on some Node versions),\n * but the registry normalizes auto-generated IDs to lowercase before\n * insertion, so they pass the same regex.\n */\nconst TASK_ID_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\n/** Reserved prefixes for adapter-generated IDs (D368, EC-5). */\nconst RESERVED_PREFIXES = [\"wf-\", \"b-\", \"cron-\"] as const;\n\n/**\n * Validates a task ID against the public grammar + reserved prefixes.\n * Throws `InvalidTaskIdError` from `../errors.js` on rejection.\n *\n * Adapter callers (workflow/batch/cron) MUST set `allowReserved: true`\n * to register their own IDs; user-facing surfaces (`Task.submit`,\n * `agent.send({ task: { id } })`) leave it false.\n */\nexport function isValidTaskId(id: string, allowReserved: boolean): boolean {\n if (!TASK_ID_GRAMMAR.test(id)) return false;\n if (allowReserved) return true;\n for (const prefix of RESERVED_PREFIXES) {\n if (id.startsWith(prefix)) return false;\n }\n return true;\n}\n\n/** Re-exported for adapter implementations + tests. */\nexport const TASK_RESERVED_PREFIXES: readonly string[] = RESERVED_PREFIXES;\n","/**\n * `TaskStore` interface + 2 implementations (ADR D364).\n *\n * - `InMemoryTaskStore` — default, transient, single-process.\n * - `JsonFileTaskStore` — opt-in, one JSON file per task under a\n * dedicated dir; single-process invariant (EC-15 documented).\n *\n * Edge cases absorbed:\n * - EC-1: constructor auto-creates dir (mkdirSync recursive idempotent).\n * - EC-2: every method validates the task id against the public\n * grammar (D368) BEFORE doing any path I/O — path-traversal defense.\n * - EC-6: `list()` returns `[]` on ENOENT (fresh install path).\n * - EC-8: `list()` skips `.tmp.*` orphan files left by interrupted\n * atomic writes.\n * - EC-14 (DOCUMENT): `list()` JSDoc documents the 256-row hard cap.\n * It no longer advertises `submittedBefore` as a way to page past it:\n * the cap is applied to the raw directory listing before any filter, so\n * paging beyond 256 files is not reachable through this interface.\n * - EC-15 (DOCUMENT): `JsonFileTaskStore` JSDoc documents the\n * single-process invariant; v0.2 SQLite covers cross-process.\n *\n * @internal\n */\n\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { InvalidTaskIdError } from \"../../errors.js\";\nimport {\n isValidTaskId,\n type TaskFilter,\n type TaskHandle,\n type TaskState,\n type TaskStoreOptions,\n} from \"../../types/task.js\";\nimport { diag } from \"../diagnostics.js\";\nimport { atomicWriteText } from \"../persistence/atomic-write.js\";\n\nconst JSON_LOAD_CAP = 256;\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Storage interface used by `TaskRegistry`. */\nexport interface TaskStore {\n insert(handle: TaskHandle): Promise<void>;\n update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined>;\n get(id: string): Promise<TaskHandle | undefined>;\n /**\n * Returns at most `filter.limit ?? 100` matching handles.\n *\n * `JsonFileTaskStore` returns them newest-first and considers every task in its\n * directory, reading at most 256 files concurrently (#362 — that number bounds\n * I/O, not which tasks are visible). Walk past `limit` by passing the oldest\n * `submittedAt` of a page as the next call's `submittedBefore`; the ordering is\n * what makes that a cursor rather than a re-roll of an arbitrary subset.\n * `InMemoryTaskStore` applies the same filters but does not order its result.\n */\n list(filter: TaskFilter): Promise<TaskHandle[]>;\n delete(id: string): Promise<boolean>;\n /** Removes terminal handles whose terminal-timestamp is older than `epochMs`. */\n evictTerminalOlderThan(epochMs: number): Promise<number>;\n}\n\nfunction assertValidIdForStore(id: string): void {\n if (!isValidTaskId(id, /* allowReserved */ true)) {\n throw new InvalidTaskIdError(`store rejects invalid task id: ${id}`, id);\n }\n}\n\nfunction terminalTimestamp(h: TaskHandle): number | undefined {\n if (h.state === \"finished\") return h.finishedAt;\n if (h.state === \"error\") return h.erroredAt;\n if (h.state === \"cancelled\") return h.cancelledAt;\n return undefined;\n}\n\nfunction isTerminal(state: TaskState): boolean {\n return state === \"finished\" || state === \"error\" || state === \"cancelled\";\n}\n\nfunction matchesState(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.state === undefined) return true;\n const states = Array.isArray(filter.state) ? filter.state : [filter.state];\n return states.includes(h.state);\n}\nfunction matchesKind(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.kind === undefined) return true;\n const kinds = Array.isArray(filter.kind) ? filter.kind : [filter.kind];\n return kinds.includes(h.kind);\n}\nfunction matchesTime(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.submittedAfter !== undefined && h.submittedAt <= filter.submittedAfter) return false;\n if (filter.submittedBefore !== undefined && h.submittedAt >= filter.submittedBefore) return false;\n return true;\n}\nfunction matchesFilter(h: TaskHandle, filter: TaskFilter): boolean {\n return matchesState(h, filter) && matchesKind(h, filter) && matchesTime(h, filter);\n}\n\n/** The states `evictTerminalOlderThan` considers terminal. */\nconst TERMINAL_STATES: readonly TaskState[] = [\"finished\", \"error\", \"cancelled\"];\n\n/**\n * Where `submittedAt` belongs in a newest-first list — the first index whose entry is older.\n *\n * Linear rather than binary: the list is bounded by the caller's `limit` (100 by default) and the\n * scan runs once per MATCHING handle, so the comparison count is dwarfed by the file read that\n * produced the handle. A binary search here would trade a real cost for an imagined one.\n */\nfunction insertionIndex(newestFirst: readonly TaskHandle[], submittedAt: number): number {\n for (let i = 0; i < newestFirst.length; i++) {\n const entry = newestFirst[i];\n if (entry !== undefined && entry.submittedAt < submittedAt) return i;\n }\n return newestFirst.length;\n}\n\nfunction applyFilter(values: Iterable<TaskHandle>, filter: TaskFilter): TaskHandle[] {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n const out: TaskHandle[] = [];\n for (const h of values) {\n if (matchesFilter(h, filter)) {\n out.push(h);\n if (out.length >= limit) break;\n }\n }\n return out;\n}\n\n/* ─── InMemory ─── */\n\n/**\n * Default `TaskStore` — a plain `Map` living in the current process.\n *\n * Nothing reaches disk and nothing survives the process, so a task recorded here\n * is invisible to a reader in another process (`theokit tasks`). Select it with\n * `getTaskStoreFor({ backend: \"memory\" })`.\n *\n * How it fails: every id-taking method validates against the task-id grammar\n * (`^[a-z0-9][a-z0-9_-]*$`; reserved `wf-`/`b-`/`cron-` prefixes are permitted\n * here) and throws `InvalidTaskIdError` before touching the map. A missing id is\n * reported by returning `undefined` (`get`, `update`) or `false` (`delete`), never\n * by throwing.\n *\n * Traps:\n * - `insert()` is a `Map.set`: an existing id is overwritten silently, not\n * rejected.\n * - `list()` walks in INSERTION order and stops as soon as `filter.limit ?? 100`\n * matches are collected. It never sorts, so the limit truncates an\n * oldest-first scan rather than returning the most recent tasks.\n * - Nothing evicts on its own; terminal handles accumulate until a caller invokes\n * `evictTerminalOlderThan`.\n */\nexport class InMemoryTaskStore implements TaskStore {\n private readonly map = new Map<string, TaskHandle>();\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n this.map.set(handle.id, handle);\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = this.map.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n this.map.set(id, next);\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n return this.map.get(id);\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n return applyFilter(this.map.values(), filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n return this.map.delete(id);\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n let count = 0;\n for (const [id, h] of this.map.entries()) {\n if (!isTerminal(h.state)) continue;\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n this.map.delete(id);\n count++;\n }\n }\n return count;\n }\n}\n\n/* ─── JsonFile (opt-in) ───\n *\n * IMPORTANT (EC-15): `JsonFileTaskStore` is **single-process**.\n * Concurrent writers from multiple Node processes against the same\n * directory may corrupt entries (atomic-write protects per-file, but\n * the `list()` scan + `update()` read-modify-write cycle is not\n * serialised across processes). Use only when the `TaskRegistry`\n * runs in exactly one process. v0.2 will add a SQLite backend with\n * the same interface for cross-process scenarios (D364 + D61).\n */\n/**\n * Opt-in `TaskStore` that keeps one JSON file per task under `dir`, so another\n * process can read the registry off disk.\n *\n * const store = new JsonFileTaskStore(\"/var/lib/theokit/tasks\");\n *\n * Two calls are synchronous, and both are deliberate: the constructor creates `dir`\n * recursively (`mkdirSync`), and `list()` scans it with `readdirSync` before awaiting\n * anything. Every other operation is async — `get` uses `readFile`, `delete` uses\n * `unlink`, and every write goes through an atomic temp-file-plus-rename.\n *\n * SINGLE-PROCESS ONLY — see the note above. Per-file atomicity does not make\n * `update()` safe: it is a read-modify-write, so two writers that both `get`\n * before either writes lose one of the two mutations. That is true within one\n * process as well as across processes.\n *\n * How it fails:\n * - An invalid id throws `InvalidTaskIdError` BEFORE any path is constructed.\n * That ordering is the path-traversal defence, and it is why ids are validated\n * rather than escaped.\n * - A missing task is `undefined` from `get` and `false` from `delete`.\n * - A file that exists but does not parse is NOT surfaced as an error: `get`\n * writes to the diagnostics channel and returns `undefined`, so a corrupted\n * task also silently vanishes from `list()`.\n * - `list()` returns `[]` when `dir` does not exist; any other `readdir` failure\n * (for example a permission error) throws.\n *\n * Traps:\n * - `list()` reads every task file in the directory (256 at a time) to apply the\n * filter, because `state`, `kind` and `submittedAt` live inside the files. On a\n * large directory that is a lot of I/O for one call; the memory it holds is\n * bounded by `limit`, the time is not.\n * - `list()` is `async`, but its directory scan is not: `readdirSync` blocks the event\n * loop for the whole listing before the first `await`. Only the per-file reads that\n * follow it are concurrent.\n * - Files whose name contains `.tmp` are skipped as orphans of an interrupted\n * write, and nothing ever removes them.\n */\nexport class JsonFileTaskStore implements TaskStore {\n constructor(private readonly dir: string) {\n // EC-1: idempotent mkdir; ignore EEXIST.\n try {\n mkdirSync(dir, { recursive: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n\n private filePath(id: string): string {\n return join(this.dir, `${id}.json`);\n }\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n await atomicWriteText(this.filePath(handle.id), JSON.stringify(handle));\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = await this.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n await atomicWriteText(this.filePath(id), JSON.stringify(next));\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n try {\n const raw = await readFile(this.filePath(id), \"utf8\");\n return JSON.parse(raw) as TaskHandle;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return undefined;\n // Corrupt JSON or other I/O error — log + degrade gracefully (D50/EC-7 cache pattern).\n diag(`[task-store] failed to read ${id}: ${(err as Error).message}\\n`);\n return undefined;\n }\n }\n\n /**\n * Task ids backed by a file in `dir`, in readdir order.\n *\n * EC-8: orphan `.tmp` files left by an interrupted atomic write are skipped, as is any name\n * outside the id grammar. EC-6: a missing directory lists as empty; any other `readdir` failure\n * (a permission error, say) throws rather than being reported as \"no tasks\".\n */\n private candidateIds(): string[] {\n let entries: string[];\n try {\n entries = readdirSync(this.dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return []; // EC-6\n throw err;\n }\n return entries\n .filter((name) => name.endsWith(\".json\") && !name.includes(\".tmp\"))\n .map((name) => name.slice(0, -\".json\".length))\n .filter((id) => isValidTaskId(id, true));\n }\n\n /**\n * Read every task in `ids`, handing each loaded handle to `visit`, at most `JSON_LOAD_CAP` file\n * reads in flight.\n *\n * #362 — the cap used to be applied to the directory listing, which made it a bound on WHICH\n * tasks were visible rather than on how much I/O ran at once. Every filter then ran on that\n * arbitrary readdir-ordered prefix. As a concurrency bound it does the job it was named for\n * without deciding what the caller gets to see; the caller's own bound on memory is whatever\n * `visit` chooses to retain.\n */\n private async forEachHandle(\n ids: readonly string[],\n visit: (handle: TaskHandle) => void | Promise<void>,\n ): Promise<void> {\n for (let start = 0; start < ids.length; start += JSON_LOAD_CAP) {\n const batch = ids.slice(start, start + JSON_LOAD_CAP);\n const loaded = await Promise.all(batch.map(async (id) => this.get(id)));\n for (const handle of loaded) {\n if (handle !== undefined) await visit(handle);\n }\n }\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n // Newest-first, keeping at most `limit` handles in memory regardless of how many match. The\n // order is what makes `submittedBefore` a real cursor: with the previous readdir order, a page\n // was an arbitrary subset, so a caller walking the timeline skipped every task that happened\n // to fall outside its window and could never come back for them.\n const newest: TaskHandle[] = [];\n await this.forEachHandle(this.candidateIds(), (handle) => {\n if (!matchesFilter(handle, filter)) return;\n const at = insertionIndex(newest, handle.submittedAt);\n if (at >= limit) return; // older than everything already held, and the page is full\n newest.splice(at, 0, handle);\n if (newest.length > limit) newest.pop();\n });\n return newest;\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n try {\n await unlink(this.filePath(id));\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n // #362 — eviction scans the directory itself rather than borrowing `list()`. Sharing that path\n // made one call incomplete twice over: it saw only a 256-file window, and it wanted the OLDEST\n // handles while `list()` is ordered newest-first — so on a large directory it could delete\n // nothing while thousands of eligible handles sat just outside the page. Sweeping every\n // candidate is what lets one call mean \"everything eligible is gone\".\n let count = 0;\n await this.forEachHandle(this.candidateIds(), async (handle) => {\n if (!TERMINAL_STATES.includes(handle.state)) return;\n const ts = terminalTimestamp(handle);\n if (ts === undefined || ts >= epochMs) return;\n if (await this.delete(handle.id)) count++;\n });\n return count;\n }\n}\n\n/** Factory used by `TaskRegistry.configure` (D364). */\nexport function getTaskStoreFor(options: TaskStoreOptions): TaskStore {\n if (options.backend === \"memory\") return new InMemoryTaskStore();\n return new JsonFileTaskStore(options.dir);\n}\n"]}
|