@theokit/sdk 5.15.0 → 5.15.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.
Files changed (368) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/a2a/index.cjs +3 -3
  3. package/dist/a2a/index.js +1 -1
  4. package/dist/{agent-B6FQad6P.d.cts → agent-BL4kxgwq.d.cts} +7 -1
  5. package/dist/{agent-B6C9i_qu.d.ts → agent-ByDx7K30.d.ts} +7 -1
  6. package/dist/agent-D6WBBNGR.cjs +72 -0
  7. package/dist/{agent-VI2HITPT.cjs.map → agent-D6WBBNGR.cjs.map} +1 -1
  8. package/dist/agent-QRIFX2SY.js +63 -0
  9. package/dist/{agent-DRDGUU6T.js.map → agent-QRIFX2SY.js.map} +1 -1
  10. package/dist/agent-session-store-2LQEPS4H.js +6 -0
  11. package/dist/{agent-session-store-22GKA7O4.js.map → agent-session-store-2LQEPS4H.js.map} +1 -1
  12. package/dist/agent-session-store-IOH6BREF.cjs +23 -0
  13. package/dist/{agent-session-store-WP6NGDVX.cjs.map → agent-session-store-IOH6BREF.cjs.map} +1 -1
  14. package/dist/auth/index.cjs +15 -15
  15. package/dist/auth/index.js +3 -3
  16. package/dist/{batch-N2XDYZED.js → batch-7IGV2OKA.js} +8 -8
  17. package/dist/{batch-N2XDYZED.js.map → batch-7IGV2OKA.js.map} +1 -1
  18. package/dist/{batch-VFR2MAUE.cjs → batch-FIWQZ3JD.cjs} +14 -14
  19. package/dist/{batch-VFR2MAUE.cjs.map → batch-FIWQZ3JD.cjs.map} +1 -1
  20. package/dist/{chunk-AT2PESXB.cjs → chunk-2UYAL4N7.cjs} +5 -5
  21. package/dist/chunk-2UYAL4N7.cjs.map +1 -0
  22. package/dist/{chunk-K2BQQ445.js → chunk-3GQYMKVJ.js} +3 -3
  23. package/dist/{chunk-K2BQQ445.js.map → chunk-3GQYMKVJ.js.map} +1 -1
  24. package/dist/{chunk-YSTXFWQE.cjs → chunk-3HL43XBD.cjs} +4 -4
  25. package/dist/{chunk-YSTXFWQE.cjs.map → chunk-3HL43XBD.cjs.map} +1 -1
  26. package/dist/{chunk-XESYJ4JC.js → chunk-3VX7JHJI.js} +7 -7
  27. package/dist/{chunk-XESYJ4JC.js.map → chunk-3VX7JHJI.js.map} +1 -1
  28. package/dist/{chunk-6XQLTQB6.cjs → chunk-3YCNBOX7.cjs} +3 -3
  29. package/dist/{chunk-6XQLTQB6.cjs.map → chunk-3YCNBOX7.cjs.map} +1 -1
  30. package/dist/{chunk-CHKV2L2Y.cjs → chunk-46YYJIHL.cjs} +30 -30
  31. package/dist/chunk-46YYJIHL.cjs.map +1 -0
  32. package/dist/{chunk-73IITAF7.js → chunk-4C37ELQL.js} +9 -9
  33. package/dist/{chunk-73IITAF7.js.map → chunk-4C37ELQL.js.map} +1 -1
  34. package/dist/{chunk-AGO5S5WD.js → chunk-4E4D6H77.js} +3 -3
  35. package/dist/{chunk-AGO5S5WD.js.map → chunk-4E4D6H77.js.map} +1 -1
  36. package/dist/{chunk-R35ZJAYT.cjs → chunk-4JF52NED.cjs} +12 -12
  37. package/dist/{chunk-R35ZJAYT.cjs.map → chunk-4JF52NED.cjs.map} +1 -1
  38. package/dist/{chunk-INVEGVGO.js → chunk-4TA47IK4.js} +8 -8
  39. package/dist/{chunk-INVEGVGO.js.map → chunk-4TA47IK4.js.map} +1 -1
  40. package/dist/{chunk-MLGETTYX.js → chunk-4YUIYSZO.js} +6 -6
  41. package/dist/{chunk-MLGETTYX.js.map → chunk-4YUIYSZO.js.map} +1 -1
  42. package/dist/{chunk-RZL2W4C3.js → chunk-5L7S2VOL.js} +5 -5
  43. package/dist/{chunk-RZL2W4C3.js.map → chunk-5L7S2VOL.js.map} +1 -1
  44. package/dist/{chunk-3WKWBYPO.cjs → chunk-6EHOJLJ3.cjs} +20 -20
  45. package/dist/chunk-6EHOJLJ3.cjs.map +1 -0
  46. package/dist/{chunk-XOU3CL7S.cjs → chunk-6HQFPX7N.cjs} +28 -28
  47. package/dist/chunk-6HQFPX7N.cjs.map +1 -0
  48. package/dist/{chunk-DJKGDLYZ.cjs → chunk-6XT5LAZG.cjs} +126 -59
  49. package/dist/chunk-6XT5LAZG.cjs.map +1 -0
  50. package/dist/{chunk-R427EOZE.cjs → chunk-7EFT6N5C.cjs} +13 -13
  51. package/dist/{chunk-R427EOZE.cjs.map → chunk-7EFT6N5C.cjs.map} +1 -1
  52. package/dist/{chunk-L7V4DDIE.cjs → chunk-7VNFOGRD.cjs} +12 -12
  53. package/dist/chunk-7VNFOGRD.cjs.map +1 -0
  54. package/dist/{chunk-YKNBTHMD.js → chunk-AQ23NII7.js} +3 -3
  55. package/dist/{chunk-YKNBTHMD.js.map → chunk-AQ23NII7.js.map} +1 -1
  56. package/dist/{chunk-ARC2V7NY.js → chunk-ATGSV4GS.js} +4 -4
  57. package/dist/{chunk-ARC2V7NY.js.map → chunk-ATGSV4GS.js.map} +1 -1
  58. package/dist/{chunk-H7HD6RXO.cjs → chunk-AW72G4TC.cjs} +5 -5
  59. package/dist/{chunk-H7HD6RXO.cjs.map → chunk-AW72G4TC.cjs.map} +1 -1
  60. package/dist/{chunk-M4U24U6G.js → chunk-AZK3QM36.js} +5 -5
  61. package/dist/{chunk-M4U24U6G.js.map → chunk-AZK3QM36.js.map} +1 -1
  62. package/dist/{chunk-QK2ERTO3.js → chunk-BH6M2DGV.js} +4 -4
  63. package/dist/{chunk-QK2ERTO3.js.map → chunk-BH6M2DGV.js.map} +1 -1
  64. package/dist/{chunk-64ECM3XM.cjs → chunk-BTGYRM4J.cjs} +264 -264
  65. package/dist/chunk-BTGYRM4J.cjs.map +1 -0
  66. package/dist/{chunk-5M5GQYBM.js → chunk-CG64VPPU.js} +50 -50
  67. package/dist/{chunk-5M5GQYBM.js.map → chunk-CG64VPPU.js.map} +1 -1
  68. package/dist/{chunk-CBBT42VX.js → chunk-CMDM4VF6.js} +3 -3
  69. package/dist/{chunk-CBBT42VX.js.map → chunk-CMDM4VF6.js.map} +1 -1
  70. package/dist/{chunk-OLJOI4LK.js → chunk-CN732Z5C.js} +4 -4
  71. package/dist/{chunk-OLJOI4LK.js.map → chunk-CN732Z5C.js.map} +1 -1
  72. package/dist/{chunk-SETHRLQS.cjs → chunk-D3BL3EEB.cjs} +9 -9
  73. package/dist/chunk-D3BL3EEB.cjs.map +1 -0
  74. package/dist/{chunk-LCCT5ZXJ.js → chunk-DB5RYCXX.js} +13 -13
  75. package/dist/{chunk-LCCT5ZXJ.js.map → chunk-DB5RYCXX.js.map} +1 -1
  76. package/dist/{chunk-SV63WTA2.js → chunk-DBH2WA2U.js} +4 -4
  77. package/dist/{chunk-SV63WTA2.js.map → chunk-DBH2WA2U.js.map} +1 -1
  78. package/dist/{chunk-XE3CAY6Z.cjs → chunk-DEHPRORL.cjs} +4 -4
  79. package/dist/{chunk-XE3CAY6Z.cjs.map → chunk-DEHPRORL.cjs.map} +1 -1
  80. package/dist/{chunk-UKMBRMGT.js → chunk-DGO4LPLP.js} +4 -4
  81. package/dist/{chunk-UKMBRMGT.js.map → chunk-DGO4LPLP.js.map} +1 -1
  82. package/dist/{chunk-RS7YQR4X.cjs → chunk-EBX6XUN7.cjs} +7 -7
  83. package/dist/{chunk-RS7YQR4X.cjs.map → chunk-EBX6XUN7.cjs.map} +1 -1
  84. package/dist/chunk-EDIYR4VR.cjs +18 -0
  85. package/dist/{chunk-SPEN7RJU.cjs.map → chunk-EDIYR4VR.cjs.map} +1 -1
  86. package/dist/{chunk-QGDPMM3C.js → chunk-EF5IONLL.js} +5 -5
  87. package/dist/{chunk-QGDPMM3C.js.map → chunk-EF5IONLL.js.map} +1 -1
  88. package/dist/{chunk-MSW7LB2V.cjs → chunk-EOWIUDKG.cjs} +5 -5
  89. package/dist/{chunk-MSW7LB2V.cjs.map → chunk-EOWIUDKG.cjs.map} +1 -1
  90. package/dist/{chunk-L7JPXZ2C.cjs → chunk-ER3BF23R.cjs} +19 -19
  91. package/dist/{chunk-L7JPXZ2C.cjs.map → chunk-ER3BF23R.cjs.map} +1 -1
  92. package/dist/{chunk-BNEUB52I.cjs → chunk-ERBKPA2N.cjs} +4 -4
  93. package/dist/{chunk-BNEUB52I.cjs.map → chunk-ERBKPA2N.cjs.map} +1 -1
  94. package/dist/{chunk-4QCJAJAG.js → chunk-F54PXZIA.js} +5 -5
  95. package/dist/{chunk-4QCJAJAG.js.map → chunk-F54PXZIA.js.map} +1 -1
  96. package/dist/{chunk-2XWAYCFM.cjs → chunk-FBT2SF2S.cjs} +12 -12
  97. package/dist/{chunk-2XWAYCFM.cjs.map → chunk-FBT2SF2S.cjs.map} +1 -1
  98. package/dist/{chunk-AUH2EVWY.cjs → chunk-FZSPVIFK.cjs} +16 -16
  99. package/dist/chunk-FZSPVIFK.cjs.map +1 -0
  100. package/dist/{chunk-CYWOJA6B.js → chunk-GA27FFT5.js} +3 -3
  101. package/dist/{chunk-CYWOJA6B.js.map → chunk-GA27FFT5.js.map} +1 -1
  102. package/dist/{chunk-HWMGR3HA.js → chunk-GEQLHBJM.js} +6 -6
  103. package/dist/{chunk-HWMGR3HA.js.map → chunk-GEQLHBJM.js.map} +1 -1
  104. package/dist/{chunk-IJ7M6GOG.cjs → chunk-GKERJ52A.cjs} +4 -4
  105. package/dist/{chunk-IJ7M6GOG.cjs.map → chunk-GKERJ52A.cjs.map} +1 -1
  106. package/dist/{chunk-NEH3BPFD.cjs → chunk-HI5TJWQE.cjs} +19 -19
  107. package/dist/{chunk-NEH3BPFD.cjs.map → chunk-HI5TJWQE.cjs.map} +1 -1
  108. package/dist/{chunk-ZOFRNNZD.cjs → chunk-IZV72OEI.cjs} +11 -11
  109. package/dist/chunk-IZV72OEI.cjs.map +1 -0
  110. package/dist/{chunk-KU5O7WPK.js → chunk-JD3EKKKB.js} +4 -4
  111. package/dist/{chunk-KU5O7WPK.js.map → chunk-JD3EKKKB.js.map} +1 -1
  112. package/dist/{chunk-DFBWYETS.js → chunk-JRP2W2DR.js} +3 -3
  113. package/dist/{chunk-DFBWYETS.js.map → chunk-JRP2W2DR.js.map} +1 -1
  114. package/dist/{chunk-IOBHFP2E.js → chunk-JRQJMZKY.js} +82 -15
  115. package/dist/chunk-JRQJMZKY.js.map +1 -0
  116. package/dist/{chunk-YQ3XC6C2.cjs → chunk-JVH3NNJ3.cjs} +13 -13
  117. package/dist/chunk-JVH3NNJ3.cjs.map +1 -0
  118. package/dist/{chunk-VWYFGE27.cjs → chunk-KC2HKUPT.cjs} +56 -56
  119. package/dist/chunk-KC2HKUPT.cjs.map +1 -0
  120. package/dist/{chunk-PDOXYXWX.cjs → chunk-KDJTECE6.cjs} +4 -4
  121. package/dist/{chunk-PDOXYXWX.cjs.map → chunk-KDJTECE6.cjs.map} +1 -1
  122. package/dist/{chunk-QD6B6W6I.js → chunk-L2KC2G7H.js} +5 -5
  123. package/dist/{chunk-QD6B6W6I.js.map → chunk-L2KC2G7H.js.map} +1 -1
  124. package/dist/{chunk-3BBTHFGJ.js → chunk-L5B7EWWH.js} +5 -5
  125. package/dist/{chunk-3BBTHFGJ.js.map → chunk-L5B7EWWH.js.map} +1 -1
  126. package/dist/{chunk-X7CE7Y5W.js → chunk-LG4JCJEI.js} +11 -11
  127. package/dist/{chunk-X7CE7Y5W.js.map → chunk-LG4JCJEI.js.map} +1 -1
  128. package/dist/{chunk-2HXTNVLM.js → chunk-LSURFRJA.js} +6 -6
  129. package/dist/{chunk-2HXTNVLM.js.map → chunk-LSURFRJA.js.map} +1 -1
  130. package/dist/{chunk-55LLQFHW.cjs → chunk-MJGY3E3C.cjs} +4 -4
  131. package/dist/{chunk-55LLQFHW.cjs.map → chunk-MJGY3E3C.cjs.map} +1 -1
  132. package/dist/{chunk-EQ5IDL6W.cjs → chunk-MKS5FFH5.cjs} +15 -15
  133. package/dist/chunk-MKS5FFH5.cjs.map +1 -0
  134. package/dist/{chunk-WM646HEN.js → chunk-MTNCJ7MQ.js} +6 -6
  135. package/dist/{chunk-WM646HEN.js.map → chunk-MTNCJ7MQ.js.map} +1 -1
  136. package/dist/{chunk-SA6K24NB.cjs → chunk-MXJJMP4U.cjs} +7 -7
  137. package/dist/chunk-MXJJMP4U.cjs.map +1 -0
  138. package/dist/{chunk-FTXY4D44.cjs → chunk-N2ORTDSO.cjs} +20 -20
  139. package/dist/chunk-N2ORTDSO.cjs.map +1 -0
  140. package/dist/{chunk-7YUPSBDV.cjs → chunk-NDXZXGVP.cjs} +6 -6
  141. package/dist/{chunk-7YUPSBDV.cjs.map → chunk-NDXZXGVP.cjs.map} +1 -1
  142. package/dist/{chunk-3UMPP553.cjs → chunk-NMRNE6QX.cjs} +11 -11
  143. package/dist/{chunk-3UMPP553.cjs.map → chunk-NMRNE6QX.cjs.map} +1 -1
  144. package/dist/{chunk-PKRPV3R7.js → chunk-NO6AG22P.js} +6 -6
  145. package/dist/{chunk-PKRPV3R7.js.map → chunk-NO6AG22P.js.map} +1 -1
  146. package/dist/{chunk-NK5EOTDY.js → chunk-OIOSY5G6.js} +3 -3
  147. package/dist/{chunk-NK5EOTDY.js.map → chunk-OIOSY5G6.js.map} +1 -1
  148. package/dist/{chunk-RGVMJU57.js → chunk-P7B64NFK.js} +8 -8
  149. package/dist/{chunk-RGVMJU57.js.map → chunk-P7B64NFK.js.map} +1 -1
  150. package/dist/{chunk-TDED5XES.js → chunk-PCPYTBVV.js} +6 -6
  151. package/dist/chunk-PCPYTBVV.js.map +1 -0
  152. package/dist/{chunk-U7PRI3XM.cjs → chunk-PDFSLD5P.cjs} +6 -6
  153. package/dist/{chunk-U7PRI3XM.cjs.map → chunk-PDFSLD5P.cjs.map} +1 -1
  154. package/dist/{chunk-YMVTUV5K.js → chunk-PISZRLES.js} +5 -5
  155. package/dist/{chunk-YMVTUV5K.js.map → chunk-PISZRLES.js.map} +1 -1
  156. package/dist/{chunk-BUUUWQMB.cjs → chunk-PNL63WDP.cjs} +4 -4
  157. package/dist/{chunk-BUUUWQMB.cjs.map → chunk-PNL63WDP.cjs.map} +1 -1
  158. package/dist/{chunk-23SS4ID4.js → chunk-PSMWZYW6.js} +4 -4
  159. package/dist/{chunk-23SS4ID4.js.map → chunk-PSMWZYW6.js.map} +1 -1
  160. package/dist/{chunk-5645PMVU.cjs → chunk-PXYINWBC.cjs} +20 -20
  161. package/dist/chunk-PXYINWBC.cjs.map +1 -0
  162. package/dist/{chunk-RTDVSHJR.cjs → chunk-Q4UOCMAO.cjs} +5 -5
  163. package/dist/{chunk-RTDVSHJR.cjs.map → chunk-Q4UOCMAO.cjs.map} +1 -1
  164. package/dist/{chunk-5IRDK5BC.js → chunk-QH6MWGOA.js} +4 -4
  165. package/dist/{chunk-5IRDK5BC.js.map → chunk-QH6MWGOA.js.map} +1 -1
  166. package/dist/{chunk-3LMVJTNJ.js → chunk-QPUKIZOT.js} +3 -3
  167. package/dist/{chunk-3LMVJTNJ.js.map → chunk-QPUKIZOT.js.map} +1 -1
  168. package/dist/{chunk-SWALPLFQ.cjs → chunk-RTCO3USA.cjs} +16 -16
  169. package/dist/chunk-RTCO3USA.cjs.map +1 -0
  170. package/dist/{chunk-SAGRF4IB.js → chunk-S2CMEJ5F.js} +3 -3
  171. package/dist/{chunk-SAGRF4IB.js.map → chunk-S2CMEJ5F.js.map} +1 -1
  172. package/dist/{chunk-VZGE7NAI.js → chunk-S5ERB3CH.js} +3 -3
  173. package/dist/{chunk-VZGE7NAI.js.map → chunk-S5ERB3CH.js.map} +1 -1
  174. package/dist/{chunk-XJ3WU5CZ.js → chunk-S7IZEVXX.js} +6 -6
  175. package/dist/{chunk-XJ3WU5CZ.js.map → chunk-S7IZEVXX.js.map} +1 -1
  176. package/dist/{chunk-Q7UG242V.js → chunk-TIYQDJFK.js} +3 -3
  177. package/dist/{chunk-Q7UG242V.js.map → chunk-TIYQDJFK.js.map} +1 -1
  178. package/dist/{chunk-POBZXULR.cjs → chunk-TTNJOM5W.cjs} +16 -16
  179. package/dist/{chunk-POBZXULR.cjs.map → chunk-TTNJOM5W.cjs.map} +1 -1
  180. package/dist/{chunk-VCQ37TC5.js → chunk-TTXRNCXY.js} +3 -3
  181. package/dist/{chunk-VCQ37TC5.js.map → chunk-TTXRNCXY.js.map} +1 -1
  182. package/dist/{chunk-3HA7N2BB.cjs → chunk-TYKBAKAS.cjs} +5 -5
  183. package/dist/{chunk-3HA7N2BB.cjs.map → chunk-TYKBAKAS.cjs.map} +1 -1
  184. package/dist/{chunk-A77UMVMD.cjs → chunk-UMCCYXFZ.cjs} +8 -8
  185. package/dist/{chunk-A77UMVMD.cjs.map → chunk-UMCCYXFZ.cjs.map} +1 -1
  186. package/dist/{chunk-3GTN4HOO.js → chunk-UNP4NCMZ.js} +3 -3
  187. package/dist/{chunk-3GTN4HOO.js.map → chunk-UNP4NCMZ.js.map} +1 -1
  188. package/dist/{chunk-BPL5CVZ5.js → chunk-VFT63FJF.js} +3 -3
  189. package/dist/{chunk-BPL5CVZ5.js.map → chunk-VFT63FJF.js.map} +1 -1
  190. package/dist/{chunk-3KIUY67P.cjs → chunk-VHY6NVCJ.cjs} +30 -30
  191. package/dist/chunk-VHY6NVCJ.cjs.map +1 -0
  192. package/dist/{chunk-KMFBNKCT.cjs → chunk-VT3NWHFH.cjs} +6 -6
  193. package/dist/{chunk-KMFBNKCT.cjs.map → chunk-VT3NWHFH.cjs.map} +1 -1
  194. package/dist/{chunk-FJWQP6EE.js → chunk-WJ6OGKAO.js} +3 -3
  195. package/dist/{chunk-FJWQP6EE.js.map → chunk-WJ6OGKAO.js.map} +1 -1
  196. package/dist/{chunk-FLDHYF5C.cjs → chunk-X2XLSE2N.cjs} +9 -9
  197. package/dist/chunk-X2XLSE2N.cjs.map +1 -0
  198. package/dist/{chunk-UDAKAE4Z.cjs → chunk-X4ZYEZPD.cjs} +12 -12
  199. package/dist/chunk-X4ZYEZPD.cjs.map +1 -0
  200. package/dist/{chunk-QG4SXZ2A.cjs → chunk-XR6O6G3B.cjs} +27 -27
  201. package/dist/chunk-XR6O6G3B.cjs.map +1 -0
  202. package/dist/{chunk-BWYC7RZG.cjs → chunk-XTQWTVVI.cjs} +11 -11
  203. package/dist/chunk-XTQWTVVI.cjs.map +1 -0
  204. package/dist/{chunk-MLJDW2YK.js → chunk-YSGS57DA.js} +4 -4
  205. package/dist/{chunk-MLJDW2YK.js.map → chunk-YSGS57DA.js.map} +1 -1
  206. package/dist/{chunk-KCM3RMKR.js → chunk-YUQNHLKN.js} +8 -8
  207. package/dist/{chunk-KCM3RMKR.js.map → chunk-YUQNHLKN.js.map} +1 -1
  208. package/dist/{chunk-P7QYG4RZ.cjs → chunk-Z5MB2HAN.cjs} +8 -8
  209. package/dist/{chunk-P7QYG4RZ.cjs.map → chunk-Z5MB2HAN.cjs.map} +1 -1
  210. package/dist/{chunk-HJ2LV464.js → chunk-ZUWVO442.js} +4 -4
  211. package/dist/{chunk-HJ2LV464.js.map → chunk-ZUWVO442.js.map} +1 -1
  212. package/dist/{compact-session-NX7HUNTB.cjs → compact-session-5DWFHMW4.cjs} +21 -21
  213. package/dist/{compact-session-NX7HUNTB.cjs.map → compact-session-5DWFHMW4.cjs.map} +1 -1
  214. package/dist/{compact-session-Q5IGNFPW.js → compact-session-AL65PEMX.js} +12 -12
  215. package/dist/{compact-session-Q5IGNFPW.js.map → compact-session-AL65PEMX.js.map} +1 -1
  216. package/dist/context/index.cjs +11 -11
  217. package/dist/context/index.js +6 -6
  218. package/dist/context-4XSD2V3G.js +7 -0
  219. package/dist/{context-7EBZST3L.js.map → context-4XSD2V3G.js.map} +1 -1
  220. package/dist/context-ZX2VBGSX.cjs +24 -0
  221. package/dist/{context-4RSI3AXT.cjs.map → context-ZX2VBGSX.cjs.map} +1 -1
  222. package/dist/{cron-Bh3aWgQ2.d.cts → cron-B7KH9sHW.d.cts} +1 -1
  223. package/dist/{cron-CCDxeeFZ.d.ts → cron-FEO7UAQF.d.ts} +1 -1
  224. package/dist/cron.cjs +40 -40
  225. package/dist/cron.d.cts +2 -2
  226. package/dist/cron.d.ts +2 -2
  227. package/dist/cron.js +39 -39
  228. package/dist/eval.cjs +51 -51
  229. package/dist/eval.cjs.map +1 -1
  230. package/dist/eval.js +41 -41
  231. package/dist/{executor-THUXNAFR.js → executor-ZHIUSHRV.js} +7 -7
  232. package/dist/{executor-THUXNAFR.js.map → executor-ZHIUSHRV.js.map} +1 -1
  233. package/dist/{executor-5QL5AXIZ.cjs → executor-ZTLJZ5LK.cjs} +24 -24
  234. package/dist/{executor-5QL5AXIZ.cjs.map → executor-ZTLJZ5LK.cjs.map} +1 -1
  235. package/dist/filesystem/index.cjs +14 -14
  236. package/dist/filesystem/index.cjs.map +1 -1
  237. package/dist/filesystem/index.js +4 -4
  238. package/dist/{fork-agent-S2EPIML7.js → fork-agent-BHVTALT5.js} +3 -3
  239. package/dist/{fork-agent-S2EPIML7.js.map → fork-agent-BHVTALT5.js.map} +1 -1
  240. package/dist/{fork-agent-WAEQI2DL.cjs → fork-agent-DCOPPXM2.cjs} +4 -4
  241. package/dist/{fork-agent-WAEQI2DL.cjs.map → fork-agent-DCOPPXM2.cjs.map} +1 -1
  242. package/dist/fs-session-store-EZ32TFGB.js +11 -0
  243. package/dist/{fs-session-store-MYQEVUFL.js.map → fs-session-store-EZ32TFGB.js.map} +1 -1
  244. package/dist/fs-session-store-G664LDDN.cjs +20 -0
  245. package/dist/{fs-session-store-CYNTEPJZ.cjs.map → fs-session-store-G664LDDN.cjs.map} +1 -1
  246. package/dist/generate-object-JFZTK567.cjs +20 -0
  247. package/dist/{generate-object-A5F576IT.cjs.map → generate-object-JFZTK567.cjs.map} +1 -1
  248. package/dist/{generate-object-TBDOBBYW.js → generate-object-RPBXY2U3.js} +4 -4
  249. package/dist/{generate-object-TBDOBBYW.js.map → generate-object-RPBXY2U3.js.map} +1 -1
  250. package/dist/index-manager-2MJEEN2O.js +18 -0
  251. package/dist/{index-manager-NF2WRLOU.js.map → index-manager-2MJEEN2O.js.map} +1 -1
  252. package/dist/index-manager-CT6MMG7S.cjs +27 -0
  253. package/dist/{index-manager-2KH75BRW.cjs.map → index-manager-CT6MMG7S.cjs.map} +1 -1
  254. package/dist/index.cjs +130 -130
  255. package/dist/index.cjs.map +1 -1
  256. package/dist/index.d.cts +3 -3
  257. package/dist/index.d.ts +3 -3
  258. package/dist/index.js +56 -56
  259. package/dist/{inject-session-4ROYLZHY.js → inject-session-E2SZ4A35.js} +6 -6
  260. package/dist/{inject-session-4ROYLZHY.js.map → inject-session-E2SZ4A35.js.map} +1 -1
  261. package/dist/{inject-session-AWKDCGHV.cjs → inject-session-QZULG65O.cjs} +8 -8
  262. package/dist/{inject-session-AWKDCGHV.cjs.map → inject-session-QZULG65O.cjs.map} +1 -1
  263. package/dist/internal/llm/hermes-tool-extract.d.ts +2 -0
  264. package/dist/internal/memory/adapters/index.cjs +4 -4
  265. package/dist/internal/memory/adapters/index.js +3 -3
  266. package/dist/internal/memory/storage/index.cjs +45 -45
  267. package/dist/internal/memory/storage/index.js +9 -9
  268. package/dist/internal/persistence/index.cjs +22 -22
  269. package/dist/internal/persistence/index.js +6 -6
  270. package/dist/internal/security/index.cjs +9 -9
  271. package/dist/internal/security/index.js +2 -2
  272. package/dist/mcp-auth.cjs +8 -8
  273. package/dist/mcp-auth.js +3 -3
  274. package/dist/models.cjs +30 -30
  275. package/dist/models.cjs.map +1 -1
  276. package/dist/models.js +8 -8
  277. package/dist/{oauth-transaction-store-U34YLEGP.js → oauth-transaction-store-QQBWGNE2.js} +3 -3
  278. package/dist/{oauth-transaction-store-U34YLEGP.js.map → oauth-transaction-store-QQBWGNE2.js.map} +1 -1
  279. package/dist/{oauth-transaction-store-OMX436YB.cjs → oauth-transaction-store-YF7WOA6B.cjs} +10 -10
  280. package/dist/{oauth-transaction-store-OMX436YB.cjs.map → oauth-transaction-store-YF7WOA6B.cjs.map} +1 -1
  281. package/dist/path-safety.cjs +9 -9
  282. package/dist/path-safety.js +2 -2
  283. package/dist/persistence.cjs +41 -41
  284. package/dist/persistence.cjs.map +1 -1
  285. package/dist/persistence.js +11 -11
  286. package/dist/project.cjs +7 -7
  287. package/dist/project.cjs.map +1 -1
  288. package/dist/project.js +4 -4
  289. package/dist/providers.cjs +6 -6
  290. package/dist/providers.js +4 -4
  291. package/dist/{registry-5TDAF7RK.js → registry-DOYBR237.js} +6 -6
  292. package/dist/{registry-5TDAF7RK.js.map → registry-DOYBR237.js.map} +1 -1
  293. package/dist/{registry-W7TSCLQO.cjs → registry-PB7JFVUJ.cjs} +14 -14
  294. package/dist/{registry-W7TSCLQO.cjs.map → registry-PB7JFVUJ.cjs.map} +1 -1
  295. package/dist/sandbox/index.cjs +41 -41
  296. package/dist/sandbox/index.cjs.map +1 -1
  297. package/dist/sandbox/index.js +7 -7
  298. package/dist/sanitize/index.cjs +2 -2
  299. package/dist/sanitize/index.js +1 -1
  300. package/dist/server/auth/index.cjs +9 -9
  301. package/dist/server/auth/index.cjs.map +1 -1
  302. package/dist/server/auth/index.js +4 -4
  303. package/dist/{session-transcript-7TZ6HXSX.cjs → session-transcript-EBVGLZ6B.cjs} +17 -17
  304. package/dist/{session-transcript-7TZ6HXSX.cjs.map → session-transcript-EBVGLZ6B.cjs.map} +1 -1
  305. package/dist/{session-transcript-BSHZYYY6.js → session-transcript-GU2VE7JX.js} +4 -4
  306. package/dist/{session-transcript-BSHZYYY6.js.map → session-transcript-GU2VE7JX.js.map} +1 -1
  307. package/dist/skills.cjs +7 -7
  308. package/dist/skills.js +4 -4
  309. package/dist/stream-object-3AU7QDHT.js +7 -0
  310. package/dist/{stream-object-EKM4WGTI.js.map → stream-object-3AU7QDHT.js.map} +1 -1
  311. package/dist/stream-object-SLGR2KIA.cjs +20 -0
  312. package/dist/{stream-object-AS4YYM4B.cjs.map → stream-object-SLGR2KIA.cjs.map} +1 -1
  313. package/dist/subagents-loader-4PQS7ULK.cjs +16 -0
  314. package/dist/{subagents-loader-KLWF4QPD.cjs.map → subagents-loader-4PQS7ULK.cjs.map} +1 -1
  315. package/dist/subagents-loader-CPFVLWLH.js +7 -0
  316. package/dist/{subagents-loader-SXETS2F2.js.map → subagents-loader-CPFVLWLH.js.map} +1 -1
  317. package/dist/subagents-loader.cjs +4 -4
  318. package/dist/subagents-loader.d.cts +1 -1
  319. package/dist/subagents-loader.d.ts +1 -1
  320. package/dist/subagents-loader.js +3 -3
  321. package/dist/subagents.cjs +2 -2
  322. package/dist/subagents.js +1 -1
  323. package/dist/task-store.cjs +5 -5
  324. package/dist/task-store.js +2 -2
  325. package/dist/types/hooks.d.ts +7 -1
  326. package/dist/workflow.cjs +28 -28
  327. package/dist/workflow.js +5 -5
  328. package/package.json +1 -1
  329. package/dist/agent-DRDGUU6T.js +0 -63
  330. package/dist/agent-VI2HITPT.cjs +0 -72
  331. package/dist/agent-session-store-22GKA7O4.js +0 -6
  332. package/dist/agent-session-store-WP6NGDVX.cjs +0 -23
  333. package/dist/chunk-3KIUY67P.cjs.map +0 -1
  334. package/dist/chunk-3WKWBYPO.cjs.map +0 -1
  335. package/dist/chunk-5645PMVU.cjs.map +0 -1
  336. package/dist/chunk-64ECM3XM.cjs.map +0 -1
  337. package/dist/chunk-AT2PESXB.cjs.map +0 -1
  338. package/dist/chunk-AUH2EVWY.cjs.map +0 -1
  339. package/dist/chunk-BWYC7RZG.cjs.map +0 -1
  340. package/dist/chunk-CHKV2L2Y.cjs.map +0 -1
  341. package/dist/chunk-DJKGDLYZ.cjs.map +0 -1
  342. package/dist/chunk-EQ5IDL6W.cjs.map +0 -1
  343. package/dist/chunk-FLDHYF5C.cjs.map +0 -1
  344. package/dist/chunk-FTXY4D44.cjs.map +0 -1
  345. package/dist/chunk-IOBHFP2E.js.map +0 -1
  346. package/dist/chunk-L7V4DDIE.cjs.map +0 -1
  347. package/dist/chunk-QG4SXZ2A.cjs.map +0 -1
  348. package/dist/chunk-SA6K24NB.cjs.map +0 -1
  349. package/dist/chunk-SETHRLQS.cjs.map +0 -1
  350. package/dist/chunk-SPEN7RJU.cjs +0 -18
  351. package/dist/chunk-SWALPLFQ.cjs.map +0 -1
  352. package/dist/chunk-TDED5XES.js.map +0 -1
  353. package/dist/chunk-UDAKAE4Z.cjs.map +0 -1
  354. package/dist/chunk-VWYFGE27.cjs.map +0 -1
  355. package/dist/chunk-XOU3CL7S.cjs.map +0 -1
  356. package/dist/chunk-YQ3XC6C2.cjs.map +0 -1
  357. package/dist/chunk-ZOFRNNZD.cjs.map +0 -1
  358. package/dist/context-4RSI3AXT.cjs +0 -24
  359. package/dist/context-7EBZST3L.js +0 -7
  360. package/dist/fs-session-store-CYNTEPJZ.cjs +0 -20
  361. package/dist/fs-session-store-MYQEVUFL.js +0 -11
  362. package/dist/generate-object-A5F576IT.cjs +0 -20
  363. package/dist/index-manager-2KH75BRW.cjs +0 -27
  364. package/dist/index-manager-NF2WRLOU.js +0 -18
  365. package/dist/stream-object-AS4YYM4B.cjs +0 -20
  366. package/dist/stream-object-EKM4WGTI.js +0 -7
  367. package/dist/subagents-loader-KLWF4QPD.cjs +0 -16
  368. package/dist/subagents-loader-SXETS2F2.js +0 -7
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal/persistence/session-writer.ts"],"names":["TheokitAgentError","hostname","openSync","writeSync","fchmodSync","closeSync","readFileSync","existsSync","rmSync"],"mappings":"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","file":"chunk-EQ5IDL6W.cjs","sourcesContent":["/**\n * M81 — single-writer lease for a session transcript.\n *\n * ## The problem\n *\n * Nothing stops two processes appending to the same JSONL transcript. The concrete case: `exec\n * resume --last` can write into the TUI's live session. Two interleaved appends to an append-only\n * file produce lines that are each individually valid and whose SEQUENCE is fiction — and nothing\n * reports it, because every line parses.\n *\n * ## Why an exclusive lockfile rather than `withFileLock`\n *\n * The plan's ADR D2 said to compose `withFileLock`, and that was the right instinct — do not build a\n * second lock mechanism. It turned out not to fit the SHAPE: `withFileLock(path, fn)` is\n * scope-based — it holds the lock for the duration of a callback. A session lease is **held across\n * turns**, for as long as the process owns the session, with an explicit `release()`. Wrapping the\n * whole session lifetime in a callback would invert control of the entire agent loop.\n *\n * So this uses the same underlying primitive `withFileLock` uses (an exclusive-create lockfile,\n * `wx`) with lease semantics on top. That keeps the mechanism single — the file-existence lock —\n * while giving it the lifetime the caller needs. The deviation from D2 is recorded here because the\n * plan's rationale (no second mechanism) still holds; only its shape assumption did not.\n *\n * ## Fail fast, never wait\n *\n * A second writer that WAITED would block `exec` behind a TUI session that can last hours. The typed\n * error lets the caller choose: fork to a new id, or give up with a real diagnosis.\n *\n * @internal\n */\n\nimport {\n closeSync,\n existsSync,\n fchmodSync,\n openSync,\n readFileSync,\n rmSync,\n writeSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\n\nimport { TheokitAgentError } from \"../../errors.js\";\n\n/**\n * M81 — another process already holds the writer lease for this session.\n *\n * Carries `sessionPath` because knowing WHICH session is busy is what lets the caller decide between\n * forking and waiting for the user to close the TUI (`rules/error-handling.md § 2` — context enough\n * to act on).\n */\nexport class SessionBusyError extends TheokitAgentError {\n override readonly name = \"SessionBusyError\";\n\n constructor(readonly sessionPath: string) {\n super(\n `another process is already writing this session: ${sessionPath}. ` +\n \"Fork it to a new id instead of appending — two writers interleave lines into a sequence \" +\n \"that parses but is not what either process wrote.\",\n { code: \"session_busy\", isRetryable: false },\n );\n }\n}\n\n/** A held writer lease. `release()` is idempotent. */\nexport interface SessionWriterLease {\n readonly sessionPath: string;\n release(): Promise<void>;\n /**\n * Re-stamp the ownership record, so a **live** owner never crosses the staleness window.\n *\n * Idempotent and cheap: one `write` of a ~80-byte JSON. Call it on the path that already writes to\n * the session — appending a turn — and the cross-host window stops being a lie about liveness.\n *\n * A no-op after `release()`: renewing a lease you no longer hold would re-create the lock file and\n * hand this process ownership it gave up. That is the one direction of this API that could\n * *create* the double-writer it exists to prevent.\n */\n renew(): void;\n}\n\n/**\n * Staleness window **between machines** — and only between them.\n *\n * 30 s from ACQUISITION. Calling it a \"heartbeat\" would be a lie: the record is written once, when\n * the lease is taken, and is **not** renewed on every write. An earlier version of this comment\n * claimed \"the owner touches the file on every acquisition, so a live process never crosses the\n * window\" — false twice over, and adversarial review measured both.\n *\n * On the **same host** this does not matter: `reclaimable` decides by `pid`, which is exact, and age\n * never enters the calculation. Across hosts it does matter, and it is a real limit: a **live**\n * remote owner loses the lease after 30 s, because there is no way to ask another machine whether\n * its process still exists.\n *\n * **The residue is now closable by the caller** (`agent-builder#118`). `SessionWriterLease.renew()`\n * re-stamps the record; calling it on the path that already writes to the session — appending a turn\n * — keeps a live owner from ever crossing the window. It costs one `write` of ~80 bytes on a file\n * that was previously written once per session.\n *\n * It is `renew()` and not an internal timer on purpose. A timer inside the lease would keep the event\n * loop alive (or need `unref` plus its own teardown), and it would renew a lease belonging to a\n * process that is hung rather than working — which is precisely the state the window exists to\n * detect. Tying the renewal to a real write means the record tracks **progress**, not mere existence.\n */\nexport const HEARTBEAT_WINDOW_MS = 30_000;\n\n/** Who holds the lock. Written as JSON into `.writer.lock`. */\ninterface LockOwner {\n pid: number;\n hostname: string;\n mtime: number;\n}\n\n/** The ownership record for THIS process, stamped now. One place, so acquire and renew cannot drift. */\nfunction ownerNow(): LockOwner {\n return { pid: process.pid, hostname: hostname(), mtime: Date.now() };\n}\n\n/**\n * Writes the owner with user-only permissions.\n *\n * `0600` because the lock is an assertion of OWNERSHIP: with the `0664` the usual umask produces,\n * another user in the same group can overwrite the file and forge ownership of the session — and\n * from then on it is the legitimate owner who starts receiving `SessionBusyError`. The content\n * (`pid`, `hostname`) is low-sensitivity; what the permission protects is the signal's\n * **integrity**, not its secrecy.\n */\nfunction writeOwner(lockPath: string, owner: LockOwner): void {\n const fd = openSync(lockPath, \"w\", 0o600);\n try {\n writeSync(fd, JSON.stringify(owner));\n // `open`'s `mode` only applies on CREATION. A `.writer.lock` inherited from an earlier version —\n // or left behind by a process with a different umask — would stay `0664` after being reclaimed,\n // and the forgery window the mode closes for new locks would remain open for old ones.\n fchmodSync(fd, 0o600);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Reads the lock's owner. `undefined` when the file vanished or the content is unreadable. */\nfunction readOwner(lockPath: string): LockOwner | undefined {\n let raw: string;\n try {\n raw = readFileSync(lockPath, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n // `ENOENT` — vanished between the `EEXIST` and the read. Benign race: the lock is gone.\n if (code === \"ENOENT\") return undefined;\n // `EISDIR` — the lock path is a DIRECTORY. No process in this library creates one; it is debris\n // from something else, and it will never become a readable lock. Failing closed here would lock\n // the session out forever. Treated as owner-less: acquisition proceeds and fails with the real\n // FS error, which says what is wrong — instead of a permanent SessionBusyError that says\n // nothing. It is NOT \"reclaimable\": the lock is not removed, and the caller proceeds without a\n // lease.\n if (code === \"EISDIR\") return undefined;\n // Any other read failure (`EACCES` in a shared directory, `EIO`) differs in kind: the lock\n // **exists** and it is we who cannot read the owner. Treating it as free would let two writers\n // coexist — precisely what the lease exists to prevent — and the `0600` that protects the lock\n // against forgery WIDENS that surface: in a shared directory, another user's lock is unreadable\n // by design.\n //\n // Not knowing who the owner is differs from there being no owner. Fail closed.\n throw new SessionBusyError(lockPath.replace(/\\.writer\\.lock$/, \"\"));\n }\n try {\n const d = JSON.parse(raw) as Partial<LockOwner>;\n if (\n typeof d.pid !== \"number\" ||\n typeof d.hostname !== \"string\" ||\n typeof d.mtime !== \"number\"\n ) {\n return undefined;\n }\n return { pid: d.pid, hostname: d.hostname, mtime: d.mtime };\n } catch {\n // Unreadable JSON: a lock nobody can interpret must not lock the session out forever. Treating\n // it as stale is the recoverable choice; the cost is the same as that of an old lock.\n return undefined;\n }\n}\n\n/** Does the process exist? `signal 0` sends nothing — it only queries permission/existence. */\nfunction processAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM means it EXISTS and belongs to another user.\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\n/**\n * Can the lock be taken from whoever holds it?\n *\n * ADR-2 of the plan: reclaiming by `pid` alone has a false positive across machines — the same\n * number exists on another host, pointing at an unrelated process. So:\n *\n * - **same host:** the `pid` is authoritative, and it **alone**. Dead process => reclaimable at\n * once; live process => never, however old the lock is.\n * - **other host:** the `pid` says nothing here. Only the heartbeat window counts, because it is\n * the one signal that does not lie across machines.\n *\n * ## Why age does NOT count on the same host\n *\n * The first version did `stale || !processAlive(pid)`, and that was a serious defect: `mtime` is\n * written at **acquisition** and is not touched on each append, so any session lasting longer than\n * the window — that is, **every real session** — became stealable by another process. Two writers\n * on the same transcript is exactly what the lease exists to prevent.\n *\n * On the same host the question \"does the owner still exist?\" has an exact answer, and age adds no\n * information to it — only a way to be wrong. Keeping the window there would be heuristic layered\n * on top of a fact.\n */\nfunction reclaimable(owner: LockOwner | undefined): boolean {\n if (owner === undefined) return true; // unreadable or vanished\n if (owner.hostname !== hostname()) {\n return Date.now() - owner.mtime > HEARTBEAT_WINDOW_MS;\n }\n return !processAlive(owner.pid);\n}\n\n/**\n * Does the session have a writer **right now**? A query that does NOT take the lease.\n *\n * M95 — it exists because asking by taking creates the very contention it meant to detect: two\n * processes querying a **free** session at the same time made one of them lose, and the consumer\n * forked for no reason. Measured in adversarial review: `RACE: spurious forks = 1`.\n *\n * It is a snapshot, not a guarantee: between the query and the real acquisition someone may take\n * the session. Callers needing the guarantee use {@link acquireSessionWriter}; callers needing to\n * **decide an id before opening anything** use this, and handle the race where it shows up.\n *\n */\nexport function sessionHasWriter(sessionPath: string): boolean {\n const lockPath = `${sessionPath}.writer.lock`;\n if (!existsSync(lockPath)) return false;\n try {\n return !reclaimable(readOwner(lockPath));\n } catch {\n // `readOwner` throws when the lock exists and cannot be read — fail closed, for the same reason\n // as there: not knowing who the owner is differs from there being no owner.\n return true;\n }\n}\n\n/**\n * Take the exclusive writer lease for `sessionPath`, or reject with {@link SessionBusyError}.\n *\n * The lock is a sibling `.lock` file created with `wx` — the same file-existence primitive the\n * SDK's `withFileLock` builds on. Exclusivity comes from the filesystem, so it holds across\n * processes, not just across async tasks in one process.\n */\nexport async function acquireSessionWriter(sessionPath: string): Promise<SessionWriterLease> {\n // M95 — `.writer.lock`, NOT `.lock`.\n //\n // `withFileLock(path, fn)` already uses `<path>.lock` as its companion. While the lease was called\n // from nowhere (the defect this milestone fixes) the collision was theoretical; wiring it to the\n // same file would make the long-lived lease block every short critical section on the same path.\n //\n // Two files because they are two things: `withFileLock` protects a section with a start and an\n // end; the lease is OWNERSHIP, held across turns, with an explicit `release()` — the distinction\n // the M81 docstring above already explains.\n const lockPath = `${sessionPath}.writer.lock`;\n const mine: LockOwner = ownerNow();\n let fd: number;\n try {\n // The mode applies on CREATION — the `w` in `writeOwner` does not alter an existing file.\n fd = openSync(lockPath, \"wx\", 0o600);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n // M95 — the lock exists. Until now that alone was enough to refuse, and that was the defect: a\n // TUI killed by SIGKILL locked the user out of their own session PERMANENTLY, with no documented\n // recovery path. Now the lock says who the owner is, and a dead owner yields its place.\n if (!reclaimable(readOwner(lockPath))) throw new SessionBusyError(sessionPath);\n writeOwner(lockPath, mine);\n return createLease(sessionPath, lockPath);\n }\n closeSync(fd);\n writeOwner(lockPath, mine);\n\n return createLease(sessionPath, lockPath);\n}\n\n/** The lease itself — idempotent `release()`. Extracted because acquisition has two exit paths. */\nfunction createLease(sessionPath: string, lockPath: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath,\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n rmSync(lockPath, { force: true });\n },\n renew: (): void => {\n // A released lease does not own the lock. Re-stamping here would RE-CREATE the file and give\n // this process ownership it explicitly gave up — the one way this method could manufacture the\n // double-writer the lease exists to prevent.\n if (released) return;\n writeOwner(lockPath, ownerNow());\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sandbox/shell-escape.ts","../src/sandbox/types.ts","../src/sandbox/local-sandbox.ts"],"names":["TheokitAgentError","ConfigurationError","execFile","resolveChildEnv","path","mkdir","dirname","fsWriteFile"],"mappings":"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","file":"chunk-FLDHYF5C.cjs","sourcesContent":["/**\n * POSIX shell escaping for values interpolated into a `SandboxBackend.execute`\n * command string. `execute` runs via `/bin/sh -c`, so any untrusted value\n * (repo URL, ref, path) MUST be quoted to prevent command injection.\n */\n\n/**\n * Quotes `arg` as one word for POSIX `sh`, for a command line you build yourself and hand to\n * `SandboxBackend.execute`. A value from outside cannot end the word and start another command,\n * whatever it holds: quotes, `$()`, backticks, newlines, backslashes. An empty string comes back as\n * `''`. Quoting does not stop the program you call from reading a leading dash as an option, so put\n * `--` before the value for one that parses options. The derived helpers and `provisionRepo` use this\n * same quoter.\n *\n * Wrap `arg` in single quotes, escaping embedded single quotes (`'\\''`). A backslash before a quote,\n * a backslash or the end is written outside the quotes too (`'\\\\'`), so the word parses the same\n * under fish, which reads `\\'` and `\\\\` inside single quotes as escapes, as under sh (B-600): the PTY\n * runs the sandbox wrap under the user's `$SHELL`. After the first pass an original quote reads\n * `'\\''`, whose own backslash is always followed by `''`, so the second pass never matches it.\n */\nexport function shellEscapePosix(arg: string): string {\n const quotesEscaped = arg.replace(/'/g, \"'\\\\''\");\n return `'${quotesEscaped.replace(/\\\\(?=\\\\|'\\\\''|$)/g, \"'\\\\\\\\'\")}'`;\n}\n","/**\n * Sandbox backend protocol — pluggable execution environment for agent tools.\n *\n * Per ADR D1: only 2 abstract methods (`execute` + `uploadFile`). All\n * higher-level operations are derived on the base class. New backends\n * (Docker, Firecracker, E2B) only implement those 2 methods.\n *\n * @public\n */\n\nimport { ConfigurationError, TheokitAgentError } from \"../errors.js\";\nimport type { EnvPolicy } from \"../types/env-policy.js\";\nimport { shellEscapePosix } from \"./shell-escape.js\";\n\n/**\n * What a {@link SandboxBackend} returns for one command.\n *\n * `exitCode` is a coarse signal rather than the child's real exit status: the backends in this\n * package report `0` on success, `124` on timeout, and `1` for everything else — including a command\n * that exited with its own non-zero code. Read `timedOut` to tell a killed command from a failed one,\n * and `stderr` for the reason; branching on a specific exit code will not work here.\n *\n * `stdout` and `stderr` are truncated independently at `SandboxConfig.maxOutputBytes`, with a\n * trailing `...(truncated)` marker. Neither is safe to parse as a complete document without checking\n * for it.\n */\nexport interface ExecuteResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n timedOut: boolean;\n}\n\n/**\n * Construction-time settings shared by every {@link SandboxBackend}. All fields are optional and the\n * base constructor fills them in, so `{}` is a valid config: `workDir` `/tmp`, `timeoutMs` 30000,\n * `maxOutputBytes` 5 MiB, `env` `\"inherit-scrubbed\"`.\n *\n * `workDir` is the child process's cwd and the base a relative `uploadFile` path resolves against. It\n * is not a boundary — under `LocalSandbox` nothing stops a command, or an absolute path, from leaving\n * it. Only `LinuxSandbox` makes it enforceable.\n *\n * `timeoutMs` is the default for every call; `execute(command, { timeoutMs })` overrides it per\n * command. On expiry the child is killed and the result carries `timedOut: true` with `exitCode`\n * 124 — the partial output collected so far is still returned.\n */\nexport interface SandboxConfig {\n workDir?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n /**\n * #54 — env inherit/scrub policy for the executed command's child process.\n * Defaults to `\"inherit-scrubbed\"` (drop secret-like vars: `*KEY*`, `*SECRET*`,\n * `*TOKEN*`, `*PASSWORD*`, `*_AUTH*`). Pass `\"all\"` to restore full inheritance\n * or `\"core\"` for a minimal safe allowlist.\n */\n env?: EnvPolicy;\n}\n\n/**\n * What a {@link SandboxBackend} states about its outbound network. It is a statement the backend\n * makes about itself, for a caller that wants to ask before it trusts: this package enforces nothing\n * from it, and a custom backend is responsible for the claim being true.\n *\n * `\"unrestricted\"` means a command can reach whatever the host can. `\"none\"` means it cannot reach\n * the network at all. `\"allowlist\"` means it can reach only the destinations in `allow`, written as\n * the backend's own firewall spells them (usually domain names); the field is meaningful only with\n * that mode. A backend that says nothing leaves `networkPolicy` undefined, which is not the same as\n * `\"unrestricted\"`: it means the backend makes no claim either way.\n */\nexport interface SandboxNetworkPolicy {\n readonly mode: \"unrestricted\" | \"allowlist\" | \"none\";\n readonly allow?: readonly string[];\n}\n\n/**\n * A backend refused a command on policy grounds — the environment works, and it said no. Carries a\n * stable `code: \"sandbox_security\"` for callers that prefer not to match on the class.\n *\n * Nothing in this package throws it, which is worth knowing before you write a `catch` for it:\n * `LocalSandbox` and `LinuxSandbox` report a refusal through {@link ExecuteResult} (non-zero\n * `exitCode`, reason on `stderr`), and a write blocked under confinement surfaces as a plain `Error`\n * from `uploadFile`. This type exists so a custom backend that must reject a command BEFORE spawning\n * anything — a container or VM runner enforcing its own policy — can do so in the protocol's\n * vocabulary instead of inventing an error of its own.\n */\nexport class SandboxSecurityError extends TheokitAgentError {\n override readonly name = \"SandboxSecurityError\";\n override readonly code = \"sandbox_security\" as const;\n constructor(message: string) {\n // not retryable: a refused command is refused every time; retrying re-asks the same question\n super(message, { code: \"sandbox_security\", isRetryable: false });\n }\n}\n\n/**\n * A backend cannot be used at all: its runtime is missing or unusable. The distinction from\n * {@link SandboxSecurityError} is which side failed — there the sandbox worked and denied the\n * command, here there is no sandbox. Carries `code: \"sandbox_not_available\"`.\n *\n * Nothing in this package throws it either, and that is a position rather than an oversight:\n * `createSandboxBackend` treats a missing bubblewrap as a degradation, warns once, and hands back an\n * unconfined `LocalSandbox`. Throw this instead from a backend that must never silently run outside\n * its isolation.\n */\nexport class SandboxNotAvailableError extends TheokitAgentError {\n override readonly name = \"SandboxNotAvailableError\";\n override readonly code = \"sandbox_not_available\" as const;\n constructor(message: string) {\n // not retryable: a missing runtime does not appear on a second attempt\n super(message, { code: \"sandbox_not_available\", isRetryable: false });\n }\n}\n\n/**\n * The execution capability handed to an agent tool, and the extension point for new environments.\n *\n * A backend implements exactly two methods, `execute` and `uploadFile`. Everything else here —\n * `readFile`, `glob`, `grep`, `listDir` — is derived from `execute` by shelling out `cat`, `find`,\n * `grep` and `ls`, so a subclass that overrides `execute` alone inherits confined reads, searches and\n * listings for free. It does not inherit confined writes: `writeFile` delegates to `uploadFile`, and\n * that is the second override `LinuxSandbox` needs.\n *\n * Choosing one: `LocalSandbox` runs the command on the host with no isolation and suits code you\n * already trust; `LinuxSandbox` adds kernel confinement where bubblewrap is available;\n * `createSandboxBackend` probes and picks between them, and is the right entry point unless you have\n * a reason to pin one. For genuinely untrusted code you want a container or VM backend, which this\n * package does not ship — write it against these two methods.\n *\n * Two optional members let a backend say more about itself. {@link SandboxBackend.networkPolicy}\n * states its egress: `LocalSandbox` declares `\"unrestricted\"`, `LinuxSandbox` declares `\"none\"` when\n * bubblewrap unshares the network, and a backend that does not override it states nothing. Nothing in\n * this package enforces an allowlist, so `\"allowlist\"` is only ever a claim a custom backend makes\n * for itself. {@link SandboxBackend.dispose} releases what the backend holds, and the owner of the\n * backend calls it.\n *\n * Because the derived helpers assemble shell command lines (arguments escaped for POSIX `sh`), a\n * backend whose `execute` is not a POSIX shell must override them as well.\n */\nexport abstract class SandboxBackend {\n protected config: SandboxConfig;\n\n /**\n * What this backend states about its outbound network, or `undefined` when it states nothing.\n * Read it to check an isolation claim before trusting a backend; do not read `undefined` as\n * \"unrestricted\". A subclass sets it as a field, and it is a statement only: the backend must\n * enforce it itself.\n */\n declare readonly networkPolicy?: SandboxNetworkPolicy;\n\n constructor(config: SandboxConfig = {}) {\n this.config = {\n workDir: config.workDir ?? \"/tmp\",\n timeoutMs: config.timeoutMs ?? 30_000,\n maxOutputBytes: config.maxOutputBytes ?? 5 * 1024 * 1024,\n // #54 — preserve the env policy so backends can scrub secrets.\n env: config.env ?? \"inherit-scrubbed\",\n };\n }\n\n abstract execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult>;\n\n abstract uploadFile(path: string, content: string | Buffer): Promise<void>;\n\n /**\n * Releases whatever the backend holds, such as a VM or a container. The default does nothing,\n * which is correct for a backend that holds nothing, so existing subclasses need no change. Safe\n * to call more than once. The owner of the backend calls it; nothing in this package does.\n */\n async dispose(): Promise<void> {}\n\n async readFile(path: string): Promise<string> {\n const result = await this.execute(`cat ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`readFile failed: ${result.stderr}`);\n }\n return result.stdout;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.uploadFile(path, content);\n }\n\n async glob(pattern: string, cwd?: string): Promise<string[]> {\n const dir = cwd ?? this.config.workDir ?? \".\";\n const result = await this.execute(\n `find ${this.shellEscape(dir)} -name ${this.shellEscape(pattern)} -type f 2>/dev/null`,\n );\n this.assertCommandRan(\"glob\", result);\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async grep(pattern: string, path?: string): Promise<string[]> {\n const target = path ?? \".\";\n const result = await this.execute(\n `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`,\n );\n // `grep` exits 1 for NO MATCH and >= 2 for an error, so a 1 is an answer and is not asserted on.\n if (result.exitCode !== 1) this.assertCommandRan(\"grep\", result);\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async listDir(path: string): Promise<string[]> {\n const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);\n this.assertCommandRan(\"listDir\", result);\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n /**\n * Turns \"the command did not run\" into an error instead of an empty array.\n *\n * `glob`, `grep` and `listDir` used to `return []` on any non-zero exit, so a search that could not\n * run reported the same thing as a search that found nothing — opposite facts, one normal and one\n * meaning the agent is looking at a filesystem it cannot read. That is the failure mode a backend\n * whose `execute` is not a POSIX shell hits, which this class's own docblock warns about in prose\n * and could not enforce.\n *\n * A genuine no-match is still `[]`: `find` exits 0 with empty output, and `grep`'s exit 1 is\n * checked by its caller before this is reached.\n */\n private assertCommandRan(operation: string, result: ExecuteResult): void {\n if (result.exitCode === 0) return;\n throw new ConfigurationError(\n `${operation} failed (exit ${String(result.exitCode)}): ${result.stderr.trim() || \"no stderr\"}. ` +\n `The derived helpers shell out to POSIX \\`cat\\`/\\`find\\`/\\`grep\\`/\\`ls\\` — a backend whose ` +\n `execute() is not a POSIX shell must override them.`,\n { code: \"sandbox_derived_helper_failed\" },\n );\n }\n\n protected truncateOutput(output: string): string {\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n if (Buffer.byteLength(output) > max) {\n return `${output.slice(0, max)}\\n...(truncated)`;\n }\n return output;\n }\n\n private shellEscape(arg: string): string {\n return shellEscapePosix(arg);\n }\n}\n\n/**\n * A backend OR a per-request resolver of one — mirrors `FilesystemProvider` / `InteractiveProvider`.\n * A resolver runs at tool-execution time (request scope), so a multi-tenant / multi-role agent gets a\n * distinct sandbox per request without a shared mutable one. This is how a tool takes execution as an\n * INJECTED capability: the same tool runs on a local sandbox, a container/E2B backend (cluster/web), or\n * any future backend, with no direct `child_process` import.\n *\n * @public\n */\nexport type SandboxProvider<Ctx = unknown> =\n | SandboxBackend\n | ((ctx: Ctx) => SandboxBackend | Promise<SandboxBackend>);\n\n/** Resolve a {@link SandboxProvider} to a concrete backend for `ctx`. */\nexport async function resolveSandbox<Ctx>(\n provider: SandboxProvider<Ctx>,\n ctx: Ctx,\n): Promise<SandboxBackend> {\n return provider instanceof SandboxBackend ? provider : provider(ctx);\n}\n\n/** Codex's three canonical sandbox modes; lives in this leaf module so bwrap.ts and deny-read.ts need not import each other. */\nexport type SandboxMode = \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n\n/** Result of probing for bubblewrap. Detection never throws. */\nexport type BwrapDetection = { ok: true; bin: string } | { ok: false; reason: string };\n","/**\n * LocalSandbox — subprocess-based execution. **This is NOT an isolation\n * boundary.** It runs the command via `/bin/sh -c` in the SAME OS as the host\n * with the host's filesystem and network fully reachable — it provides NO\n * process, filesystem, or network isolation. Its only safety affordances are:\n * - a wall-clock timeout (kills a runaway command),\n * - an output-size cap (bounds memory), and\n * - env scrubbing (#54): secret-like parent env vars (`*KEY*`/`*SECRET*`/\n * `*TOKEN*`/`*PASSWORD*`/`*_AUTH*`) are dropped from the child by default\n * (`SandboxConfig.env`), so a shell tool cannot exfiltrate host secrets via\n * the environment.\n *\n * For real isolation (untrusted code), use a container/VM backend — NOT this.\n *\n * @public\n */\n\nimport { execFile } from \"node:child_process\";\nimport { writeFile as fsWriteFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { resolveChildEnv } from \"../internal/runtime/lifecycle/env-policy.js\";\nimport {\n type ExecuteResult,\n SandboxBackend,\n type SandboxConfig,\n type SandboxNetworkPolicy,\n} from \"./types.js\";\n\n/**\n * Runs a command with `/bin/sh -c` on the host. NOT an isolation boundary — the\n * module note above states exactly what it does and does not protect.\n *\n * const sandbox = new LocalSandbox({ workDir: \"/srv/repo\", timeoutMs: 10_000 });\n * const { stdout, exitCode, timedOut } = await sandbox.execute(\"ls -1\");\n *\n * Needs a POSIX host with `/bin/sh`. Every helper inherited from\n * {@link SandboxBackend} — `readFile`, `glob`, `grep`, `listDir` — is built on\n * this `execute`, so they inherit that requirement too.\n *\n * How it fails: `execute` NEVER rejects. Every outcome, including a failure to\n * spawn, comes back as an {@link ExecuteResult}; read `timedOut` first\n * (`true` implies `exitCode` 124) and `stderr` for the reason. `uploadFile` DOES\n * reject, with the raw `node:fs` error, on a bad path or missing permission.\n *\n * Traps:\n * - `exitCode` is not the child's exit code. It is 0, 124 (timed out) or 1 — a\n * command that exits 3 is reported as 1, so branching on a specific code does\n * not work here.\n * - Output beyond `maxOutputBytes` (default 5 MiB) makes Node kill the child.\n * The result is `exitCode: 1` and `timedOut: false`, with the cut stream\n * carrying the `...(truncated)` marker described on `ExecuteResult` (#363).\n * Since `exitCode` is 1 either way, that marker is what tells a lost-output\n * result from a command that genuinely failed.\n * - `uploadFile` resolves a relative path against `workDir` but does not contain\n * it: an absolute path, or one containing `..`, writes wherever the process\n * can. `workDir` is a starting directory, not a jail.\n * - Secret-like parent env vars are dropped by default. Passing\n * `env: \"all\"` in {@link SandboxConfig} puts the host's API keys back within\n * reach of any command the agent runs.\n */\nexport class LocalSandbox extends SandboxBackend {\n /** Runs on the host, so the network is whatever the host's is. */\n override readonly networkPolicy: SandboxNetworkPolicy = { mode: \"unrestricted\" };\n\n constructor(config: SandboxConfig = {}) {\n super(config);\n }\n\n async execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult> {\n const timeout = opts?.timeoutMs ?? this.config.timeoutMs ?? 30_000;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n\n return new Promise<ExecuteResult>((resolve) => {\n const child = execFile(\n \"/bin/sh\",\n [\"-c\", command],\n {\n cwd: this.config.workDir,\n timeout,\n maxBuffer: max,\n encoding: \"utf-8\",\n // #54 — scrub secret-like host env vars from the child by default.\n env: resolveChildEnv({ policy: this.config.env }),\n },\n (error, stdout, stderr) => {\n resolve(this.buildResult(error, stdout ?? \"\", stderr ?? \"\"));\n },\n );\n\n // Safety: if child somehow doesn't callback\n child.on(\"error\", () => {\n resolve({ stdout: \"\", stderr: \"spawn error\", exitCode: 1, timedOut: false });\n });\n });\n }\n\n private buildResult(error: Error | null, stdout: string, stderr: string): ExecuteResult {\n const timedOut = error !== null && \"killed\" in error && (error as { killed: boolean }).killed;\n // #363 — the ONLY reliable signal that output was lost. Node caps the buffer AT `maxBuffer`, so\n // for ASCII output the string comes back exactly at the cap and `truncateOutput`'s `> max` test\n // never fires; a length comparison cannot tell a cut document from a complete one that happens\n // to be that long. The child being killed for overflow can.\n const overflowed =\n error !== null && (error as { code?: unknown }).code === \"ERR_CHILD_PROCESS_STDIO_MAXBUFFER\";\n return {\n stdout: this.markIfCut(this.truncateOutput(stdout), overflowed),\n stderr: this.markIfCut(this.truncateOutput(stderr), overflowed),\n exitCode: timedOut ? 124 : error ? 1 : 0,\n timedOut,\n };\n }\n\n /**\n * Append the `...(truncated)` marker `ExecuteResult` tells callers to branch on, when the child\n * was killed for exceeding `maxOutputBytes` and this stream is the one sitting at the cap.\n *\n * `truncateOutput` has already run, so an output it marked is left alone rather than marked\n * twice. Node does not say WHICH stream overflowed, so the stream at the cap is the one that\n * lost data — the other, being shorter, is complete.\n */\n private markIfCut(output: string, overflowed: boolean): string {\n if (!overflowed) return output;\n if (output.endsWith(\"...(truncated)\")) return output;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n return Buffer.byteLength(output) >= max ? `${output}\\n...(truncated)` : output;\n }\n\n async uploadFile(path: string, content: string | Buffer): Promise<void> {\n const fullPath = path.startsWith(\"/\") ? path : `${this.config.workDir}/${path}`;\n await mkdir(dirname(fullPath), { recursive: true });\n await fsWriteFile(fullPath, content, \"utf-8\");\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal/persistence/fs-session-store.ts"],"names":["acquireSessionWriter","transcriptPath","legacyTranscriptPath","existsSync","readTranscript","path","mkdir","dirname","withFileLock","appendJsonl"],"mappings":"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","file":"chunk-FTXY4D44.cjs","sourcesContent":["/**\n * SE41 — `FsSessionStore`, the DEFAULT reference implementation of the pluggable\n * {@link SessionStore} seam. It reads and append-writes the native Claude-shaped\n * `.jsonl` transcript at `<baseDir>/projects/<encoded-cwd>/<agentId>.jsonl` — the\n * exact on-disk format SE40 introduced (the file the Claude Code CLI can\n * `--continue`). Omitting `local.sessionStore` resolves to this store, so the\n * default persistence path is byte-identical to SE40 behavior.\n *\n * `readRecords` is `readTranscript(transcriptPath(...))` (a missing session →\n * `[]`, not an error — a fresh agent has no history). `appendRecords` is a TRUE\n * append: it reads the prior records, concatenates the new-turn delta, and\n * rewrites the whole line set atomically under the SE40 cross-process file lock\n * (`writeTranscript` never shrinks — the native format is an append-only\n * `parentUuid` DAG). The parent dir is created BEFORE acquiring the lock because\n * the lock's companion `<path>.lock` file needs an existing parent dir (the SE40\n * `mkdir(dirname)`-before-lock fix).\n *\n * @internal\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { withFileLock } from \"./file-lock.js\";\nimport { appendJsonl } from \"./jsonl.js\";\nimport {\n legacyTranscriptPath,\n readTranscript,\n type SessionRecord,\n transcriptPath,\n} from \"./session-transcript.js\";\nimport { acquireSessionWriter, type SessionWriterLease } from \"./session-writer.js\";\n\n/** Options identifying the on-disk transcript location for the FS default store. */\nexport interface FsSessionStoreOptions {\n /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */\n baseDir: string;\n /** The workspace cwd whose encoded form is the transcript project dir. */\n cwd: string;\n}\n\n/**\n * Reference-counted leases, shared per path.\n *\n * `acquireSessionWriter` is STRICT on purpose: two concurrent acquisitions of the same path, and\n * exactly one wins — \"a lease that let both through would be decorative\" (the M81 test says exactly\n * that, and it is right). That is the primitive's contract, and it does not change.\n *\n * But within ONE process it is normal to have more than one store over the same session: the golden\n * compaction tests and the concurrent-send tests do exactly that, and they record real runtime\n * behavior. Applying the raw primitive there would turn a legitimate pattern into a\n * `SessionBusyError`.\n *\n * The reconciliation belongs here, in the primitive's consumer: the process takes ONE lease per path\n * and counts how many stores use it. The last to release actually frees it. Cross-process stays\n * strict — which is the problem M81 states (\"`exec resume --last` can write into the TUI's live\n * session\").\n */\nconst sharedLeases = new Map<string, { lease: SessionWriterLease; refs: number }>();\n\nasync function acquireShared(path: string): Promise<SessionWriterLease> {\n const existing = sharedLeases.get(path);\n if (existing !== undefined) {\n existing.refs++;\n return createProxy(path);\n }\n const lease = await acquireSessionWriter(path);\n sharedLeases.set(path, { lease, refs: 1 });\n return createProxy(path);\n}\n\n/** A handle that decrements the count; the last to release frees the real lease. */\nfunction createProxy(path: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath: path,\n // The proxy forwards the renewal to the ONE real lease this path shares. Renewing through any\n // holder is correct and is the point: the record must say \"someone in this process is still\n // writing\", and the refcount already guarantees they are all the same owner.\n renew: (): void => {\n if (released) return;\n sharedLeases.get(path)?.lease.renew();\n },\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n const entry = sharedLeases.get(path);\n if (entry === undefined) return;\n entry.refs--;\n if (entry.refs > 0) return;\n sharedLeases.delete(path);\n await entry.lease.release();\n },\n };\n}\n\n/**\n * The default `SessionStore` — reads/append-writes the native `.jsonl` transcript.\n */\nexport class FsSessionStore implements SessionStore {\n readonly #baseDir: string;\n readonly #cwd: string;\n /** One lease per `agentId` — a store serves more than one session over the process lifetime. */\n readonly #leases = new Map<string, SessionWriterLease>();\n /** Memoized per agent so read, append and lease can never disagree about which file is the session. */\n readonly #paths = new Map<string, string>();\n\n constructor(options: FsSessionStoreOptions) {\n this.#baseDir = options.baseDir;\n this.#cwd = options.cwd;\n }\n\n /**\n * The file that IS this agent's session.\n *\n * #400 made transcript filenames UUIDs so `claude --continue` can find them. A transcript written\n * before that lives under the old name, and switching to the new one would not lose the file but\n * would abandon it: the agent would start a second, empty history beside its real one. So an\n * existing legacy file wins, and only a session with no file yet gets the new name.\n *\n * Resolved ONCE per agent. Re-deciding per call would let `acquire` lock one path while\n * `appendRecords` wrote another — the lease would be guarding a file nobody was writing.\n */\n #pathFor(agentId: string): string {\n const memoized = this.#paths.get(agentId);\n if (memoized !== undefined) return memoized;\n const canonical = transcriptPath(this.#baseDir, this.#cwd, agentId);\n const legacy = legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);\n const resolved = legacy !== canonical && existsSync(legacy) ? legacy : canonical;\n this.#paths.set(agentId, resolved);\n return resolved;\n }\n\n async readRecords(agentId: string): Promise<SessionRecord[]> {\n return readTranscript(this.#pathFor(agentId));\n }\n\n async appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void> {\n // Empty delta → nothing to persist (avoids an unnecessary lock + rewrite).\n if (records.length === 0) return;\n const path = this.#pathFor(agentId);\n // mkdir BEFORE the lock: withFileLock's companion `<path>.lock` needs the parent dir.\n await mkdir(dirname(path), { recursive: true });\n\n await withFileLock(path, async () => {\n // M93 — appends the DELTA instead of rewriting the whole file.\n //\n // Before: `readTranscript` + `writeTranscript` of everything, per turn. O(n) of I/O **and** of\n // parsing on every turn, O(n^2) per session — the consumer note in\n // `agents/lib/session/backtrack.ts` records 1.4 MB / 3000 lines over 200 turns.\n //\n // Correct because the format **is already append-only**: the `parentUuid` DAG does not depend\n // on line order, and every record carries its own parent. `appendJsonl` **already existed in\n // the package** and had a single caller (`eval/runner.ts`) — the primitive was there, it was\n // the store that ignored it (rung 4).\n //\n // `withFileLock` stays — but the earlier claim that \"it is what serializes two concurrent\n // `appendRecords`\" was too strong, and M93's adversarial review measured it: removing it fails\n // no test. The reason is the paragraph above — the `parentUuid` DAG does not depend on line\n // order, so two interleaved batches reconstruct identically. The work the lock was doing\n // (protecting a read-modify-write) disappeared along with the rewrite.\n //\n // What it still covers is the TOCTOU window in `needsLineBreakBefore` (read the last byte,\n // then write): without it, two processes can both conclude \"a \\n is missing\" and produce a\n // blank line — which the reader discards, i.e. benign. It stays as a **declared, not\n // mechanized** defense (the discipline in `error-handling.md` § 4: enumerate the residue\n // rather than let a missing test pass for coverage).\n //\n // `writeTranscript` still exists for **compaction**, the one operation that legitimately\n // rewrites the file.\n for (const record of records) appendJsonl(path, record);\n });\n }\n\n /**\n * Takes the session's writer lease. Throws `SessionBusyError` when another process holds it.\n *\n * **Explicit, and NOT inside `appendRecords`** — M95's adversarial review measured why that\n * matters: the `SessionStore` contract states that \"an `appendRecords` rejection is logged to\n * stderr, NOT thrown to the caller (best-effort write)\". Acquiring there made the\n * `SessionBusyError` get **swallowed**, and the result was worse than the original problem:\n * instead of two writers interleaving lines, the loser **lost the turn silently** — nothing on\n * disk, a stderr warning invisible under the TUI, and no way for the caller to react.\n *\n * At init the error reaches someone who can act: `exec` forks to a new id, which is exactly what\n * the error message itself prescribes. It is the difference between failing where a decision is\n * possible and failing where only loss is.\n */\n async acquire(agentId: string): Promise<void> {\n if (this.#leases.has(agentId)) return;\n const path = this.#pathFor(agentId);\n await mkdir(dirname(path), { recursive: true });\n this.#leases.set(agentId, await acquireShared(path));\n }\n\n /**\n * Releases ONE agent's lease.\n *\n * It exists because `dispose()` releases **every** lease the store holds, and a store injected by\n * the consumer may serve several agents: an init that fails for agent B must not free agent A's\n * lease, which is still live and writing.\n */\n async release(agentId: string): Promise<void> {\n const lease = this.#leases.get(agentId);\n if (lease === undefined) return;\n this.#leases.delete(agentId);\n await lease.release();\n }\n\n /**\n * Releases every lease this store holds.\n *\n * Without it the `.writer.lock` outlives the process and the next open would have to wait out the\n * heartbeat window — recoverable, but 30 s of waiting after a CLEAN shutdown would be an avoidable\n * defect. Idempotent: calling it twice is not an error.\n */\n async dispose(): Promise<void> {\n const leases = [...this.#leases.values()];\n this.#leases.clear();\n for (const lease of leases) await lease.release();\n }\n}\n"]}