@theokit/sdk 5.15.1 → 5.15.3

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 (360) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/a2a/index.cjs +3 -3
  3. package/dist/a2a/index.js +1 -1
  4. package/dist/agent-LTJYDRWI.cjs +72 -0
  5. package/dist/{agent-CB27KF6O.cjs.map → agent-LTJYDRWI.cjs.map} +1 -1
  6. package/dist/agent-MOSFFVPU.js +63 -0
  7. package/dist/{agent-66U5QCPF.js.map → agent-MOSFFVPU.js.map} +1 -1
  8. package/dist/agent-session-store-2LQEPS4H.js +6 -0
  9. package/dist/{agent-session-store-22GKA7O4.js.map → agent-session-store-2LQEPS4H.js.map} +1 -1
  10. package/dist/agent-session-store-IOH6BREF.cjs +23 -0
  11. package/dist/{agent-session-store-WP6NGDVX.cjs.map → agent-session-store-IOH6BREF.cjs.map} +1 -1
  12. package/dist/auth/index.cjs +15 -15
  13. package/dist/auth/index.js +3 -3
  14. package/dist/{batch-N2XDYZED.js → batch-7IGV2OKA.js} +8 -8
  15. package/dist/{batch-N2XDYZED.js.map → batch-7IGV2OKA.js.map} +1 -1
  16. package/dist/{batch-VFR2MAUE.cjs → batch-FIWQZ3JD.cjs} +14 -14
  17. package/dist/{batch-VFR2MAUE.cjs.map → batch-FIWQZ3JD.cjs.map} +1 -1
  18. package/dist/{chunk-AT2PESXB.cjs → chunk-2UYAL4N7.cjs} +5 -5
  19. package/dist/chunk-2UYAL4N7.cjs.map +1 -0
  20. package/dist/{chunk-K2BQQ445.js → chunk-3GQYMKVJ.js} +3 -3
  21. package/dist/{chunk-K2BQQ445.js.map → chunk-3GQYMKVJ.js.map} +1 -1
  22. package/dist/{chunk-YSTXFWQE.cjs → chunk-3HL43XBD.cjs} +4 -4
  23. package/dist/{chunk-YSTXFWQE.cjs.map → chunk-3HL43XBD.cjs.map} +1 -1
  24. package/dist/{chunk-XESYJ4JC.js → chunk-3VX7JHJI.js} +7 -7
  25. package/dist/{chunk-XESYJ4JC.js.map → chunk-3VX7JHJI.js.map} +1 -1
  26. package/dist/{chunk-6XQLTQB6.cjs → chunk-3YCNBOX7.cjs} +3 -3
  27. package/dist/{chunk-6XQLTQB6.cjs.map → chunk-3YCNBOX7.cjs.map} +1 -1
  28. package/dist/{chunk-3E3VSMB6.cjs → chunk-3ZRJ4RNB.cjs} +100 -61
  29. package/dist/chunk-3ZRJ4RNB.cjs.map +1 -0
  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-VNRNZ3WI.js → chunk-4OMRDNPY.js} +4 -4
  39. package/dist/{chunk-VNRNZ3WI.js.map → chunk-4OMRDNPY.js.map} +1 -1
  40. package/dist/{chunk-INVEGVGO.js → chunk-4TA47IK4.js} +8 -8
  41. package/dist/{chunk-INVEGVGO.js.map → chunk-4TA47IK4.js.map} +1 -1
  42. package/dist/{chunk-O2RCS3SJ.js → chunk-4TTR63O7.js} +4 -4
  43. package/dist/{chunk-O2RCS3SJ.js.map → chunk-4TTR63O7.js.map} +1 -1
  44. package/dist/{chunk-MLGETTYX.js → chunk-4YUIYSZO.js} +6 -6
  45. package/dist/{chunk-MLGETTYX.js.map → chunk-4YUIYSZO.js.map} +1 -1
  46. package/dist/{chunk-RZL2W4C3.js → chunk-5L7S2VOL.js} +5 -5
  47. package/dist/{chunk-RZL2W4C3.js.map → chunk-5L7S2VOL.js.map} +1 -1
  48. package/dist/{chunk-3WKWBYPO.cjs → chunk-6EHOJLJ3.cjs} +20 -20
  49. package/dist/chunk-6EHOJLJ3.cjs.map +1 -0
  50. package/dist/{chunk-XOU3CL7S.cjs → chunk-6HQFPX7N.cjs} +28 -28
  51. package/dist/chunk-6HQFPX7N.cjs.map +1 -0
  52. package/dist/{chunk-R427EOZE.cjs → chunk-7EFT6N5C.cjs} +13 -13
  53. package/dist/{chunk-R427EOZE.cjs.map → chunk-7EFT6N5C.cjs.map} +1 -1
  54. package/dist/{chunk-L7V4DDIE.cjs → chunk-7VNFOGRD.cjs} +12 -12
  55. package/dist/chunk-7VNFOGRD.cjs.map +1 -0
  56. package/dist/{chunk-YKNBTHMD.js → chunk-AQ23NII7.js} +3 -3
  57. package/dist/{chunk-YKNBTHMD.js.map → chunk-AQ23NII7.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-CBBT42VX.js → chunk-CMDM4VF6.js} +3 -3
  65. package/dist/{chunk-CBBT42VX.js.map → chunk-CMDM4VF6.js.map} +1 -1
  66. package/dist/{chunk-OLJOI4LK.js → chunk-CN732Z5C.js} +4 -4
  67. package/dist/{chunk-OLJOI4LK.js.map → chunk-CN732Z5C.js.map} +1 -1
  68. package/dist/{chunk-SETHRLQS.cjs → chunk-D3BL3EEB.cjs} +9 -9
  69. package/dist/chunk-D3BL3EEB.cjs.map +1 -0
  70. package/dist/{chunk-LCCT5ZXJ.js → chunk-DB5RYCXX.js} +13 -13
  71. package/dist/{chunk-LCCT5ZXJ.js.map → chunk-DB5RYCXX.js.map} +1 -1
  72. package/dist/{chunk-SV63WTA2.js → chunk-DBH2WA2U.js} +4 -4
  73. package/dist/{chunk-SV63WTA2.js.map → chunk-DBH2WA2U.js.map} +1 -1
  74. package/dist/{chunk-XE3CAY6Z.cjs → chunk-DEHPRORL.cjs} +4 -4
  75. package/dist/{chunk-XE3CAY6Z.cjs.map → chunk-DEHPRORL.cjs.map} +1 -1
  76. package/dist/{chunk-UKMBRMGT.js → chunk-DGO4LPLP.js} +4 -4
  77. package/dist/{chunk-UKMBRMGT.js.map → chunk-DGO4LPLP.js.map} +1 -1
  78. package/dist/{chunk-RS7YQR4X.cjs → chunk-EBX6XUN7.cjs} +7 -7
  79. package/dist/{chunk-RS7YQR4X.cjs.map → chunk-EBX6XUN7.cjs.map} +1 -1
  80. package/dist/chunk-EDIYR4VR.cjs +18 -0
  81. package/dist/{chunk-SPEN7RJU.cjs.map → chunk-EDIYR4VR.cjs.map} +1 -1
  82. package/dist/{chunk-QGDPMM3C.js → chunk-EF5IONLL.js} +5 -5
  83. package/dist/{chunk-QGDPMM3C.js.map → chunk-EF5IONLL.js.map} +1 -1
  84. package/dist/{chunk-MSW7LB2V.cjs → chunk-EOWIUDKG.cjs} +5 -5
  85. package/dist/{chunk-MSW7LB2V.cjs.map → chunk-EOWIUDKG.cjs.map} +1 -1
  86. package/dist/{chunk-L7JPXZ2C.cjs → chunk-ER3BF23R.cjs} +19 -19
  87. package/dist/{chunk-L7JPXZ2C.cjs.map → chunk-ER3BF23R.cjs.map} +1 -1
  88. package/dist/{chunk-BNEUB52I.cjs → chunk-ERBKPA2N.cjs} +4 -4
  89. package/dist/{chunk-BNEUB52I.cjs.map → chunk-ERBKPA2N.cjs.map} +1 -1
  90. package/dist/{chunk-4QCJAJAG.js → chunk-F54PXZIA.js} +5 -5
  91. package/dist/{chunk-4QCJAJAG.js.map → chunk-F54PXZIA.js.map} +1 -1
  92. package/dist/{chunk-2XWAYCFM.cjs → chunk-FBT2SF2S.cjs} +12 -12
  93. package/dist/{chunk-2XWAYCFM.cjs.map → chunk-FBT2SF2S.cjs.map} +1 -1
  94. package/dist/{chunk-AUH2EVWY.cjs → chunk-FZSPVIFK.cjs} +16 -16
  95. package/dist/chunk-FZSPVIFK.cjs.map +1 -0
  96. package/dist/{chunk-CYWOJA6B.js → chunk-GA27FFT5.js} +3 -3
  97. package/dist/{chunk-CYWOJA6B.js.map → chunk-GA27FFT5.js.map} +1 -1
  98. package/dist/{chunk-HWMGR3HA.js → chunk-GEQLHBJM.js} +6 -6
  99. package/dist/{chunk-HWMGR3HA.js.map → chunk-GEQLHBJM.js.map} +1 -1
  100. package/dist/{chunk-IJ7M6GOG.cjs → chunk-GKERJ52A.cjs} +4 -4
  101. package/dist/{chunk-IJ7M6GOG.cjs.map → chunk-GKERJ52A.cjs.map} +1 -1
  102. package/dist/{chunk-26GI3OYA.js → chunk-HFEAU2L2.js} +55 -16
  103. package/dist/chunk-HFEAU2L2.js.map +1 -0
  104. package/dist/{chunk-NEH3BPFD.cjs → chunk-HI5TJWQE.cjs} +19 -19
  105. package/dist/{chunk-NEH3BPFD.cjs.map → chunk-HI5TJWQE.cjs.map} +1 -1
  106. package/dist/{chunk-ZOFRNNZD.cjs → chunk-IZV72OEI.cjs} +11 -11
  107. package/dist/chunk-IZV72OEI.cjs.map +1 -0
  108. package/dist/{chunk-KU5O7WPK.js → chunk-JD3EKKKB.js} +4 -4
  109. package/dist/{chunk-KU5O7WPK.js.map → chunk-JD3EKKKB.js.map} +1 -1
  110. package/dist/{chunk-DFBWYETS.js → chunk-JRP2W2DR.js} +3 -3
  111. package/dist/{chunk-DFBWYETS.js.map → chunk-JRP2W2DR.js.map} +1 -1
  112. package/dist/{chunk-YQ3XC6C2.cjs → chunk-JVH3NNJ3.cjs} +13 -13
  113. package/dist/chunk-JVH3NNJ3.cjs.map +1 -0
  114. package/dist/{chunk-VWYFGE27.cjs → chunk-KC2HKUPT.cjs} +56 -56
  115. package/dist/chunk-KC2HKUPT.cjs.map +1 -0
  116. package/dist/{chunk-PDOXYXWX.cjs → chunk-KDJTECE6.cjs} +4 -4
  117. package/dist/{chunk-PDOXYXWX.cjs.map → chunk-KDJTECE6.cjs.map} +1 -1
  118. package/dist/{chunk-QD6B6W6I.js → chunk-L2KC2G7H.js} +5 -5
  119. package/dist/{chunk-QD6B6W6I.js.map → chunk-L2KC2G7H.js.map} +1 -1
  120. package/dist/{chunk-3BBTHFGJ.js → chunk-L5B7EWWH.js} +5 -5
  121. package/dist/{chunk-3BBTHFGJ.js.map → chunk-L5B7EWWH.js.map} +1 -1
  122. package/dist/{chunk-26UQG242.cjs → chunk-LAZTNGNR.cjs} +264 -264
  123. package/dist/chunk-LAZTNGNR.cjs.map +1 -0
  124. package/dist/{chunk-X7CE7Y5W.js → chunk-LG4JCJEI.js} +11 -11
  125. package/dist/{chunk-X7CE7Y5W.js.map → chunk-LG4JCJEI.js.map} +1 -1
  126. package/dist/{chunk-2HXTNVLM.js → chunk-LSURFRJA.js} +6 -6
  127. package/dist/{chunk-2HXTNVLM.js.map → chunk-LSURFRJA.js.map} +1 -1
  128. package/dist/{chunk-55LLQFHW.cjs → chunk-MJGY3E3C.cjs} +4 -4
  129. package/dist/{chunk-55LLQFHW.cjs.map → chunk-MJGY3E3C.cjs.map} +1 -1
  130. package/dist/{chunk-EQ5IDL6W.cjs → chunk-MKS5FFH5.cjs} +15 -15
  131. package/dist/chunk-MKS5FFH5.cjs.map +1 -0
  132. package/dist/{chunk-WM646HEN.js → chunk-MTNCJ7MQ.js} +6 -6
  133. package/dist/{chunk-WM646HEN.js.map → chunk-MTNCJ7MQ.js.map} +1 -1
  134. package/dist/{chunk-SA6K24NB.cjs → chunk-MXJJMP4U.cjs} +7 -7
  135. package/dist/chunk-MXJJMP4U.cjs.map +1 -0
  136. package/dist/{chunk-FTXY4D44.cjs → chunk-N2ORTDSO.cjs} +20 -20
  137. package/dist/chunk-N2ORTDSO.cjs.map +1 -0
  138. package/dist/{chunk-7YUPSBDV.cjs → chunk-NDXZXGVP.cjs} +6 -6
  139. package/dist/{chunk-7YUPSBDV.cjs.map → chunk-NDXZXGVP.cjs.map} +1 -1
  140. package/dist/{chunk-3UMPP553.cjs → chunk-NMRNE6QX.cjs} +11 -11
  141. package/dist/{chunk-3UMPP553.cjs.map → chunk-NMRNE6QX.cjs.map} +1 -1
  142. package/dist/{chunk-PKRPV3R7.js → chunk-NO6AG22P.js} +6 -6
  143. package/dist/{chunk-PKRPV3R7.js.map → chunk-NO6AG22P.js.map} +1 -1
  144. package/dist/{chunk-NK5EOTDY.js → chunk-OIOSY5G6.js} +3 -3
  145. package/dist/{chunk-NK5EOTDY.js.map → chunk-OIOSY5G6.js.map} +1 -1
  146. package/dist/{chunk-RGVMJU57.js → chunk-P7B64NFK.js} +8 -8
  147. package/dist/{chunk-RGVMJU57.js.map → chunk-P7B64NFK.js.map} +1 -1
  148. package/dist/{chunk-TDED5XES.js → chunk-PCPYTBVV.js} +6 -6
  149. package/dist/chunk-PCPYTBVV.js.map +1 -0
  150. package/dist/{chunk-YMVTUV5K.js → chunk-PISZRLES.js} +5 -5
  151. package/dist/{chunk-YMVTUV5K.js.map → chunk-PISZRLES.js.map} +1 -1
  152. package/dist/{chunk-BUUUWQMB.cjs → chunk-PNL63WDP.cjs} +4 -4
  153. package/dist/{chunk-BUUUWQMB.cjs.map → chunk-PNL63WDP.cjs.map} +1 -1
  154. package/dist/{chunk-5645PMVU.cjs → chunk-PXYINWBC.cjs} +20 -20
  155. package/dist/chunk-PXYINWBC.cjs.map +1 -0
  156. package/dist/{chunk-RTDVSHJR.cjs → chunk-Q4UOCMAO.cjs} +5 -5
  157. package/dist/{chunk-RTDVSHJR.cjs.map → chunk-Q4UOCMAO.cjs.map} +1 -1
  158. package/dist/{chunk-5IRDK5BC.js → chunk-QH6MWGOA.js} +4 -4
  159. package/dist/{chunk-5IRDK5BC.js.map → chunk-QH6MWGOA.js.map} +1 -1
  160. package/dist/{chunk-FBIDOOJM.cjs → chunk-QJVTDW3G.cjs} +6 -6
  161. package/dist/{chunk-FBIDOOJM.cjs.map → chunk-QJVTDW3G.cjs.map} +1 -1
  162. package/dist/{chunk-3LMVJTNJ.js → chunk-QPUKIZOT.js} +3 -3
  163. package/dist/{chunk-3LMVJTNJ.js.map → chunk-QPUKIZOT.js.map} +1 -1
  164. package/dist/{chunk-TRPRTZEH.cjs → chunk-RB3ZMMUU.cjs} +6 -6
  165. package/dist/{chunk-TRPRTZEH.cjs.map → chunk-RB3ZMMUU.cjs.map} +1 -1
  166. package/dist/{chunk-SWALPLFQ.cjs → chunk-RTCO3USA.cjs} +16 -16
  167. package/dist/chunk-RTCO3USA.cjs.map +1 -0
  168. package/dist/{chunk-SAGRF4IB.js → chunk-S2CMEJ5F.js} +3 -3
  169. package/dist/{chunk-SAGRF4IB.js.map → chunk-S2CMEJ5F.js.map} +1 -1
  170. package/dist/{chunk-VZGE7NAI.js → chunk-S5ERB3CH.js} +3 -3
  171. package/dist/{chunk-VZGE7NAI.js.map → chunk-S5ERB3CH.js.map} +1 -1
  172. package/dist/{chunk-XJ3WU5CZ.js → chunk-S7IZEVXX.js} +6 -6
  173. package/dist/{chunk-XJ3WU5CZ.js.map → chunk-S7IZEVXX.js.map} +1 -1
  174. package/dist/{chunk-Q7UG242V.js → chunk-TIYQDJFK.js} +3 -3
  175. package/dist/{chunk-Q7UG242V.js.map → chunk-TIYQDJFK.js.map} +1 -1
  176. package/dist/{chunk-POBZXULR.cjs → chunk-TTNJOM5W.cjs} +16 -16
  177. package/dist/{chunk-POBZXULR.cjs.map → chunk-TTNJOM5W.cjs.map} +1 -1
  178. package/dist/{chunk-VCQ37TC5.js → chunk-TTXRNCXY.js} +3 -3
  179. package/dist/{chunk-VCQ37TC5.js.map → chunk-TTXRNCXY.js.map} +1 -1
  180. package/dist/{chunk-3HA7N2BB.cjs → chunk-TYKBAKAS.cjs} +5 -5
  181. package/dist/{chunk-3HA7N2BB.cjs.map → chunk-TYKBAKAS.cjs.map} +1 -1
  182. package/dist/{chunk-A77UMVMD.cjs → chunk-UMCCYXFZ.cjs} +8 -8
  183. package/dist/{chunk-A77UMVMD.cjs.map → chunk-UMCCYXFZ.cjs.map} +1 -1
  184. package/dist/{chunk-3GTN4HOO.js → chunk-UNP4NCMZ.js} +3 -3
  185. package/dist/{chunk-3GTN4HOO.js.map → chunk-UNP4NCMZ.js.map} +1 -1
  186. package/dist/{chunk-BPL5CVZ5.js → chunk-VFT63FJF.js} +3 -3
  187. package/dist/{chunk-BPL5CVZ5.js.map → chunk-VFT63FJF.js.map} +1 -1
  188. package/dist/{chunk-3KIUY67P.cjs → chunk-VHY6NVCJ.cjs} +30 -30
  189. package/dist/chunk-VHY6NVCJ.cjs.map +1 -0
  190. package/dist/{chunk-ZQWRUJHE.js → chunk-WAKGYSQ6.js} +50 -50
  191. package/dist/{chunk-ZQWRUJHE.js.map → chunk-WAKGYSQ6.js.map} +1 -1
  192. package/dist/{chunk-FJWQP6EE.js → chunk-WJ6OGKAO.js} +3 -3
  193. package/dist/{chunk-FJWQP6EE.js.map → chunk-WJ6OGKAO.js.map} +1 -1
  194. package/dist/{chunk-FLDHYF5C.cjs → chunk-X2XLSE2N.cjs} +9 -9
  195. package/dist/chunk-X2XLSE2N.cjs.map +1 -0
  196. package/dist/{chunk-UDAKAE4Z.cjs → chunk-X4ZYEZPD.cjs} +12 -12
  197. package/dist/chunk-X4ZYEZPD.cjs.map +1 -0
  198. package/dist/{chunk-QG4SXZ2A.cjs → chunk-XR6O6G3B.cjs} +27 -27
  199. package/dist/chunk-XR6O6G3B.cjs.map +1 -0
  200. package/dist/{chunk-BWYC7RZG.cjs → chunk-XTQWTVVI.cjs} +11 -11
  201. package/dist/chunk-XTQWTVVI.cjs.map +1 -0
  202. package/dist/{chunk-MLJDW2YK.js → chunk-YSGS57DA.js} +4 -4
  203. package/dist/{chunk-MLJDW2YK.js.map → chunk-YSGS57DA.js.map} +1 -1
  204. package/dist/{chunk-KCM3RMKR.js → chunk-YUQNHLKN.js} +8 -8
  205. package/dist/{chunk-KCM3RMKR.js.map → chunk-YUQNHLKN.js.map} +1 -1
  206. package/dist/{chunk-P7QYG4RZ.cjs → chunk-Z5MB2HAN.cjs} +8 -8
  207. package/dist/{chunk-P7QYG4RZ.cjs.map → chunk-Z5MB2HAN.cjs.map} +1 -1
  208. package/dist/{chunk-HJ2LV464.js → chunk-ZUWVO442.js} +4 -4
  209. package/dist/{chunk-HJ2LV464.js.map → chunk-ZUWVO442.js.map} +1 -1
  210. package/dist/{compact-session-CHBJOYWP.cjs → compact-session-PIDZQ7AR.cjs} +21 -21
  211. package/dist/{compact-session-CHBJOYWP.cjs.map → compact-session-PIDZQ7AR.cjs.map} +1 -1
  212. package/dist/{compact-session-F3J3FMEU.js → compact-session-ZPEJSTLA.js} +12 -12
  213. package/dist/{compact-session-F3J3FMEU.js.map → compact-session-ZPEJSTLA.js.map} +1 -1
  214. package/dist/context/index.cjs +11 -11
  215. package/dist/context/index.js +6 -6
  216. package/dist/context-4XSD2V3G.js +7 -0
  217. package/dist/{context-7EBZST3L.js.map → context-4XSD2V3G.js.map} +1 -1
  218. package/dist/context-ZX2VBGSX.cjs +24 -0
  219. package/dist/{context-4RSI3AXT.cjs.map → context-ZX2VBGSX.cjs.map} +1 -1
  220. package/dist/cron.cjs +40 -40
  221. package/dist/cron.js +39 -39
  222. package/dist/eval.cjs +51 -51
  223. package/dist/eval.cjs.map +1 -1
  224. package/dist/eval.js +41 -41
  225. package/dist/{executor-THUXNAFR.js → executor-ZHIUSHRV.js} +7 -7
  226. package/dist/{executor-THUXNAFR.js.map → executor-ZHIUSHRV.js.map} +1 -1
  227. package/dist/{executor-5QL5AXIZ.cjs → executor-ZTLJZ5LK.cjs} +24 -24
  228. package/dist/{executor-5QL5AXIZ.cjs.map → executor-ZTLJZ5LK.cjs.map} +1 -1
  229. package/dist/filesystem/index.cjs +14 -14
  230. package/dist/filesystem/index.cjs.map +1 -1
  231. package/dist/filesystem/index.js +4 -4
  232. package/dist/{fork-agent-S2EPIML7.js → fork-agent-BHVTALT5.js} +3 -3
  233. package/dist/{fork-agent-S2EPIML7.js.map → fork-agent-BHVTALT5.js.map} +1 -1
  234. package/dist/{fork-agent-WAEQI2DL.cjs → fork-agent-DCOPPXM2.cjs} +4 -4
  235. package/dist/{fork-agent-WAEQI2DL.cjs.map → fork-agent-DCOPPXM2.cjs.map} +1 -1
  236. package/dist/fs-session-store-EZ32TFGB.js +11 -0
  237. package/dist/{fs-session-store-MYQEVUFL.js.map → fs-session-store-EZ32TFGB.js.map} +1 -1
  238. package/dist/fs-session-store-G664LDDN.cjs +20 -0
  239. package/dist/{fs-session-store-CYNTEPJZ.cjs.map → fs-session-store-G664LDDN.cjs.map} +1 -1
  240. package/dist/generate-object-JFZTK567.cjs +20 -0
  241. package/dist/{generate-object-A5F576IT.cjs.map → generate-object-JFZTK567.cjs.map} +1 -1
  242. package/dist/{generate-object-TBDOBBYW.js → generate-object-RPBXY2U3.js} +4 -4
  243. package/dist/{generate-object-TBDOBBYW.js.map → generate-object-RPBXY2U3.js.map} +1 -1
  244. package/dist/index-manager-2MJEEN2O.js +18 -0
  245. package/dist/{index-manager-NF2WRLOU.js.map → index-manager-2MJEEN2O.js.map} +1 -1
  246. package/dist/index-manager-CT6MMG7S.cjs +27 -0
  247. package/dist/{index-manager-2KH75BRW.cjs.map → index-manager-CT6MMG7S.cjs.map} +1 -1
  248. package/dist/index.cjs +130 -130
  249. package/dist/index.cjs.map +1 -1
  250. package/dist/index.js +56 -56
  251. package/dist/{inject-session-LC2TROHM.js → inject-session-OMBJKMVD.js} +6 -6
  252. package/dist/{inject-session-LC2TROHM.js.map → inject-session-OMBJKMVD.js.map} +1 -1
  253. package/dist/{inject-session-T4RXANG3.cjs → inject-session-Z2HIB6P6.cjs} +8 -8
  254. package/dist/{inject-session-T4RXANG3.cjs.map → inject-session-Z2HIB6P6.cjs.map} +1 -1
  255. package/dist/internal/llm/openai.d.ts +2 -0
  256. package/dist/internal/llm/responses.d.ts +2 -0
  257. package/dist/internal/llm/sse.d.ts +6 -0
  258. package/dist/internal/memory/adapters/index.cjs +4 -4
  259. package/dist/internal/memory/adapters/index.js +3 -3
  260. package/dist/internal/memory/storage/index.cjs +45 -45
  261. package/dist/internal/memory/storage/index.js +9 -9
  262. package/dist/internal/persistence/index.cjs +22 -22
  263. package/dist/internal/persistence/index.js +6 -6
  264. package/dist/internal/security/index.cjs +9 -9
  265. package/dist/internal/security/index.js +2 -2
  266. package/dist/mcp-auth.cjs +8 -8
  267. package/dist/mcp-auth.js +3 -3
  268. package/dist/models.cjs +30 -30
  269. package/dist/models.cjs.map +1 -1
  270. package/dist/models.js +8 -8
  271. package/dist/{oauth-transaction-store-U34YLEGP.js → oauth-transaction-store-QQBWGNE2.js} +3 -3
  272. package/dist/{oauth-transaction-store-U34YLEGP.js.map → oauth-transaction-store-QQBWGNE2.js.map} +1 -1
  273. package/dist/{oauth-transaction-store-OMX436YB.cjs → oauth-transaction-store-YF7WOA6B.cjs} +10 -10
  274. package/dist/{oauth-transaction-store-OMX436YB.cjs.map → oauth-transaction-store-YF7WOA6B.cjs.map} +1 -1
  275. package/dist/path-safety.cjs +9 -9
  276. package/dist/path-safety.js +2 -2
  277. package/dist/persistence.cjs +41 -41
  278. package/dist/persistence.cjs.map +1 -1
  279. package/dist/persistence.js +11 -11
  280. package/dist/project.cjs +7 -7
  281. package/dist/project.cjs.map +1 -1
  282. package/dist/project.js +4 -4
  283. package/dist/providers.cjs +6 -6
  284. package/dist/providers.js +4 -4
  285. package/dist/{registry-5TDAF7RK.js → registry-DOYBR237.js} +6 -6
  286. package/dist/{registry-5TDAF7RK.js.map → registry-DOYBR237.js.map} +1 -1
  287. package/dist/{registry-W7TSCLQO.cjs → registry-PB7JFVUJ.cjs} +14 -14
  288. package/dist/{registry-W7TSCLQO.cjs.map → registry-PB7JFVUJ.cjs.map} +1 -1
  289. package/dist/sandbox/index.cjs +41 -41
  290. package/dist/sandbox/index.cjs.map +1 -1
  291. package/dist/sandbox/index.js +7 -7
  292. package/dist/sanitize/index.cjs +2 -2
  293. package/dist/sanitize/index.js +1 -1
  294. package/dist/server/auth/index.cjs +9 -9
  295. package/dist/server/auth/index.cjs.map +1 -1
  296. package/dist/server/auth/index.js +4 -4
  297. package/dist/{session-transcript-7TZ6HXSX.cjs → session-transcript-EBVGLZ6B.cjs} +17 -17
  298. package/dist/{session-transcript-7TZ6HXSX.cjs.map → session-transcript-EBVGLZ6B.cjs.map} +1 -1
  299. package/dist/{session-transcript-BSHZYYY6.js → session-transcript-GU2VE7JX.js} +4 -4
  300. package/dist/{session-transcript-BSHZYYY6.js.map → session-transcript-GU2VE7JX.js.map} +1 -1
  301. package/dist/skills.cjs +7 -7
  302. package/dist/skills.js +4 -4
  303. package/dist/stream-object-3AU7QDHT.js +7 -0
  304. package/dist/{stream-object-EKM4WGTI.js.map → stream-object-3AU7QDHT.js.map} +1 -1
  305. package/dist/stream-object-SLGR2KIA.cjs +20 -0
  306. package/dist/{stream-object-AS4YYM4B.cjs.map → stream-object-SLGR2KIA.cjs.map} +1 -1
  307. package/dist/subagents-loader-4PQS7ULK.cjs +16 -0
  308. package/dist/{subagents-loader-KLWF4QPD.cjs.map → subagents-loader-4PQS7ULK.cjs.map} +1 -1
  309. package/dist/subagents-loader-CPFVLWLH.js +7 -0
  310. package/dist/{subagents-loader-SXETS2F2.js.map → subagents-loader-CPFVLWLH.js.map} +1 -1
  311. package/dist/subagents-loader.cjs +4 -4
  312. package/dist/subagents-loader.js +3 -3
  313. package/dist/subagents.cjs +2 -2
  314. package/dist/subagents.js +1 -1
  315. package/dist/task-store.cjs +5 -5
  316. package/dist/task-store.js +2 -2
  317. package/dist/workflow.cjs +28 -28
  318. package/dist/workflow.js +5 -5
  319. package/docs/error-codes.md +5 -4
  320. package/package.json +1 -1
  321. package/dist/agent-66U5QCPF.js +0 -63
  322. package/dist/agent-CB27KF6O.cjs +0 -72
  323. package/dist/agent-session-store-22GKA7O4.js +0 -6
  324. package/dist/agent-session-store-WP6NGDVX.cjs +0 -23
  325. package/dist/chunk-26GI3OYA.js.map +0 -1
  326. package/dist/chunk-26UQG242.cjs.map +0 -1
  327. package/dist/chunk-3E3VSMB6.cjs.map +0 -1
  328. package/dist/chunk-3KIUY67P.cjs.map +0 -1
  329. package/dist/chunk-3WKWBYPO.cjs.map +0 -1
  330. package/dist/chunk-5645PMVU.cjs.map +0 -1
  331. package/dist/chunk-AT2PESXB.cjs.map +0 -1
  332. package/dist/chunk-AUH2EVWY.cjs.map +0 -1
  333. package/dist/chunk-BWYC7RZG.cjs.map +0 -1
  334. package/dist/chunk-CHKV2L2Y.cjs.map +0 -1
  335. package/dist/chunk-EQ5IDL6W.cjs.map +0 -1
  336. package/dist/chunk-FLDHYF5C.cjs.map +0 -1
  337. package/dist/chunk-FTXY4D44.cjs.map +0 -1
  338. package/dist/chunk-L7V4DDIE.cjs.map +0 -1
  339. package/dist/chunk-QG4SXZ2A.cjs.map +0 -1
  340. package/dist/chunk-SA6K24NB.cjs.map +0 -1
  341. package/dist/chunk-SETHRLQS.cjs.map +0 -1
  342. package/dist/chunk-SPEN7RJU.cjs +0 -18
  343. package/dist/chunk-SWALPLFQ.cjs.map +0 -1
  344. package/dist/chunk-TDED5XES.js.map +0 -1
  345. package/dist/chunk-UDAKAE4Z.cjs.map +0 -1
  346. package/dist/chunk-VWYFGE27.cjs.map +0 -1
  347. package/dist/chunk-XOU3CL7S.cjs.map +0 -1
  348. package/dist/chunk-YQ3XC6C2.cjs.map +0 -1
  349. package/dist/chunk-ZOFRNNZD.cjs.map +0 -1
  350. package/dist/context-4RSI3AXT.cjs +0 -24
  351. package/dist/context-7EBZST3L.js +0 -7
  352. package/dist/fs-session-store-CYNTEPJZ.cjs +0 -20
  353. package/dist/fs-session-store-MYQEVUFL.js +0 -11
  354. package/dist/generate-object-A5F576IT.cjs +0 -20
  355. package/dist/index-manager-2KH75BRW.cjs +0 -27
  356. package/dist/index-manager-NF2WRLOU.js +0 -18
  357. package/dist/stream-object-AS4YYM4B.cjs +0 -20
  358. package/dist/stream-object-EKM4WGTI.js +0 -7
  359. package/dist/subagents-loader-KLWF4QPD.cjs +0 -16
  360. package/dist/subagents-loader-SXETS2F2.js +0 -7
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/persistence/session-writer.ts"],"names":["TheokitAgentError","hostname","openSync","writeSync","fchmodSync","closeSync","readFileSync","existsSync","rmSync"],"mappings":";;;;;;AAmDO,IAAM,gBAAA,GAAN,cAA+BA,mCAAA,CAAkB;AAAA,EAGtD,YAAqB,WAAA,EAAqB;AACxC,IAAA,KAAA;AAAA,MACE,oDAAoD,WAAW,CAAA,gJAAA,CAAA;AAAA,MAG/D,EAAE,IAAA,EAAM,cAAA,EAAgB,WAAA,EAAa,KAAA;AAAM,KAC7C;AANmB,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EAOrB;AAAA,EAPqB,WAAA;AAAA,EAFH,IAAA,GAAO,kBAAA;AAU3B;AA0CO,IAAM,mBAAA,GAAsB,GAAA;AAUnC,SAAS,QAAA,GAAsB;AAC7B,EAAA,OAAO,EAAE,GAAA,EAAK,OAAA,CAAQ,GAAA,EAAK,QAAA,EAAUC,kBAAS,EAAG,KAAA,EAAO,IAAA,CAAK,GAAA,EAAI,EAAE;AACrE;AAWA,SAAS,UAAA,CAAW,UAAkB,KAAA,EAAwB;AAC5D,EAAA,MAAM,EAAA,GAAKC,gBAAA,CAAS,QAAA,EAAU,GAAA,EAAK,GAAK,CAAA;AACxC,EAAA,IAAI;AACF,IAAAC,iBAAA,CAAU,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAInC,IAAAC,kBAAA,CAAW,IAAI,GAAK,CAAA;AAAA,EACtB,CAAA,SAAE;AACA,IAAAC,iBAAA,CAAU,EAAE,CAAA;AAAA,EACd;AACF;AAGA,SAAS,UAAU,QAAA,EAAyC;AAC1D,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAMC,oBAAA,CAAa,UAAU,MAAM,CAAA;AAAA,EACrC,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,OAAQ,GAAA,CAA8B,IAAA;AAE5C,IAAA,IAAI,IAAA,KAAS,UAAU,OAAO,MAAA;AAO9B,IAAA,IAAI,IAAA,KAAS,UAAU,OAAO,MAAA;AAQ9B,IAAA,MAAM,IAAI,gBAAA,CAAiB,QAAA,CAAS,OAAA,CAAQ,iBAAA,EAAmB,EAAE,CAAC,CAAA;AAAA,EACpE;AACA,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACxB,IAAA,IACE,OAAO,CAAA,CAAE,GAAA,KAAQ,QAAA,IACjB,OAAO,CAAA,CAAE,QAAA,KAAa,QAAA,IACtB,OAAO,CAAA,CAAE,KAAA,KAAU,QAAA,EACnB;AACA,MAAA,OAAO,KAAA,CAAA;AAAA,IACT;AACA,IAAA,OAAO,EAAE,KAAK,CAAA,CAAE,GAAA,EAAK,UAAU,CAAA,CAAE,QAAA,EAAU,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM;AAAA,EAC5D,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAGA,SAAS,aAAa,GAAA,EAAsB;AAC1C,EAAA,IAAI;AACF,IAAA,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAC,CAAA;AACnB,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,GAAA,EAAK;AAEZ,IAAA,OAAQ,IAA8B,IAAA,KAAS,OAAA;AAAA,EACjD;AACF;AAwBA,SAAS,YAAY,KAAA,EAAuC;AAC1D,EAAA,IAAI,KAAA,KAAU,QAAW,OAAO,IAAA;AAChC,EAAA,IAAI,KAAA,CAAM,QAAA,KAAaL,gBAAA,EAAS,EAAG;AACjC,IAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA,CAAM,KAAA,GAAQ,mBAAA;AAAA,EACpC;AACA,EAAA,OAAO,CAAC,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA;AAChC;AAcO,SAAS,iBAAiB,WAAA,EAA8B;AAC7D,EAAA,MAAM,QAAA,GAAW,GAAG,WAAW,CAAA,YAAA,CAAA;AAC/B,EAAA,IAAI,CAACM,kBAAA,CAAW,QAAQ,CAAA,EAAG,OAAO,KAAA;AAClC,EAAA,IAAI;AACF,IAAA,OAAO,CAAC,WAAA,CAAY,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,EACzC,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AASA,eAAsB,qBAAqB,WAAA,EAAkD;AAU3F,EAAA,MAAM,QAAA,GAAW,GAAG,WAAW,CAAA,YAAA,CAAA;AAC/B,EAAA,MAAM,OAAkB,QAAA,EAAS;AACjC,EAAA,IAAI,EAAA;AACJ,EAAA,IAAI;AAEF,IAAA,EAAA,GAAKL,gBAAA,CAAS,QAAA,EAAU,IAAA,EAAM,GAAK,CAAA;AAAA,EACrC,SAAS,GAAA,EAAK;AACZ,IAAA,IAAK,GAAA,CAA8B,IAAA,KAAS,QAAA,EAAU,MAAM,GAAA;AAI5D,IAAA,IAAI,CAAC,YAAY,SAAA,CAAU,QAAQ,CAAC,CAAA,EAAG,MAAM,IAAI,gBAAA,CAAiB,WAAW,CAAA;AAC7E,IAAA,UAAA,CAAW,UAAU,IAAI,CAAA;AACzB,IAAA,OAAO,WAAA,CAAY,aAAa,QAAQ,CAAA;AAAA,EAC1C;AACA,EAAAG,iBAAA,CAAU,EAAE,CAAA;AACZ,EAAA,UAAA,CAAW,UAAU,IAAI,CAAA;AAEzB,EAAA,OAAO,WAAA,CAAY,aAAa,QAAQ,CAAA;AAC1C;AAGA,SAAS,WAAA,CAAY,aAAqB,QAAA,EAAsC;AAC9E,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,SAAS,YAA2B;AAClC,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAAG,cAAA,CAAO,QAAA,EAAU,EAAE,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,IAClC,CAAA;AAAA,IACA,OAAO,MAAY;AAIjB,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,UAAA,CAAW,QAAA,EAAU,UAAU,CAAA;AAAA,IACjC;AAAA,GACF;AACF","file":"chunk-MKS5FFH5.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,9 +1,9 @@
1
- import { pluginBundleRoots, projectConfigRoots } from './chunk-YMVTUV5K.js';
2
- import { readWorkspaceDir, parseSimpleYaml } from './chunk-Q7UG242V.js';
1
+ import { pluginBundleRoots, projectConfigRoots } from './chunk-PISZRLES.js';
2
+ import { readWorkspaceDir, parseSimpleYaml } from './chunk-TIYQDJFK.js';
3
3
  import { ConfigurationError } from './chunk-TNFOU6SB.js';
4
4
  import { diag } from './chunk-3OY6H3I7.js';
5
- import { readdir, readFile } from 'fs/promises';
6
- import { join } from 'path';
5
+ import { readdir, readFile } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
7
 
8
8
  async function pluginBundleDirs(cwd, compatSources = []) {
9
9
  const dirs = [];
@@ -203,5 +203,5 @@ function parseFrontmatterFields(frontmatter) {
203
203
  }
204
204
 
205
205
  export { loadSubagents, pluginBundleDirs };
206
- //# sourceMappingURL=chunk-WM646HEN.js.map
207
- //# sourceMappingURL=chunk-WM646HEN.js.map
206
+ //# sourceMappingURL=chunk-MTNCJ7MQ.js.map
207
+ //# sourceMappingURL=chunk-MTNCJ7MQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/runtime/plugin-loader/plugin-bundles.ts","../src/internal/runtime/skills/subagents-loader.ts"],"names":["join"],"mappings":"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","file":"chunk-WM646HEN.js","sourcesContent":["/**\n * Locating the plugin bundles a project carries.\n *\n * Shared by the skills and subagents loaders, which both need the same answer to \"which folders in\n * this project are plugins\" and would otherwise each grow their own copy of the directory walk.\n *\n * @internal\n */\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pluginBundleRoots } from \"../../persistence/paths.js\";\nimport type { CompatSourceDeclaration } from \"../compat/foreign-config-sources.js\";\n\n/**\n * Every plugin folder under the project's plugin roots.\n *\n * Returns the FOLDERS, not their contents — what a bundle contributes (`skills/`, `agents/`) is the\n * caller's business, and a loader that also knew the layout would have to change whenever the other\n * one did.\n *\n * A missing root is not an error: most projects carry no plugins, and treating their absence as a\n * failure would make \"none installed\" indistinguishable from \"the directory could not be read\".\n */\nexport async function pluginBundleDirs(\n cwd: string,\n /** Declared foreign dialects (#524). Empty reads `.theokit/plugins` only. */\n compatSources: readonly CompatSourceDeclaration[] = [],\n): Promise<string[]> {\n const dirs: string[] = [];\n for (const root of pluginBundleRoots(cwd, compatSources)) {\n let entries: Dirent[];\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.isDirectory()) dirs.push(join(root, entry.name));\n }\n }\n return dirs;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport type { AgentDefinition } from \"../../../types/agent.js\";\nimport type { ModelSelection } from \"../../../types/agent-prims.js\";\nimport { diag } from \"../../diagnostics.js\";\nimport { projectConfigRoots } from \"../../persistence/paths.js\";\nimport type { CompatSourceDeclaration } from \"../compat/foreign-config-sources.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\nimport { pluginBundleDirs } from \"../plugin-loader/plugin-bundles.js\";\n\n/**\n * Load file-based subagents from `.theokit/agents/*.md` and merge with\n * inline definitions. Inline overrides file-based on name conflict.\n *\n * Each markdown file has YAML frontmatter (description + optional model)\n * and a body that becomes the subagent prompt.\n *\n * @internal\n */\nexport async function loadSubagents(\n cwd: string,\n settingSourcesIncludeProject: boolean,\n inline: Record<string, AgentDefinition> | undefined,\n /** Declared foreign dialects (#524). Empty reads `.theokit/` only. */\n compatSources: readonly CompatSourceDeclaration[] = [],\n): Promise<Record<string, AgentDefinition>> {\n const result: Record<string, AgentDefinition> = {};\n if (settingSourcesIncludeProject) {\n const projectAgents = await readProjectSubagents(cwd, compatSources);\n for (const [name, definition] of Object.entries(projectAgents)) {\n result[name] = definition;\n }\n }\n if (inline !== undefined) {\n for (const [name, definition] of Object.entries(inline)) {\n result[name] = definition;\n }\n }\n return result;\n}\n\n/**\n * Read agent declarations from every project config root (`.theokit`, then `.claude`).\n *\n * FIRST occurrence of a name wins, which is what makes `projectConfigRoots`' order a contract rather\n * than a detail: a project declaring the same agent in both means the explicit namespace.\n */\nasync function readProjectSubagents(\n cwd: string,\n compatSources: readonly CompatSourceDeclaration[],\n): Promise<Record<string, AgentDefinition>> {\n const subagents: Record<string, AgentDefinition> = {};\n for (const configRoot of projectConfigRoots(cwd, compatSources, \"subagents\")) {\n await readSubagentsFrom(join(configRoot, \"agents\"), subagents);\n }\n // A Claude Code plugin is a BUNDLE, and its `agents/` is what it exists to contribute. Read after\n // the project's own, so a project can shadow an agent a plugin ships without editing the plugin.\n for (const bundle of await pluginBundleDirs(cwd, compatSources)) {\n await readSubagentsFrom(join(bundle, \"agents\"), subagents);\n }\n return subagents;\n}\n\n/**\n * Parse an agent file, or return null when it was written for another runtime.\n *\n * ## What this changed, and what it deliberately did not\n *\n * The failure used to take the whole directory, and that was a decision rather than an oversight:\n * \"a file that HAS frontmatter and gets it wrong is a broken agent and still fails loudly, which is\n * what keeps a typo'd `sandbox` from returning as a silent gate through this door. Isolating the\n * failure per file would hand that risk back.\"\n *\n * The risk named there is real and is NOT handed back, because the isolation is narrower than the\n * one that argument refuses. Only `KNOWN_CLAUDE_CODE_FIELDS` takes this door — the set that already\n * encodes \"this key belongs to another runtime and carries no behaviour here\". A misspelling of one\n * of OUR keys carries `subagent_unknown_field` and still propagates, so `sandboxx` remains fatal\n * and cannot return as a silent gate. `a-claude-code-field-names-itself.test.ts` pins both halves.\n *\n * What moved the decision was the cost, measured in a live TUI on 2026-09-15: one ported\n * `.claude/agents/*.md` carrying `memory:` stopped every sibling agent from loading and the turn\n * produced no answer at all — on an agent the task never used. Beside it, on the same prompt and\n * the same project, another runtime answered normally. The diagnosis fix this file shipped tells a\n * user which key is foreign; it does not give them back the turn.\n *\n * The loader already draws exactly this line for a file with no frontmatter, and for the same\n * stated reason: one of them must not stop every agent in the directory from loading.\n */\nfunction parseOrSkipForeignRuntime(\n raw: string,\n filename: string,\n): { name: string; definition: AgentDefinition } | null {\n try {\n return parseSubagentMarkdown(raw, filename);\n } catch (error) {\n if (error instanceof ConfigurationError && error.code === \"subagent_foreign_runtime_field\") {\n diag(`[theokit-sdk] ${filename}: ${error.message} — skipping this agent`);\n return null;\n }\n throw error;\n }\n}\n\nasync function readSubagentsFrom(\n root: string,\n subagents: Record<string, AgentDefinition>,\n): Promise<void> {\n const entries = await readWorkspaceDir(root, \"subagents_read_error\", \"subagents directory\");\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue;\n const path = join(root, entry.name);\n const raw = await readFile(path, \"utf8\");\n // A markdown file with NO frontmatter is not an agent declaration — a directory of agents\n // written for the Claude Code CLI conventionally carries documentation beside them, and\n // `.claude/agents/README.md` exists in this repository. Throwing on it made ONE such file stop\n // every agent in the directory from loading.\n //\n // Skipped with a warn rather than in silence, and ONLY for the no-frontmatter case: a file that\n // HAS frontmatter and gets it wrong is a broken agent and still fails loudly, which is what\n // keeps a typo'd `sandbox` from returning as a silent gate through this door.\n if (!hasFrontmatter(raw)) {\n diag(`[theokit-sdk] ${entry.name} has no frontmatter — not an agent declaration, skipping`);\n continue;\n }\n const definition = parseOrSkipForeignRuntime(raw, entry.name);\n if (definition === null) continue;\n if (subagents[definition.name] === undefined) {\n // `path` is computed above to read the file and was then dropped. Keeping it is the whole\n // visibility fix (#524): without it a listing cannot say which root an agent came from.\n subagents[definition.name] = { ...definition.definition, source: path };\n }\n }\n}\n\n// The frontmatter keys a disk subagent may declare. Any other key is a typed load\n// error rather than a silent drop — a dropped `sandbox` an operator wrote believing\n// it confines the child is exactly the silent-gate failure class this guards against.\nconst ACCEPTED_FIELDS = new Set([\n \"name\",\n \"description\",\n \"model\",\n \"tools\",\n \"reasoning_effort\",\n \"mcp\",\n \"sandbox\",\n \"memory\",\n]);\n\n// Fields the Claude Code CLI writes that carry NO behaviour for this runtime. Accepted and ignored,\n// so an agent authored for the CLI loads here unchanged — measured 2026-08-26 across the 59 agent\n// files on one machine, where `color` appeared in 38 of them and made every one of those a\n// `subagent_unknown_field` load error.\n//\n// Named explicitly instead of loosening the check above, because that check's reason is sound: a\n// dropped `sandbox` an operator wrote believing it confines the child is a silent gate. A field that\n// COULD change behaviour must still fail loudly. This set is the difference between \"we know this\n// one and it does nothing\" and \"we have never heard of this\" — two facts a bare allow-everything\n// would collapse into one.\n//\n// Anything added here needs the same justification: inert for THIS runtime, not merely unfamiliar.\nconst INERT_CLAUDE_CODE_FIELDS = new Set([\n /** The CLI's label colour for the agent. Presentation only. */\n \"color\",\n]);\n\nfunction parseSubagentMarkdown(\n raw: string,\n filename: string,\n): { name: string; definition: AgentDefinition } {\n const { frontmatter, body } = splitFrontmatter(raw, filename);\n const fields = parseFrontmatterFields(frontmatter);\n rejectUnknownFields(fields, filename);\n rejectMcp(fields, filename);\n\n const definition: AgentDefinition = {\n description: asString(fields.description) ?? \"\",\n prompt: body,\n };\n const model = resolveModel(fields, filename);\n if (model !== undefined) definition.model = model;\n const tools = toStringList(fields.tools);\n if (tools.length > 0) definition.tools = tools;\n const sandbox = resolveSandbox(fields, filename);\n if (sandbox !== undefined) definition.sandbox = sandbox;\n // Carried unjudged — see `AgentDefinition.memory`. Which root a scope names, and whether the\n // name is one at all, is `@theokit/agents`' decision and refusing it twice means two lists.\n const memory = asString(fields.memory);\n if (memory !== undefined) definition.memory = memory;\n\n const name = asString(fields.name) ?? filename.replace(/\\.md$/, \"\");\n return { name, definition };\n}\n\n/**\n * Frontmatter keys that Claude Code documents for a subagent and this runtime does not accept.\n *\n * Not an allow-list — every one of these still fails the load. It exists so the FAILURE can say\n * which kind of unknown it met. A user migrating a `.claude/agents/` tree used to learn one key per\n * round trip: fix `memory`, meet `permissionMode`, fix that, meet `maxTurns` — with nothing saying\n * the set was finite or that the tree was simply written for another runtime.\n *\n * The distinction this file already draws for `INERT_CLAUDE_CODE_FIELDS` is the same one: \"the\n * difference between 'we know this one and it does nothing' and 'we have never heard of this' — two\n * facts a bare allow-everything would collapse into one.\" This is the third fact beside them —\n * \"another runtime's field\" — and it changes only the message, never the verdict.\n */\nconst KNOWN_CLAUDE_CODE_FIELDS = new Set([\n \"disallowedTools\",\n \"permissionMode\",\n \"maxTurns\",\n \"skills\",\n \"background\",\n \"effort\",\n \"isolation\",\n \"initialPrompt\",\n \"hooks\",\n \"experimental\",\n \"mcpServers\",\n]);\n\n/**\n * The typed error for a key we do not accept. Built here rather than inline so `rejectUnknownFields`\n * stays a plain loop: the message has two shapes and the code has two values, and computing all four\n * at the throw site is what pushed that function past the cognitive-complexity gate.\n */\nfunction unknownFieldError(key: string, filename: string): ConfigurationError {\n const foreign = KNOWN_CLAUDE_CODE_FIELDS.has(key);\n const siblings = [...KNOWN_CLAUDE_CODE_FIELDS].filter((f) => f !== key);\n const origin = foreign\n ? ` — \"${key}\" is a Claude Code subagent field that this runtime does not support. ` +\n `The same applies to: ${siblings.join(\", \")}. Removing them one at a time will meet each ` +\n `in turn; the tree is written for another runtime`\n : \"\";\n // The two codes are written as LITERALS, one per branch, rather than as a ternary inside the\n // options object. `tools/generate-error-codes.mjs` walks the source for the literal assigned to\n // `code:`, so a ternary there is not a literal and it extracted NEITHER — measured 2026-09-15,\n // when `quality:docs-errors` went red and the regenerated reference had lost\n // `subagent_unknown_field` without ever gaining `subagent_foreign_runtime_field`. Two real,\n // throwable codes were absent from the published error reference, which is the one place a\n // consumer looks to find out what they can catch.\n if (foreign)\n return new ConfigurationError(\n `Subagent ${filename}: unknown frontmatter field \"${key}\" (accepted: ${[...ACCEPTED_FIELDS].join(\", \")})${origin}`,\n { code: \"subagent_foreign_runtime_field\" },\n );\n return new ConfigurationError(\n `Subagent ${filename}: unknown frontmatter field \"${key}\" (accepted: ${[...ACCEPTED_FIELDS].join(\", \")})${origin}`,\n { code: \"subagent_unknown_field\" },\n );\n}\n\nfunction rejectUnknownFields(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): void {\n for (const key of Object.keys(fields)) {\n if (INERT_CLAUDE_CODE_FIELDS.has(key)) continue;\n if (!ACCEPTED_FIELDS.has(key)) throw unknownFieldError(key, filename);\n }\n}\n\n// mcp: a known field, but not yet honored on the LOCAL delegation path. The frontmatter YAML can only\n// express server NAMES (parseSimpleYaml has no nested-object support), while a child's `Agent.create`\n// needs `mcpServers` as a Record<name, config>; resolving names→config per-subagent in local delegation\n// is its own follow-up. Rather than silently drop it (the M26/M32 silent-gate class), it is a typed load\n// error that names the field and points at the alternative.\nfunction rejectMcp(fields: Record<string, FrontmatterValue | undefined>, filename: string): void {\n if (fields.mcp !== undefined) {\n throw new ConfigurationError(\n `Subagent ${filename}: per-subagent \"mcp\" is not yet supported on the local delegation path; declare MCP servers in .theokit/mcp.json (or the parent) instead`,\n { code: \"subagent_mcp_unsupported_local\" },\n );\n }\n}\n\n// model + reasoning_effort — effort rides inside `model.params[thinking]`, so it requires a concrete\n// model id to attach to (a child inheriting the parent's model cannot carry the parent's provider-\n// specific effort param safely).\nfunction resolveModel(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): ModelSelection | \"inherit\" | undefined {\n const modelId = asString(fields.model);\n // An empty/blank effort (`reasoning_effort: \"\"`) names nothing: treated as absent, never as a\n // `{ id: \"thinking\", value: \"\" }` param the provider would drop or reject.\n const rawEffort = asString(fields.reasoning_effort)?.trim();\n const effort = rawEffort === \"\" ? undefined : rawEffort;\n // reasoning_effort rides in model.params[thinking], so it needs a CONCRETE model id to attach to.\n // Neither an absent model NOR `model: inherit` can carry it (the inherited id is unknown at load), so\n // both are typed errors rather than a silently-dropped effort — the silent-gate class this guards.\n if (effort !== undefined && (modelId === undefined || modelId === \"inherit\")) {\n throw new ConfigurationError(\n `Subagent ${filename}: reasoning_effort requires a concrete model (effort is a model parameter; an absent model or \"inherit\" cannot carry it)`,\n { code: \"subagent_reasoning_effort_without_model\" },\n );\n }\n if (modelId === undefined) return undefined;\n if (modelId === \"inherit\") return \"inherit\";\n return effort !== undefined\n ? { id: modelId, params: [{ id: \"thinking\", value: effort }] }\n : { id: modelId };\n}\n\n// sandbox: boolean only. A granular mode string (read-only/…) is unsupported by the SDK runtime and is\n// a typed error rather than a silent coercion to a boolean.\nfunction resolveSandbox(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): boolean | undefined {\n if (fields.sandbox === undefined) return undefined;\n if (typeof fields.sandbox !== \"boolean\") {\n throw new ConfigurationError(\n `Subagent ${filename}: sandbox must be a boolean (got \"${String(fields.sandbox)}\"); granular sandbox modes are not supported by the runtime`,\n { code: \"subagent_sandbox_not_boolean\" },\n );\n }\n return fields.sandbox;\n}\n\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n if (typeof v !== \"string\") return undefined;\n // parseSimpleYaml does not strip quotes (documented), and `model`/`reasoning_effort` are fields users\n // habitually quote (`model: \"openai/gpt-4o\"`). Strip a single matching surrounding quote pair so a\n // quoted id/effort does not slip past validation and fail only at the provider.\n const m = /^([\"'])(.*)\\1$/.exec(v);\n return m ? m[2] : v;\n}\n\n/** Accept a YAML list (`string[]`) or a comma/space-separated scalar; trim + drop empties. */\nfunction toStringList(v: FrontmatterValue | undefined): string[] {\n if (Array.isArray(v)) return v.map((t) => t.trim()).filter((t) => t.length > 0);\n if (typeof v === \"string\") {\n return v\n .split(/[\\s,]+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n }\n return [];\n}\n\n/** Does this file open with a frontmatter block at all? Its ABSENCE means \"not an agent\". */\nfunction hasFrontmatter(raw: string): boolean {\n return /^---\\s*\\n/.test(raw);\n}\n\nfunction splitFrontmatter(raw: string, filename: string): { frontmatter: string; body: string } {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n([\\s\\S]*)$/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Subagent ${filename} is missing frontmatter`, {\n code: \"subagent_missing_frontmatter\",\n });\n }\n return { frontmatter: match[1] ?? \"\", body: (match[2] ?? \"\").trim() };\n}\n\nfunction parseFrontmatterFields(frontmatter: string): Record<string, FrontmatterValue | undefined> {\n // Preserve the rich YAML value types (boolean/number/string[]): `sandbox: true` and\n // `mcp: [a, b]` are meaningful here, so narrowing everything to string (as the\n // pre-M33 loader did) would drop them. Per-field validation happens in parseSubagentMarkdown.\n return parseSimpleYaml(frontmatter);\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/runtime/plugin-loader/plugin-bundles.ts","../src/internal/runtime/skills/subagents-loader.ts"],"names":["join"],"mappings":"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","file":"chunk-MTNCJ7MQ.js","sourcesContent":["/**\n * Locating the plugin bundles a project carries.\n *\n * Shared by the skills and subagents loaders, which both need the same answer to \"which folders in\n * this project are plugins\" and would otherwise each grow their own copy of the directory walk.\n *\n * @internal\n */\n\nimport type { Dirent } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pluginBundleRoots } from \"../../persistence/paths.js\";\nimport type { CompatSourceDeclaration } from \"../compat/foreign-config-sources.js\";\n\n/**\n * Every plugin folder under the project's plugin roots.\n *\n * Returns the FOLDERS, not their contents — what a bundle contributes (`skills/`, `agents/`) is the\n * caller's business, and a loader that also knew the layout would have to change whenever the other\n * one did.\n *\n * A missing root is not an error: most projects carry no plugins, and treating their absence as a\n * failure would make \"none installed\" indistinguishable from \"the directory could not be read\".\n */\nexport async function pluginBundleDirs(\n cwd: string,\n /** Declared foreign dialects (#524). Empty reads `.theokit/plugins` only. */\n compatSources: readonly CompatSourceDeclaration[] = [],\n): Promise<string[]> {\n const dirs: string[] = [];\n for (const root of pluginBundleRoots(cwd, compatSources)) {\n let entries: Dirent[];\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (entry.isDirectory()) dirs.push(join(root, entry.name));\n }\n }\n return dirs;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport type { AgentDefinition } from \"../../../types/agent.js\";\nimport type { ModelSelection } from \"../../../types/agent-prims.js\";\nimport { diag } from \"../../diagnostics.js\";\nimport { projectConfigRoots } from \"../../persistence/paths.js\";\nimport type { CompatSourceDeclaration } from \"../compat/foreign-config-sources.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\nimport { pluginBundleDirs } from \"../plugin-loader/plugin-bundles.js\";\n\n/**\n * Load file-based subagents from `.theokit/agents/*.md` and merge with\n * inline definitions. Inline overrides file-based on name conflict.\n *\n * Each markdown file has YAML frontmatter (description + optional model)\n * and a body that becomes the subagent prompt.\n *\n * @internal\n */\nexport async function loadSubagents(\n cwd: string,\n settingSourcesIncludeProject: boolean,\n inline: Record<string, AgentDefinition> | undefined,\n /** Declared foreign dialects (#524). Empty reads `.theokit/` only. */\n compatSources: readonly CompatSourceDeclaration[] = [],\n): Promise<Record<string, AgentDefinition>> {\n const result: Record<string, AgentDefinition> = {};\n if (settingSourcesIncludeProject) {\n const projectAgents = await readProjectSubagents(cwd, compatSources);\n for (const [name, definition] of Object.entries(projectAgents)) {\n result[name] = definition;\n }\n }\n if (inline !== undefined) {\n for (const [name, definition] of Object.entries(inline)) {\n result[name] = definition;\n }\n }\n return result;\n}\n\n/**\n * Read agent declarations from every project config root (`.theokit`, then `.claude`).\n *\n * FIRST occurrence of a name wins, which is what makes `projectConfigRoots`' order a contract rather\n * than a detail: a project declaring the same agent in both means the explicit namespace.\n */\nasync function readProjectSubagents(\n cwd: string,\n compatSources: readonly CompatSourceDeclaration[],\n): Promise<Record<string, AgentDefinition>> {\n const subagents: Record<string, AgentDefinition> = {};\n for (const configRoot of projectConfigRoots(cwd, compatSources, \"subagents\")) {\n await readSubagentsFrom(join(configRoot, \"agents\"), subagents);\n }\n // A Claude Code plugin is a BUNDLE, and its `agents/` is what it exists to contribute. Read after\n // the project's own, so a project can shadow an agent a plugin ships without editing the plugin.\n for (const bundle of await pluginBundleDirs(cwd, compatSources)) {\n await readSubagentsFrom(join(bundle, \"agents\"), subagents);\n }\n return subagents;\n}\n\n/**\n * Parse an agent file, or return null when it was written for another runtime.\n *\n * ## What this changed, and what it deliberately did not\n *\n * The failure used to take the whole directory, and that was a decision rather than an oversight:\n * \"a file that HAS frontmatter and gets it wrong is a broken agent and still fails loudly, which is\n * what keeps a typo'd `sandbox` from returning as a silent gate through this door. Isolating the\n * failure per file would hand that risk back.\"\n *\n * The risk named there is real and is NOT handed back, because the isolation is narrower than the\n * one that argument refuses. Only `KNOWN_CLAUDE_CODE_FIELDS` takes this door — the set that already\n * encodes \"this key belongs to another runtime and carries no behaviour here\". A misspelling of one\n * of OUR keys carries `subagent_unknown_field` and still propagates, so `sandboxx` remains fatal\n * and cannot return as a silent gate. `a-claude-code-field-names-itself.test.ts` pins both halves.\n *\n * What moved the decision was the cost, measured in a live TUI on 2026-09-15: one ported\n * `.claude/agents/*.md` carrying `memory:` stopped every sibling agent from loading and the turn\n * produced no answer at all — on an agent the task never used. Beside it, on the same prompt and\n * the same project, another runtime answered normally. The diagnosis fix this file shipped tells a\n * user which key is foreign; it does not give them back the turn.\n *\n * The loader already draws exactly this line for a file with no frontmatter, and for the same\n * stated reason: one of them must not stop every agent in the directory from loading.\n */\nfunction parseOrSkipForeignRuntime(\n raw: string,\n filename: string,\n): { name: string; definition: AgentDefinition } | null {\n try {\n return parseSubagentMarkdown(raw, filename);\n } catch (error) {\n if (error instanceof ConfigurationError && error.code === \"subagent_foreign_runtime_field\") {\n diag(`[theokit-sdk] ${filename}: ${error.message} — skipping this agent`);\n return null;\n }\n throw error;\n }\n}\n\nasync function readSubagentsFrom(\n root: string,\n subagents: Record<string, AgentDefinition>,\n): Promise<void> {\n const entries = await readWorkspaceDir(root, \"subagents_read_error\", \"subagents directory\");\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue;\n const path = join(root, entry.name);\n const raw = await readFile(path, \"utf8\");\n // A markdown file with NO frontmatter is not an agent declaration — a directory of agents\n // written for the Claude Code CLI conventionally carries documentation beside them, and\n // `.claude/agents/README.md` exists in this repository. Throwing on it made ONE such file stop\n // every agent in the directory from loading.\n //\n // Skipped with a warn rather than in silence, and ONLY for the no-frontmatter case: a file that\n // HAS frontmatter and gets it wrong is a broken agent and still fails loudly, which is what\n // keeps a typo'd `sandbox` from returning as a silent gate through this door.\n if (!hasFrontmatter(raw)) {\n diag(`[theokit-sdk] ${entry.name} has no frontmatter — not an agent declaration, skipping`);\n continue;\n }\n const definition = parseOrSkipForeignRuntime(raw, entry.name);\n if (definition === null) continue;\n if (subagents[definition.name] === undefined) {\n // `path` is computed above to read the file and was then dropped. Keeping it is the whole\n // visibility fix (#524): without it a listing cannot say which root an agent came from.\n subagents[definition.name] = { ...definition.definition, source: path };\n }\n }\n}\n\n// The frontmatter keys a disk subagent may declare. Any other key is a typed load\n// error rather than a silent drop — a dropped `sandbox` an operator wrote believing\n// it confines the child is exactly the silent-gate failure class this guards against.\nconst ACCEPTED_FIELDS = new Set([\n \"name\",\n \"description\",\n \"model\",\n \"tools\",\n \"reasoning_effort\",\n \"mcp\",\n \"sandbox\",\n \"memory\",\n]);\n\n// Fields the Claude Code CLI writes that carry NO behaviour for this runtime. Accepted and ignored,\n// so an agent authored for the CLI loads here unchanged — measured 2026-08-26 across the 59 agent\n// files on one machine, where `color` appeared in 38 of them and made every one of those a\n// `subagent_unknown_field` load error.\n//\n// Named explicitly instead of loosening the check above, because that check's reason is sound: a\n// dropped `sandbox` an operator wrote believing it confines the child is a silent gate. A field that\n// COULD change behaviour must still fail loudly. This set is the difference between \"we know this\n// one and it does nothing\" and \"we have never heard of this\" — two facts a bare allow-everything\n// would collapse into one.\n//\n// Anything added here needs the same justification: inert for THIS runtime, not merely unfamiliar.\nconst INERT_CLAUDE_CODE_FIELDS = new Set([\n /** The CLI's label colour for the agent. Presentation only. */\n \"color\",\n]);\n\nfunction parseSubagentMarkdown(\n raw: string,\n filename: string,\n): { name: string; definition: AgentDefinition } {\n const { frontmatter, body } = splitFrontmatter(raw, filename);\n const fields = parseFrontmatterFields(frontmatter);\n rejectUnknownFields(fields, filename);\n rejectMcp(fields, filename);\n\n const definition: AgentDefinition = {\n description: asString(fields.description) ?? \"\",\n prompt: body,\n };\n const model = resolveModel(fields, filename);\n if (model !== undefined) definition.model = model;\n const tools = toStringList(fields.tools);\n if (tools.length > 0) definition.tools = tools;\n const sandbox = resolveSandbox(fields, filename);\n if (sandbox !== undefined) definition.sandbox = sandbox;\n // Carried unjudged — see `AgentDefinition.memory`. Which root a scope names, and whether the\n // name is one at all, is `@theokit/agents`' decision and refusing it twice means two lists.\n const memory = asString(fields.memory);\n if (memory !== undefined) definition.memory = memory;\n\n const name = asString(fields.name) ?? filename.replace(/\\.md$/, \"\");\n return { name, definition };\n}\n\n/**\n * Frontmatter keys that Claude Code documents for a subagent and this runtime does not accept.\n *\n * Not an allow-list — every one of these still fails the load. It exists so the FAILURE can say\n * which kind of unknown it met. A user migrating a `.claude/agents/` tree used to learn one key per\n * round trip: fix `memory`, meet `permissionMode`, fix that, meet `maxTurns` — with nothing saying\n * the set was finite or that the tree was simply written for another runtime.\n *\n * The distinction this file already draws for `INERT_CLAUDE_CODE_FIELDS` is the same one: \"the\n * difference between 'we know this one and it does nothing' and 'we have never heard of this' — two\n * facts a bare allow-everything would collapse into one.\" This is the third fact beside them —\n * \"another runtime's field\" — and it changes only the message, never the verdict.\n */\nconst KNOWN_CLAUDE_CODE_FIELDS = new Set([\n \"disallowedTools\",\n \"permissionMode\",\n \"maxTurns\",\n \"skills\",\n \"background\",\n \"effort\",\n \"isolation\",\n \"initialPrompt\",\n \"hooks\",\n \"experimental\",\n \"mcpServers\",\n]);\n\n/**\n * The typed error for a key we do not accept. Built here rather than inline so `rejectUnknownFields`\n * stays a plain loop: the message has two shapes and the code has two values, and computing all four\n * at the throw site is what pushed that function past the cognitive-complexity gate.\n */\nfunction unknownFieldError(key: string, filename: string): ConfigurationError {\n const foreign = KNOWN_CLAUDE_CODE_FIELDS.has(key);\n const siblings = [...KNOWN_CLAUDE_CODE_FIELDS].filter((f) => f !== key);\n const origin = foreign\n ? ` — \"${key}\" is a Claude Code subagent field that this runtime does not support. ` +\n `The same applies to: ${siblings.join(\", \")}. Removing them one at a time will meet each ` +\n `in turn; the tree is written for another runtime`\n : \"\";\n // The two codes are written as LITERALS, one per branch, rather than as a ternary inside the\n // options object. `tools/generate-error-codes.mjs` walks the source for the literal assigned to\n // `code:`, so a ternary there is not a literal and it extracted NEITHER — measured 2026-09-15,\n // when `quality:docs-errors` went red and the regenerated reference had lost\n // `subagent_unknown_field` without ever gaining `subagent_foreign_runtime_field`. Two real,\n // throwable codes were absent from the published error reference, which is the one place a\n // consumer looks to find out what they can catch.\n if (foreign)\n return new ConfigurationError(\n `Subagent ${filename}: unknown frontmatter field \"${key}\" (accepted: ${[...ACCEPTED_FIELDS].join(\", \")})${origin}`,\n { code: \"subagent_foreign_runtime_field\" },\n );\n return new ConfigurationError(\n `Subagent ${filename}: unknown frontmatter field \"${key}\" (accepted: ${[...ACCEPTED_FIELDS].join(\", \")})${origin}`,\n { code: \"subagent_unknown_field\" },\n );\n}\n\nfunction rejectUnknownFields(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): void {\n for (const key of Object.keys(fields)) {\n if (INERT_CLAUDE_CODE_FIELDS.has(key)) continue;\n if (!ACCEPTED_FIELDS.has(key)) throw unknownFieldError(key, filename);\n }\n}\n\n// mcp: a known field, but not yet honored on the LOCAL delegation path. The frontmatter YAML can only\n// express server NAMES (parseSimpleYaml has no nested-object support), while a child's `Agent.create`\n// needs `mcpServers` as a Record<name, config>; resolving names→config per-subagent in local delegation\n// is its own follow-up. Rather than silently drop it (the M26/M32 silent-gate class), it is a typed load\n// error that names the field and points at the alternative.\nfunction rejectMcp(fields: Record<string, FrontmatterValue | undefined>, filename: string): void {\n if (fields.mcp !== undefined) {\n throw new ConfigurationError(\n `Subagent ${filename}: per-subagent \"mcp\" is not yet supported on the local delegation path; declare MCP servers in .theokit/mcp.json (or the parent) instead`,\n { code: \"subagent_mcp_unsupported_local\" },\n );\n }\n}\n\n// model + reasoning_effort — effort rides inside `model.params[thinking]`, so it requires a concrete\n// model id to attach to (a child inheriting the parent's model cannot carry the parent's provider-\n// specific effort param safely).\nfunction resolveModel(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): ModelSelection | \"inherit\" | undefined {\n const modelId = asString(fields.model);\n // An empty/blank effort (`reasoning_effort: \"\"`) names nothing: treated as absent, never as a\n // `{ id: \"thinking\", value: \"\" }` param the provider would drop or reject.\n const rawEffort = asString(fields.reasoning_effort)?.trim();\n const effort = rawEffort === \"\" ? undefined : rawEffort;\n // reasoning_effort rides in model.params[thinking], so it needs a CONCRETE model id to attach to.\n // Neither an absent model NOR `model: inherit` can carry it (the inherited id is unknown at load), so\n // both are typed errors rather than a silently-dropped effort — the silent-gate class this guards.\n if (effort !== undefined && (modelId === undefined || modelId === \"inherit\")) {\n throw new ConfigurationError(\n `Subagent ${filename}: reasoning_effort requires a concrete model (effort is a model parameter; an absent model or \"inherit\" cannot carry it)`,\n { code: \"subagent_reasoning_effort_without_model\" },\n );\n }\n if (modelId === undefined) return undefined;\n if (modelId === \"inherit\") return \"inherit\";\n return effort !== undefined\n ? { id: modelId, params: [{ id: \"thinking\", value: effort }] }\n : { id: modelId };\n}\n\n// sandbox: boolean only. A granular mode string (read-only/…) is unsupported by the SDK runtime and is\n// a typed error rather than a silent coercion to a boolean.\nfunction resolveSandbox(\n fields: Record<string, FrontmatterValue | undefined>,\n filename: string,\n): boolean | undefined {\n if (fields.sandbox === undefined) return undefined;\n if (typeof fields.sandbox !== \"boolean\") {\n throw new ConfigurationError(\n `Subagent ${filename}: sandbox must be a boolean (got \"${String(fields.sandbox)}\"); granular sandbox modes are not supported by the runtime`,\n { code: \"subagent_sandbox_not_boolean\" },\n );\n }\n return fields.sandbox;\n}\n\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n if (typeof v !== \"string\") return undefined;\n // parseSimpleYaml does not strip quotes (documented), and `model`/`reasoning_effort` are fields users\n // habitually quote (`model: \"openai/gpt-4o\"`). Strip a single matching surrounding quote pair so a\n // quoted id/effort does not slip past validation and fail only at the provider.\n const m = /^([\"'])(.*)\\1$/.exec(v);\n return m ? m[2] : v;\n}\n\n/** Accept a YAML list (`string[]`) or a comma/space-separated scalar; trim + drop empties. */\nfunction toStringList(v: FrontmatterValue | undefined): string[] {\n if (Array.isArray(v)) return v.map((t) => t.trim()).filter((t) => t.length > 0);\n if (typeof v === \"string\") {\n return v\n .split(/[\\s,]+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n }\n return [];\n}\n\n/** Does this file open with a frontmatter block at all? Its ABSENCE means \"not an agent\". */\nfunction hasFrontmatter(raw: string): boolean {\n return /^---\\s*\\n/.test(raw);\n}\n\nfunction splitFrontmatter(raw: string, filename: string): { frontmatter: string; body: string } {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n([\\s\\S]*)$/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Subagent ${filename} is missing frontmatter`, {\n code: \"subagent_missing_frontmatter\",\n });\n }\n return { frontmatter: match[1] ?? \"\", body: (match[2] ?? \"\").trim() };\n}\n\nfunction parseFrontmatterFields(frontmatter: string): Record<string, FrontmatterValue | undefined> {\n // Preserve the rich YAML value types (boolean/number/string[]): `sandbox: true` and\n // `mcp: [a, b]` are meaningful here, so narrowing everything to string (as the\n // pre-M33 loader did) would drop them. Per-field validation happens in parseSubagentMarkdown.\n return parseSimpleYaml(frontmatter);\n}\n"]}
@@ -1,19 +1,19 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var path = require('path');
3
+ var node_fs = require('node:fs');
4
+ var node_path = require('node:path');
5
5
 
6
6
  // src/internal/security/path-containment.ts
7
7
  function realOrResolved(path) {
8
8
  try {
9
- return fs.realpathSync(path);
9
+ return node_fs.realpathSync(path);
10
10
  } catch {
11
11
  return path;
12
12
  }
13
13
  }
14
14
  function insideRoot(target, root) {
15
- const rel = path.relative(realOrResolved(root), realOrResolved(target));
16
- return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
15
+ const rel = node_path.relative(realOrResolved(root), realOrResolved(target));
16
+ return rel !== "" && !rel.startsWith("..") && !node_path.isAbsolute(rel);
17
17
  }
18
18
  function atOrInsideRoot(target, root) {
19
19
  return insideRoot(target, root) || realOrResolved(target) === realOrResolved(root);
@@ -21,5 +21,5 @@ function atOrInsideRoot(target, root) {
21
21
 
22
22
  exports.atOrInsideRoot = atOrInsideRoot;
23
23
  exports.insideRoot = insideRoot;
24
- //# sourceMappingURL=chunk-SA6K24NB.cjs.map
25
- //# sourceMappingURL=chunk-SA6K24NB.cjs.map
24
+ //# sourceMappingURL=chunk-MXJJMP4U.cjs.map
25
+ //# sourceMappingURL=chunk-MXJJMP4U.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/security/path-containment.ts"],"names":["realpathSync","relative","isAbsolute"],"mappings":";;;;;;AAoCO,SAAS,eAAe,IAAA,EAAsB;AACnD,EAAA,IAAI;AACF,IAAA,OAAOA,qBAAa,IAAI,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAUO,SAAS,UAAA,CAAW,QAAgB,IAAA,EAAuB;AAChE,EAAA,MAAM,MAAMC,kBAAA,CAAS,cAAA,CAAe,IAAI,CAAA,EAAG,cAAA,CAAe,MAAM,CAAC,CAAA;AACjE,EAAA,OAAO,GAAA,KAAQ,MAAM,CAAC,GAAA,CAAI,WAAW,IAAI,CAAA,IAAK,CAACC,oBAAA,CAAW,GAAG,CAAA;AAC/D;AAgBO,SAAS,cAAA,CAAe,QAAgB,IAAA,EAAuB;AACpE,EAAA,OAAO,UAAA,CAAW,QAAQ,IAAI,CAAA,IAAK,eAAe,MAAM,CAAA,KAAM,eAAe,IAAI,CAAA;AACnF","file":"chunk-MXJJMP4U.cjs","sourcesContent":["/**\n * The one rule for \"is this resolved path inside that root\".\n *\n * Extracted because the package held it twice, at different strengths, for the same question. The\n * correct version lived as a private function in `context-import-resolver.ts`, written for the\n * 4.41.1 patch; the context manager had its own `absolute.startsWith(resolvePath(cwd))`, which\n * admits two escapes:\n *\n * - **No separator boundary.** With `root = /home/user/proj`, the path `/home/user/proj-evil/x`\n * starts with the root string and is NOT inside it. A sibling directory whose name merely\n * extends the project's is enough; no `..` past the parent is needed.\n * - **Lexical, not real.** A symlink whose name sits inside the root and whose target does not\n * passes any comparison made before symlink resolution.\n *\n * The obvious escapes (`../../etc/passwd`, an absolute path) ARE refused by a prefix test, which is\n * why the weaker version survived review: it looks like it works on the inputs a reader tries.\n *\n * Both readers of repository-supplied paths now share this module. That is DRY about the RULE\n * rather than about line count — one piece of knowledge, one representation, so the two cannot\n * drift apart again.\n *\n * @internal\n */\n\nimport { realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\n\n/**\n * The real path, or the lexical one when it cannot be resolved.\n *\n * The fallback is load-bearing rather than defensive: the context manager checks containment BEFORE\n * it stats the file, so a source that is legitimately absent must still be judged on its declared\n * location instead of being refused for the wrong reason.\n *\n * @internal\n */\nexport function realOrResolved(path: string): string {\n try {\n return realpathSync(path);\n } catch {\n return path;\n }\n}\n\n/**\n * Whether `target` is inside `root`, compared AFTER symlink resolution.\n *\n * `relative()` supplies the separator boundary that a prefix test lacks: it answers `..` for a\n * sibling, `''` for the root itself, and an absolute path when the two are on different roots.\n *\n * @internal\n */\nexport function insideRoot(target: string, root: string): boolean {\n const rel = relative(realOrResolved(root), realOrResolved(target));\n return rel !== \"\" && !rel.startsWith(\"..\") && !isAbsolute(rel);\n}\n\n/**\n * Whether `target` is the root ITSELF or inside it, compared after symlink resolution.\n *\n * Separate from {@link insideRoot} because the two answer a genuinely different question and their\n * callers need different ones. The context manager asks \"is this source under the project?\", where\n * the project directory itself is not a source. A path JOIN asks \"did this stay within its base?\",\n * where joining nothing and getting the base back is the ordinary case — `safePathJoin(base)` must\n * return `base`.\n *\n * Named here rather than written out at each call site: it appeared twice within one change, which\n * is how this rule ended up with three copies at three strengths the first time.\n *\n * @internal\n */\nexport function atOrInsideRoot(target: string, root: string): boolean {\n return insideRoot(target, root) || realOrResolved(target) === realOrResolved(root);\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkEQ5IDL6W_cjs = require('./chunk-EQ5IDL6W.cjs');
4
- var chunk3WKWBYPO_cjs = require('./chunk-3WKWBYPO.cjs');
5
- var chunkPOBZXULR_cjs = require('./chunk-POBZXULR.cjs');
3
+ var chunkMKS5FFH5_cjs = require('./chunk-MKS5FFH5.cjs');
4
+ var chunk6EHOJLJ3_cjs = require('./chunk-6EHOJLJ3.cjs');
5
+ var chunkTTNJOM5W_cjs = require('./chunk-TTNJOM5W.cjs');
6
6
  var chunkMHUOVS5I_cjs = require('./chunk-MHUOVS5I.cjs');
7
- var fs = require('fs');
8
- var promises = require('fs/promises');
9
- var path = require('path');
7
+ var node_fs = require('node:fs');
8
+ var promises = require('node:fs/promises');
9
+ var node_path = require('node:path');
10
10
 
11
11
  var sharedLeases = /* @__PURE__ */ new Map();
12
12
  async function acquireShared(path) {
@@ -15,7 +15,7 @@ async function acquireShared(path) {
15
15
  existing.refs++;
16
16
  return createProxy(path);
17
17
  }
18
- const lease = await chunkEQ5IDL6W_cjs.acquireSessionWriter(path);
18
+ const lease = await chunkMKS5FFH5_cjs.acquireSessionWriter(path);
19
19
  sharedLeases.set(path, { lease, refs: 1 });
20
20
  return createProxy(path);
21
21
  }
@@ -67,21 +67,21 @@ var FsSessionStore = class {
67
67
  #pathFor(agentId) {
68
68
  const memoized = this.#paths.get(agentId);
69
69
  if (memoized !== void 0) return memoized;
70
- const canonical = chunk3WKWBYPO_cjs.transcriptPath(this.#baseDir, this.#cwd, agentId);
71
- const legacy = chunk3WKWBYPO_cjs.legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);
72
- const resolved = legacy !== canonical && fs.existsSync(legacy) ? legacy : canonical;
70
+ const canonical = chunk6EHOJLJ3_cjs.transcriptPath(this.#baseDir, this.#cwd, agentId);
71
+ const legacy = chunk6EHOJLJ3_cjs.legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);
72
+ const resolved = legacy !== canonical && node_fs.existsSync(legacy) ? legacy : canonical;
73
73
  this.#paths.set(agentId, resolved);
74
74
  return resolved;
75
75
  }
76
76
  async readRecords(agentId) {
77
- return chunk3WKWBYPO_cjs.readTranscript(this.#pathFor(agentId));
77
+ return chunk6EHOJLJ3_cjs.readTranscript(this.#pathFor(agentId));
78
78
  }
79
79
  async appendRecords(agentId, records) {
80
80
  if (records.length === 0) return;
81
- const path$1 = this.#pathFor(agentId);
82
- await promises.mkdir(path.dirname(path$1), { recursive: true });
83
- await chunkMHUOVS5I_cjs.withFileLock(path$1, async () => {
84
- for (const record of records) chunkPOBZXULR_cjs.appendJsonl(path$1, record);
81
+ const path = this.#pathFor(agentId);
82
+ await promises.mkdir(node_path.dirname(path), { recursive: true });
83
+ await chunkMHUOVS5I_cjs.withFileLock(path, async () => {
84
+ for (const record of records) chunkTTNJOM5W_cjs.appendJsonl(path, record);
85
85
  });
86
86
  }
87
87
  /**
@@ -100,9 +100,9 @@ var FsSessionStore = class {
100
100
  */
101
101
  async acquire(agentId) {
102
102
  if (this.#leases.has(agentId)) return;
103
- const path$1 = this.#pathFor(agentId);
104
- await promises.mkdir(path.dirname(path$1), { recursive: true });
105
- this.#leases.set(agentId, await acquireShared(path$1));
103
+ const path = this.#pathFor(agentId);
104
+ await promises.mkdir(node_path.dirname(path), { recursive: true });
105
+ this.#leases.set(agentId, await acquireShared(path));
106
106
  }
107
107
  /**
108
108
  * Releases ONE agent's lease.
@@ -132,5 +132,5 @@ var FsSessionStore = class {
132
132
  };
133
133
 
134
134
  exports.FsSessionStore = FsSessionStore;
135
- //# sourceMappingURL=chunk-FTXY4D44.cjs.map
136
- //# sourceMappingURL=chunk-FTXY4D44.cjs.map
135
+ //# sourceMappingURL=chunk-N2ORTDSO.cjs.map
136
+ //# sourceMappingURL=chunk-N2ORTDSO.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/persistence/fs-session-store.ts"],"names":["acquireSessionWriter","transcriptPath","legacyTranscriptPath","existsSync","readTranscript","mkdir","dirname","withFileLock","appendJsonl"],"mappings":"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","file":"chunk-N2ORTDSO.cjs","sourcesContent":["/**\n * SE41 — `FsSessionStore`, the DEFAULT reference implementation of the pluggable\n * {@link SessionStore} seam. It reads and append-writes the native Claude-shaped\n * `.jsonl` transcript at `<baseDir>/projects/<encoded-cwd>/<agentId>.jsonl` — the\n * exact on-disk format SE40 introduced (the file the Claude Code CLI can\n * `--continue`). Omitting `local.sessionStore` resolves to this store, so the\n * default persistence path is byte-identical to SE40 behavior.\n *\n * `readRecords` is `readTranscript(transcriptPath(...))` (a missing session →\n * `[]`, not an error — a fresh agent has no history). `appendRecords` is a TRUE\n * append: it reads the prior records, concatenates the new-turn delta, and\n * rewrites the whole line set atomically under the SE40 cross-process file lock\n * (`writeTranscript` never shrinks — the native format is an append-only\n * `parentUuid` DAG). The parent dir is created BEFORE acquiring the lock because\n * the lock's companion `<path>.lock` file needs an existing parent dir (the SE40\n * `mkdir(dirname)`-before-lock fix).\n *\n * @internal\n */\n\nimport { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { SessionStore } from \"../../types/session-store.js\";\nimport { withFileLock } from \"./file-lock.js\";\nimport { appendJsonl } from \"./jsonl.js\";\nimport {\n legacyTranscriptPath,\n readTranscript,\n type SessionRecord,\n transcriptPath,\n} from \"./session-transcript.js\";\nimport { acquireSessionWriter, type SessionWriterLease } from \"./session-writer.js\";\n\n/** Options identifying the on-disk transcript location for the FS default store. */\nexport interface FsSessionStoreOptions {\n /** Transcript base dir (`~/.theokit` default, `~/.claude` for CLI interop) — already `~`-expanded. */\n baseDir: string;\n /** The workspace cwd whose encoded form is the transcript project dir. */\n cwd: string;\n}\n\n/**\n * Reference-counted leases, shared per path.\n *\n * `acquireSessionWriter` is STRICT on purpose: two concurrent acquisitions of the same path, and\n * exactly one wins — \"a lease that let both through would be decorative\" (the M81 test says exactly\n * that, and it is right). That is the primitive's contract, and it does not change.\n *\n * But within ONE process it is normal to have more than one store over the same session: the golden\n * compaction tests and the concurrent-send tests do exactly that, and they record real runtime\n * behavior. Applying the raw primitive there would turn a legitimate pattern into a\n * `SessionBusyError`.\n *\n * The reconciliation belongs here, in the primitive's consumer: the process takes ONE lease per path\n * and counts how many stores use it. The last to release actually frees it. Cross-process stays\n * strict — which is the problem M81 states (\"`exec resume --last` can write into the TUI's live\n * session\").\n */\nconst sharedLeases = new Map<string, { lease: SessionWriterLease; refs: number }>();\n\nasync function acquireShared(path: string): Promise<SessionWriterLease> {\n const existing = sharedLeases.get(path);\n if (existing !== undefined) {\n existing.refs++;\n return createProxy(path);\n }\n const lease = await acquireSessionWriter(path);\n sharedLeases.set(path, { lease, refs: 1 });\n return createProxy(path);\n}\n\n/** A handle that decrements the count; the last to release frees the real lease. */\nfunction createProxy(path: string): SessionWriterLease {\n let released = false;\n return {\n sessionPath: path,\n // The proxy forwards the renewal to the ONE real lease this path shares. Renewing through any\n // holder is correct and is the point: the record must say \"someone in this process is still\n // writing\", and the refcount already guarantees they are all the same owner.\n renew: (): void => {\n if (released) return;\n sharedLeases.get(path)?.lease.renew();\n },\n release: async (): Promise<void> => {\n if (released) return;\n released = true;\n const entry = sharedLeases.get(path);\n if (entry === undefined) return;\n entry.refs--;\n if (entry.refs > 0) return;\n sharedLeases.delete(path);\n await entry.lease.release();\n },\n };\n}\n\n/**\n * The default `SessionStore` — reads/append-writes the native `.jsonl` transcript.\n */\nexport class FsSessionStore implements SessionStore {\n readonly #baseDir: string;\n readonly #cwd: string;\n /** One lease per `agentId` — a store serves more than one session over the process lifetime. */\n readonly #leases = new Map<string, SessionWriterLease>();\n /** Memoized per agent so read, append and lease can never disagree about which file is the session. */\n readonly #paths = new Map<string, string>();\n\n constructor(options: FsSessionStoreOptions) {\n this.#baseDir = options.baseDir;\n this.#cwd = options.cwd;\n }\n\n /**\n * The file that IS this agent's session.\n *\n * #400 made transcript filenames UUIDs so `claude --continue` can find them. A transcript written\n * before that lives under the old name, and switching to the new one would not lose the file but\n * would abandon it: the agent would start a second, empty history beside its real one. So an\n * existing legacy file wins, and only a session with no file yet gets the new name.\n *\n * Resolved ONCE per agent. Re-deciding per call would let `acquire` lock one path while\n * `appendRecords` wrote another — the lease would be guarding a file nobody was writing.\n */\n #pathFor(agentId: string): string {\n const memoized = this.#paths.get(agentId);\n if (memoized !== undefined) return memoized;\n const canonical = transcriptPath(this.#baseDir, this.#cwd, agentId);\n const legacy = legacyTranscriptPath(this.#baseDir, this.#cwd, agentId);\n const resolved = legacy !== canonical && existsSync(legacy) ? legacy : canonical;\n this.#paths.set(agentId, resolved);\n return resolved;\n }\n\n async readRecords(agentId: string): Promise<SessionRecord[]> {\n return readTranscript(this.#pathFor(agentId));\n }\n\n async appendRecords(agentId: string, records: readonly SessionRecord[]): Promise<void> {\n // Empty delta → nothing to persist (avoids an unnecessary lock + rewrite).\n if (records.length === 0) return;\n const path = this.#pathFor(agentId);\n // mkdir BEFORE the lock: withFileLock's companion `<path>.lock` needs the parent dir.\n await mkdir(dirname(path), { recursive: true });\n\n await withFileLock(path, async () => {\n // M93 — appends the DELTA instead of rewriting the whole file.\n //\n // Before: `readTranscript` + `writeTranscript` of everything, per turn. O(n) of I/O **and** of\n // parsing on every turn, O(n^2) per session — the consumer note in\n // `agents/lib/session/backtrack.ts` records 1.4 MB / 3000 lines over 200 turns.\n //\n // Correct because the format **is already append-only**: the `parentUuid` DAG does not depend\n // on line order, and every record carries its own parent. `appendJsonl` **already existed in\n // the package** and had a single caller (`eval/runner.ts`) — the primitive was there, it was\n // the store that ignored it (rung 4).\n //\n // `withFileLock` stays — but the earlier claim that \"it is what serializes two concurrent\n // `appendRecords`\" was too strong, and M93's adversarial review measured it: removing it fails\n // no test. The reason is the paragraph above — the `parentUuid` DAG does not depend on line\n // order, so two interleaved batches reconstruct identically. The work the lock was doing\n // (protecting a read-modify-write) disappeared along with the rewrite.\n //\n // What it still covers is the TOCTOU window in `needsLineBreakBefore` (read the last byte,\n // then write): without it, two processes can both conclude \"a \\n is missing\" and produce a\n // blank line — which the reader discards, i.e. benign. It stays as a **declared, not\n // mechanized** defense (the discipline in `error-handling.md` § 4: enumerate the residue\n // rather than let a missing test pass for coverage).\n //\n // `writeTranscript` still exists for **compaction**, the one operation that legitimately\n // rewrites the file.\n for (const record of records) appendJsonl(path, record);\n });\n }\n\n /**\n * Takes the session's writer lease. Throws `SessionBusyError` when another process holds it.\n *\n * **Explicit, and NOT inside `appendRecords`** — M95's adversarial review measured why that\n * matters: the `SessionStore` contract states that \"an `appendRecords` rejection is logged to\n * stderr, NOT thrown to the caller (best-effort write)\". Acquiring there made the\n * `SessionBusyError` get **swallowed**, and the result was worse than the original problem:\n * instead of two writers interleaving lines, the loser **lost the turn silently** — nothing on\n * disk, a stderr warning invisible under the TUI, and no way for the caller to react.\n *\n * At init the error reaches someone who can act: `exec` forks to a new id, which is exactly what\n * the error message itself prescribes. It is the difference between failing where a decision is\n * possible and failing where only loss is.\n */\n async acquire(agentId: string): Promise<void> {\n if (this.#leases.has(agentId)) return;\n const path = this.#pathFor(agentId);\n await mkdir(dirname(path), { recursive: true });\n this.#leases.set(agentId, await acquireShared(path));\n }\n\n /**\n * Releases ONE agent's lease.\n *\n * It exists because `dispose()` releases **every** lease the store holds, and a store injected by\n * the consumer may serve several agents: an init that fails for agent B must not free agent A's\n * lease, which is still live and writing.\n */\n async release(agentId: string): Promise<void> {\n const lease = this.#leases.get(agentId);\n if (lease === undefined) return;\n this.#leases.delete(agentId);\n await lease.release();\n }\n\n /**\n * Releases every lease this store holds.\n *\n * Without it the `.writer.lock` outlives the process and the next open would have to wait out the\n * heartbeat window — recoverable, but 30 s of waiting after a CLEAN shutdown would be an avoidable\n * defect. Idempotent: calling it twice is not an error.\n */\n async dispose(): Promise<void> {\n const leases = [...this.#leases.values()];\n this.#leases.clear();\n for (const lease of leases) await lease.release();\n }\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunkUDAKAE4Z_cjs = require('./chunk-UDAKAE4Z.cjs');
4
- var async_hooks = require('async_hooks');
3
+ var chunkX4ZYEZPD_cjs = require('./chunk-X4ZYEZPD.cjs');
4
+ var node_async_hooks = require('node:async_hooks');
5
5
 
6
- var storage = new async_hooks.AsyncLocalStorage();
6
+ var storage = new node_async_hooks.AsyncLocalStorage();
7
7
  function withPersonalityContext(ctx, fn) {
8
8
  return storage.run(ctx, fn);
9
9
  }
@@ -11,7 +11,7 @@ function currentPersonalityContext() {
11
11
  return storage.getStore();
12
12
  }
13
13
  function warnPersonalitySwitchInsideFork(agentId) {
14
- chunkUDAKAE4Z_cjs.warnOnce(
14
+ chunkX4ZYEZPD_cjs.warnOnce(
15
15
  `personality-switch-in-fork-${agentId}`,
16
16
  `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`
17
17
  );
@@ -20,5 +20,5 @@ function warnPersonalitySwitchInsideFork(agentId) {
20
20
  exports.currentPersonalityContext = currentPersonalityContext;
21
21
  exports.warnPersonalitySwitchInsideFork = warnPersonalitySwitchInsideFork;
22
22
  exports.withPersonalityContext = withPersonalityContext;
23
- //# sourceMappingURL=chunk-7YUPSBDV.cjs.map
24
- //# sourceMappingURL=chunk-7YUPSBDV.cjs.map
23
+ //# sourceMappingURL=chunk-NDXZXGVP.cjs.map
24
+ //# sourceMappingURL=chunk-NDXZXGVP.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/personality/context.ts"],"names":["AsyncLocalStorage","warnOnce"],"mappings":";;;;;AAiCA,IAAM,OAAA,GAAU,IAAIA,6BAAA,EAA0C;AAQvD,SAAS,sBAAA,CACd,KACA,EAAA,EACY;AACZ,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,EAAE,CAAA;AAC5B;AAQO,SAAS,yBAAA,GAAgE;AAC9E,EAAA,OAAO,QAAQ,QAAA,EAAS;AAC1B;AASO,SAAS,gCAAgC,OAAA,EAAuB;AACrE,EAAAC,0BAAA;AAAA,IACE,8BAA8B,OAAO,CAAA,CAAA;AAAA,IACrC,CAAA,0IAAA;AAAA,GACF;AACF","file":"chunk-7YUPSBDV.cjs","sourcesContent":["/**\n * Personality fork-context (ADR D168 + EC-A snapshot semantic).\n *\n * Uses Node's `AsyncLocalStorage` so a fork's execution chain can know\n * that it is running inside a fork AND can see the slug that was active\n * on the parent **at fork-construction time**.\n *\n * **EC-A:** The slug stored here is captured ONCE at the wrap site\n * (`localAgentFork`) — passing `parentStore.active(parentAgentId)`\n * returns a primitive `string | undefined`, which is then frozen\n * inside the ALS context object. Subsequent `usePersonality` calls on\n * the parent do NOT mutate the fork's view, because the fork reads from\n * its own ALS frame, not from the parent's store.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport { warnOnce } from \"../runtime/hooks/hooks-source.js\";\n\n/**\n * Snapshot data carried into a fork's async context.\n *\n * @internal\n */\nexport interface PersonalityForkContext {\n /** Parent's active personality slug at fork-construction time. */\n readonly slug: string | undefined;\n /** Always `true` inside this scope (used by guards). */\n readonly isFork: true;\n}\n\nconst storage = new AsyncLocalStorage<PersonalityForkContext>();\n\n/**\n * Run `fn` with `ctx` bound as the active fork context. Nested calls\n * shadow the outer context (EC-22).\n *\n * @internal\n */\nexport function withPersonalityContext<T>(\n ctx: PersonalityForkContext,\n fn: () => Promise<T>,\n): Promise<T> {\n return storage.run(ctx, fn);\n}\n\n/**\n * Return the active fork context, or `undefined` when called outside a\n * fork scope.\n *\n * @internal\n */\nexport function currentPersonalityContext(): PersonalityForkContext | undefined {\n return storage.getStore();\n}\n\n/**\n * Emit one warning per agentId stating that personality switches inside\n * a fork are no-ops. The fork inherits the parent snapshot — runtime\n * mutation is intentionally rejected to keep fork voice deterministic.\n *\n * @internal\n */\nexport function warnPersonalitySwitchInsideFork(agentId: string): void {\n warnOnce(\n `personality-switch-in-fork-${agentId}`,\n `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`,\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/personality/context.ts"],"names":["AsyncLocalStorage","warnOnce"],"mappings":";;;;;AAiCA,IAAM,OAAA,GAAU,IAAIA,kCAAA,EAA0C;AAQvD,SAAS,sBAAA,CACd,KACA,EAAA,EACY;AACZ,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,EAAE,CAAA;AAC5B;AAQO,SAAS,yBAAA,GAAgE;AAC9E,EAAA,OAAO,QAAQ,QAAA,EAAS;AAC1B;AASO,SAAS,gCAAgC,OAAA,EAAuB;AACrE,EAAAC,0BAAA;AAAA,IACE,8BAA8B,OAAO,CAAA,CAAA;AAAA,IACrC,CAAA,0IAAA;AAAA,GACF;AACF","file":"chunk-NDXZXGVP.cjs","sourcesContent":["/**\n * Personality fork-context (ADR D168 + EC-A snapshot semantic).\n *\n * Uses Node's `AsyncLocalStorage` so a fork's execution chain can know\n * that it is running inside a fork AND can see the slug that was active\n * on the parent **at fork-construction time**.\n *\n * **EC-A:** The slug stored here is captured ONCE at the wrap site\n * (`localAgentFork`) — passing `parentStore.active(parentAgentId)`\n * returns a primitive `string | undefined`, which is then frozen\n * inside the ALS context object. Subsequent `usePersonality` calls on\n * the parent do NOT mutate the fork's view, because the fork reads from\n * its own ALS frame, not from the parent's store.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport { warnOnce } from \"../runtime/hooks/hooks-source.js\";\n\n/**\n * Snapshot data carried into a fork's async context.\n *\n * @internal\n */\nexport interface PersonalityForkContext {\n /** Parent's active personality slug at fork-construction time. */\n readonly slug: string | undefined;\n /** Always `true` inside this scope (used by guards). */\n readonly isFork: true;\n}\n\nconst storage = new AsyncLocalStorage<PersonalityForkContext>();\n\n/**\n * Run `fn` with `ctx` bound as the active fork context. Nested calls\n * shadow the outer context (EC-22).\n *\n * @internal\n */\nexport function withPersonalityContext<T>(\n ctx: PersonalityForkContext,\n fn: () => Promise<T>,\n): Promise<T> {\n return storage.run(ctx, fn);\n}\n\n/**\n * Return the active fork context, or `undefined` when called outside a\n * fork scope.\n *\n * @internal\n */\nexport function currentPersonalityContext(): PersonalityForkContext | undefined {\n return storage.getStore();\n}\n\n/**\n * Emit one warning per agentId stating that personality switches inside\n * a fork are no-ops. The fork inherits the parent snapshot — runtime\n * mutation is intentionally rejected to keep fork voice deterministic.\n *\n * @internal\n */\nexport function warnPersonalitySwitchInsideFork(agentId: string): void {\n warnOnce(\n `personality-switch-in-fork-${agentId}`,\n `[theokit-sdk] usePersonality is a no-op inside a fork (D168). Subagents inherit the parent's active personality at fork-construction time.`,\n );\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunk3KIUY67P_cjs = require('./chunk-3KIUY67P.cjs');
4
- var chunk6XQLTQB6_cjs = require('./chunk-6XQLTQB6.cjs');
3
+ var chunkVHY6NVCJ_cjs = require('./chunk-VHY6NVCJ.cjs');
4
+ var chunk3YCNBOX7_cjs = require('./chunk-3YCNBOX7.cjs');
5
5
  var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
6
- var promises = require('fs/promises');
7
- var path = require('path');
6
+ var promises = require('node:fs/promises');
7
+ var node_path = require('node:path');
8
8
 
9
9
  // src/internal/runtime/skills/skill-frontmatter.ts
10
10
  function asString(v) {
@@ -35,7 +35,7 @@ function extractAndParseFrontmatter(raw, fallbackName) {
35
35
  }
36
36
  const frontmatter = match[1] ?? "";
37
37
  try {
38
- return chunk6XQLTQB6_cjs.parseSimpleYaml(frontmatter);
38
+ return chunk3YCNBOX7_cjs.parseSimpleYaml(frontmatter);
39
39
  } catch (cause) {
40
40
  const detail = cause instanceof Error ? cause.message : String(cause);
41
41
  throw new chunkCEGU5AVD_cjs.ConfigurationError(
@@ -97,7 +97,7 @@ function hasContent(value) {
97
97
  async function discoverSkills(dir, options) {
98
98
  let entries;
99
99
  try {
100
- entries = await chunk6XQLTQB6_cjs.readWorkspaceDir(dir, "skills_read_error", "skills directory");
100
+ entries = await chunk3YCNBOX7_cjs.readWorkspaceDir(dir, "skills_read_error", "skills directory");
101
101
  } catch {
102
102
  return [];
103
103
  }
@@ -106,12 +106,12 @@ async function discoverSkills(dir, options) {
106
106
  if (!entry.isDirectory()) continue;
107
107
  let skillDir;
108
108
  try {
109
- skillDir = chunk3KIUY67P_cjs.safePathJoin(dir, entry.name);
110
- chunk3KIUY67P_cjs.assertNoSymlinkEscape(skillDir, dir);
109
+ skillDir = chunkVHY6NVCJ_cjs.safePathJoin(dir, entry.name);
110
+ chunkVHY6NVCJ_cjs.assertNoSymlinkEscape(skillDir, dir);
111
111
  } catch {
112
112
  continue;
113
113
  }
114
- const skillPath = path.join(skillDir, "SKILL.md");
114
+ const skillPath = node_path.join(skillDir, "SKILL.md");
115
115
  let raw;
116
116
  try {
117
117
  raw = await promises.readFile(skillPath, "utf8");
@@ -175,5 +175,5 @@ exports.discoverSkills = discoverSkills;
175
175
  exports.escapeBlockBody = escapeBlockBody;
176
176
  exports.loadSkillInstructions = loadSkillInstructions;
177
177
  exports.stripSkillFrontmatter = stripSkillFrontmatter;
178
- //# sourceMappingURL=chunk-3UMPP553.cjs.map
179
- //# sourceMappingURL=chunk-3UMPP553.cjs.map
178
+ //# sourceMappingURL=chunk-NMRNE6QX.cjs.map
179
+ //# sourceMappingURL=chunk-NMRNE6QX.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/runtime/skills/skill-frontmatter.ts","../src/internal/runtime/skills/discover-skills.ts","../src/internal/runtime/system-prompt/escape.ts","../src/internal/runtime/skills/skills-block.ts"],"names":["ConfigurationError","parseSimpleYaml","readWorkspaceDir","safePathJoin","assertNoSymlinkEscape","join","readFile"],"mappings":"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","file":"chunk-3UMPP553.cjs","sourcesContent":["import { ConfigurationError } from \"../../../errors.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\n\ntype StringFields = Record<string, string | undefined>;\n\n/** Narrow a FrontmatterValue to string; non-strings + undefined → undefined. */\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Coerce parser output to legacy string-only shape (skill schema is all-string). */\nfunction toStringFields(raw: Record<string, FrontmatterValue | undefined>): StringFields {\n const out: StringFields = {};\n for (const [k, v] of Object.entries(raw)) out[k] = asString(v);\n return out;\n}\n\n/**\n * Strict skill frontmatter schema (ADR D10).\n *\n * Required: `name`, `description`.\n * Optional: `category`, `dependencies` (comma-separated string in the\n * simple-YAML dialect — parsed to `string[]`).\n *\n * Unknown fields are ignored (forward-compat). Malformed YAML or missing\n * required fields surface as `ConfigurationError` with one of the typed\n * codes below.\n *\n * @internal\n */\nexport interface SkillFrontmatter {\n name: string;\n description: string;\n category?: string;\n dependencies?: string[];\n /**\n * `requires-bins: git, rg` (B-546) — executables the skill needs on PATH. A skill whose binary is\n * absent is not offered to the model; `skills.get(name)` still resolves it.\n */\n requiresBins?: string[];\n /**\n * `disable-model-invocation: true` — the model is not told this skill exists.\n *\n * The SDK owns this boundary: a skill reaches the model through exactly one place,\n * `buildSystemPromptContext`, so the declaration is enforceable here. It is a DISCLOSURE rule,\n * not an execution rule — `skills.get(name)` still resolves it, because a caller naming a skill\n * has already made the decision the field exists to keep away from the model.\n */\n disableModelInvocation?: boolean;\n /**\n * `user-invocable: false` — carried, deliberately not enforced here.\n *\n * This SDK has no user-facing invocation surface for skills; there is no slash command. Reading\n * `agent.skills.list()` as \"the user\" would be a guess — a host may call it to build a picker or\n * to introspect, and the two want opposite answers. So the declaration travels to the host that\n * does know, instead of being discarded (the defect) or enforced against an invented boundary.\n */\n userInvocable?: boolean;\n}\n\n/**\n * Parse a SKILL.md file body into validated frontmatter.\n *\n * @throws ConfigurationError(code: \"missing_frontmatter\") — no `---` block at file head.\n * @throws ConfigurationError(code: \"schema_invalid\") — YAML malformed OR required field missing.\n *\n * @internal\n */\nexport function parseSkillFrontmatter(raw: string, fallbackName: string): SkillFrontmatter {\n const parsed = extractAndParseFrontmatter(raw, fallbackName);\n const fields = toStringFields(parsed);\n const name = resolveName(fields, fallbackName);\n ensureRequiredFields(fields, name);\n return buildFrontmatter(fields, parsed, name);\n}\n\n/**\n * SE20 — return a SKILL.md's BODY (everything after the frontmatter block), trimmed.\n * When there is no frontmatter block, the whole file is the body. Reuses the same\n * frontmatter regex as {@link parseSkillFrontmatter} (DRY).\n */\nexport function stripSkillFrontmatter(raw: string): string {\n const match = /^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n/.exec(raw);\n return (match === null ? raw : raw.slice(match[0].length)).trim();\n}\n\nfunction extractAndParseFrontmatter(\n raw: string,\n fallbackName: string,\n): Record<string, FrontmatterValue | undefined> {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {\n code: \"missing_frontmatter\",\n });\n }\n const frontmatter = match[1] ?? \"\";\n // EC-5: guard against syntactically invalid frontmatter so the loader\n // surfaces schema_invalid rather than crashing.\n try {\n return parseSimpleYaml(frontmatter);\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n throw new ConfigurationError(\n `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,\n { code: \"schema_invalid\", cause },\n );\n }\n}\n\nfunction resolveName(fields: StringFields, fallbackName: string): string {\n if (hasContent(fields.name)) return fields.name;\n if (hasContent(fallbackName)) return fallbackName;\n throw new ConfigurationError(\"Skill at unknown path is missing required field: name\", {\n code: \"schema_invalid\",\n });\n}\n\nfunction ensureRequiredFields(fields: StringFields, name: string): void {\n if (!hasContent(fields.description)) {\n throw new ConfigurationError(`Skill ${name} is missing required field: description`, {\n code: \"schema_invalid\",\n });\n }\n}\n\nfunction buildFrontmatter(\n fields: StringFields,\n parsed: Record<string, FrontmatterValue | undefined>,\n name: string,\n): SkillFrontmatter {\n const description = fields.description;\n if (description === undefined) {\n // ensureRequiredFields already threw; this is unreachable but satisfies TS\n throw new ConfigurationError(`Skill ${name} missing description`, { code: \"schema_invalid\" });\n }\n const result: SkillFrontmatter = { name, description };\n if (hasContent(fields.category)) result.category = fields.category;\n const deps = parseDependencies(fields.dependencies);\n if (deps !== undefined) result.dependencies = deps;\n const bins = parseDependencies(fields[\"requires-bins\"]);\n if (bins !== undefined) result.requiresBins = bins;\n const disable = readAuthorizationFlag(parsed, \"disable-model-invocation\", name);\n if (disable !== undefined) result.disableModelInvocation = disable;\n const invocable = readAuthorizationFlag(parsed, \"user-invocable\", name);\n if (invocable !== undefined) result.userInvocable = invocable;\n return result;\n}\n\n/**\n * Read one boolean authorization flag, refusing a value this parser cannot represent.\n *\n * Unknown frontmatter keys are ignored here for forward compatibility, and that is right for\n * metadata. It is wrong for these two: the simple-YAML dialect coerces only the literals `true`\n * and `false`, so `disable-model-invocation: yes` — a valid YAML boolean — arrives as the STRING\n * `\"yes\"`, compares unequal to `true`, and the skill is disclosed to the model. The author wrote a\n * restriction and got the default.\n *\n * A restriction that fails open is worse than one that is absent, because the author stops looking.\n * So the value is refused and named, and the run stops on the line that caused it.\n */\nfunction readAuthorizationFlag(\n parsed: Record<string, FrontmatterValue | undefined>,\n key: string,\n name: string,\n): boolean | undefined {\n const value = parsed[key];\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n throw new ConfigurationError(\n `Skill ${name}: \"${key}\" must be true or false (got ${JSON.stringify(value)}) — ` +\n `this dialect reads only the literals \\`true\\` and \\`false\\`, and a value it cannot read ` +\n `would leave the skill disclosed`,\n { code: \"schema_invalid\" },\n );\n}\n\nfunction parseDependencies(raw: string | undefined): string[] | undefined {\n if (!hasContent(raw)) return undefined;\n const deps = (raw as string)\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return deps.length > 0 ? deps : undefined;\n}\n\nfunction hasContent(value: string | undefined): value is string {\n return value !== undefined && value.trim().length > 0;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { assertNoSymlinkEscape, safePathJoin } from \"../../security/path-guard.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { parseSkillFrontmatter, stripSkillFrontmatter } from \"./skill-frontmatter.js\";\n\n/**\n * A discovered skill's metadata. The skill BODY is never included — only the\n * strict frontmatter fields plus the resolved `source` path.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport interface Skill {\n name: string;\n description: string;\n /** Absolute path to the discovered `SKILL.md`. */\n source: string;\n category?: string;\n dependencies?: string[];\n /** `requires-bins` — executables that must be on PATH for the model to be offered this skill. */\n requiresBins?: string[];\n /** `disable-model-invocation: true` — the model is never told this skill exists. */\n disableModelInvocation?: boolean;\n /** `user-invocable: false` — carried for a host that has a picker; this SDK has no such surface. */\n userInvocable?: boolean;\n}\n\n/**\n * Information passed to `onInvalidSkill` when a `SKILL.md` is present but its\n * frontmatter is malformed (missing required field or invalid YAML).\n *\n * @public\n */\nexport interface InvalidSkillInfo {\n /** The skill directory name (used as the fallback skill name). */\n name: string;\n /** Absolute path to the offending `SKILL.md`. */\n source: string;\n /** Typed reason: `missing_frontmatter` or `schema_invalid`. */\n code: string;\n message: string;\n}\n\n/**\n * Options for {@link discoverSkills}.\n *\n * @public\n */\nexport interface DiscoverSkillsOptions {\n /**\n * Called once per directory that contains a `SKILL.md` with malformed\n * frontmatter. The skill is excluded from the result; discovery continues\n * (strict-frontmatter ADR / EC-5). A directory WITHOUT a `SKILL.md` is NOT a\n * malformed skill and does not trigger this callback.\n *\n * Default: no-op (a library primitive must not write to the consumer's\n * stderr by default).\n */\n onInvalidSkill?: (info: InvalidSkillInfo) => void;\n}\n\n/**\n * Discover `SKILL.md` skills under an arbitrary directory.\n *\n * For each immediate subdirectory `<dir>/<name>/` containing a `SKILL.md`, the\n * file's strict YAML frontmatter is parsed (`name`/`description` required;\n * `category`/`dependencies` optional). Malformed skills are skipped (optionally\n * reported via {@link DiscoverSkillsOptions.onInvalidSkill}); a subdirectory\n * whose realpath escapes `dir` (via symlink) is skipped (symlink-escape guard,\n * reusing `@theokit/sdk/path-safety`).\n *\n * NEVER throws: a missing, unreadable, or non-directory `dir` yields `[]`.\n *\n * Discovery order follows the filesystem `readdir` order (OS-dependent). Sort\n * the result before {@link buildSkillsBlock} if a stable block order matters.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport async function discoverSkills(\n dir: string,\n options?: DiscoverSkillsOptions,\n): Promise<Skill[]> {\n let entries: Awaited<ReturnType<typeof readWorkspaceDir>>;\n try {\n entries = await readWorkspaceDir(dir, \"skills_read_error\", \"skills directory\");\n } catch {\n // never-throw contract: unreadable / not-a-directory → no skills (EC-1)\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n let skillDir: string;\n try {\n skillDir = safePathJoin(dir, entry.name);\n assertNoSymlinkEscape(skillDir, dir);\n } catch {\n continue;\n }\n const skillPath = join(skillDir, \"SKILL.md\");\n let raw: string;\n try {\n raw = await readFile(skillPath, \"utf8\");\n } catch {\n // no SKILL.md in this subdir → not a skill, not an error (EC-2)\n continue;\n }\n const skill = tryParseSkill(raw, entry.name, skillPath, options);\n if (skill !== undefined) skills.push(skill);\n }\n return skills;\n}\n\nfunction tryParseSkill(\n raw: string,\n fallbackName: string,\n source: string,\n options: DiscoverSkillsOptions | undefined,\n): Skill | undefined {\n try {\n const frontmatter = parseSkillFrontmatter(raw, fallbackName);\n const skill: Skill = {\n name: frontmatter.name,\n description: frontmatter.description,\n source,\n };\n if (frontmatter.category !== undefined) skill.category = frontmatter.category;\n if (frontmatter.dependencies !== undefined) skill.dependencies = frontmatter.dependencies;\n // The authorization flags travel with the record. Dropping them here would reproduce the defect\n // one layer down: the parser would read the declaration and the discovery result would not\n // carry it, which is indistinguishable from never having parsed it.\n if (frontmatter.requiresBins !== undefined) skill.requiresBins = frontmatter.requiresBins;\n if (frontmatter.disableModelInvocation !== undefined) {\n skill.disableModelInvocation = frontmatter.disableModelInvocation;\n }\n if (frontmatter.userInvocable !== undefined) skill.userInvocable = frontmatter.userInvocable;\n return skill;\n } catch (cause) {\n if (cause instanceof ConfigurationError) {\n options?.onInvalidSkill?.({\n name: fallbackName,\n source,\n code: cause.code ?? \"unknown\",\n message: cause.message,\n });\n return undefined;\n }\n throw cause;\n }\n}\n\n/**\n * Read the BODY of a discovered skill — everything after its frontmatter.\n *\n * A thin selector over {@link discoverSkills} rather than a second reader, which is the same\n * relationship `loadSubagentDefinition` has to `discoverSubagents` in the sibling domain: one\n * parser is the point.\n *\n * ## Why this exists rather than a field on `Skill`\n *\n * `Skill` documents that *\"the skill BODY is never included\"*. That is a written contract with no\n * written reason, and widening it on a guess about the reason is not a trade worth making — a\n * catalog you can put in a prompt without carrying every body is the likely intent, and this keeps\n * that shape intact for whoever relied on it.\n *\n * The body was never expensive to obtain: `discoverSkills` already reads each file in full and\n * discards everything but the frontmatter. What was missing was a door that hands it over.\n *\n * ## What it is for\n *\n * Turning a discovered skill into an inline one — `SkillsSettings.inline` requires `instructions`,\n * and without this the only route was to open `source` and split the frontmatter by hand. That is a\n * second implementation of this module's own convention, and it would fail SILENTLY if the format\n * moved: the frontmatter would land inside the instructions and nothing would say so.\n *\n * Reported by the `theocode` session, which needed exactly that to give an operator's\n * `~/.theokit/skills/` to an agent through the SDK's own parser.\n *\n * @param skill - a record returned by {@link discoverSkills}; its `source` is read.\n * @returns the trimmed body. A file that is all frontmatter yields an empty string.\n * @throws if `source` is unreadable — unlike discovery, which skips what it cannot read, a caller\n * naming ONE skill has asked about that skill and an empty string would answer a question it did\n * not ask.\n * @public\n */\nexport async function loadSkillInstructions(skill: Skill): Promise<string> {\n return stripSkillFrontmatter(await readFile(skill.source, \"utf8\"));\n}\n","/**\n * Block-body XML escape (ADR D9 — prompt-injection defence).\n *\n * Order matters: `&` MUST be escaped first so subsequent `<`/`>` replacements\n * do not double-encode the `&` characters they introduce.\n *\n * @internal\n */\nexport const escapeBlockBody = (s: string): string =>\n s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n","import { escapeBlockBody } from \"../system-prompt/escape.js\";\n\n/**\n * Render the `<skills>` system-prompt block from a skill list.\n *\n * Input is the structural subset `{ name, description }` — the skill BODY is\n * NOT in the type, so it cannot leak into the prompt. Both fields are passed\n * through `escapeBlockBody` to neutralise prompt-injection vectors hidden in\n * user-controlled SKILL.md frontmatter (injection-escape ADR).\n *\n * Returns `undefined` for an empty list so the caller can omit the block.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport function buildSkillsBlock(\n skills: ReadonlyArray<{ name: string; description: string }>,\n): string | undefined {\n if (skills.length === 0) return undefined;\n const lines = skills.map(\n (skill) => ` - ${escapeBlockBody(skill.name)}: ${escapeBlockBody(skill.description)}`,\n );\n return `<skills>\\n${lines.join(\"\\n\")}\\n</skills>`;\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/runtime/skills/skill-frontmatter.ts","../src/internal/runtime/skills/discover-skills.ts","../src/internal/runtime/system-prompt/escape.ts","../src/internal/runtime/skills/skills-block.ts"],"names":["ConfigurationError","parseSimpleYaml","readWorkspaceDir","safePathJoin","assertNoSymlinkEscape","join","readFile"],"mappings":"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","file":"chunk-NMRNE6QX.cjs","sourcesContent":["import { ConfigurationError } from \"../../../errors.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\n\ntype StringFields = Record<string, string | undefined>;\n\n/** Narrow a FrontmatterValue to string; non-strings + undefined → undefined. */\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Coerce parser output to legacy string-only shape (skill schema is all-string). */\nfunction toStringFields(raw: Record<string, FrontmatterValue | undefined>): StringFields {\n const out: StringFields = {};\n for (const [k, v] of Object.entries(raw)) out[k] = asString(v);\n return out;\n}\n\n/**\n * Strict skill frontmatter schema (ADR D10).\n *\n * Required: `name`, `description`.\n * Optional: `category`, `dependencies` (comma-separated string in the\n * simple-YAML dialect — parsed to `string[]`).\n *\n * Unknown fields are ignored (forward-compat). Malformed YAML or missing\n * required fields surface as `ConfigurationError` with one of the typed\n * codes below.\n *\n * @internal\n */\nexport interface SkillFrontmatter {\n name: string;\n description: string;\n category?: string;\n dependencies?: string[];\n /**\n * `requires-bins: git, rg` (B-546) — executables the skill needs on PATH. A skill whose binary is\n * absent is not offered to the model; `skills.get(name)` still resolves it.\n */\n requiresBins?: string[];\n /**\n * `disable-model-invocation: true` — the model is not told this skill exists.\n *\n * The SDK owns this boundary: a skill reaches the model through exactly one place,\n * `buildSystemPromptContext`, so the declaration is enforceable here. It is a DISCLOSURE rule,\n * not an execution rule — `skills.get(name)` still resolves it, because a caller naming a skill\n * has already made the decision the field exists to keep away from the model.\n */\n disableModelInvocation?: boolean;\n /**\n * `user-invocable: false` — carried, deliberately not enforced here.\n *\n * This SDK has no user-facing invocation surface for skills; there is no slash command. Reading\n * `agent.skills.list()` as \"the user\" would be a guess — a host may call it to build a picker or\n * to introspect, and the two want opposite answers. So the declaration travels to the host that\n * does know, instead of being discarded (the defect) or enforced against an invented boundary.\n */\n userInvocable?: boolean;\n}\n\n/**\n * Parse a SKILL.md file body into validated frontmatter.\n *\n * @throws ConfigurationError(code: \"missing_frontmatter\") — no `---` block at file head.\n * @throws ConfigurationError(code: \"schema_invalid\") — YAML malformed OR required field missing.\n *\n * @internal\n */\nexport function parseSkillFrontmatter(raw: string, fallbackName: string): SkillFrontmatter {\n const parsed = extractAndParseFrontmatter(raw, fallbackName);\n const fields = toStringFields(parsed);\n const name = resolveName(fields, fallbackName);\n ensureRequiredFields(fields, name);\n return buildFrontmatter(fields, parsed, name);\n}\n\n/**\n * SE20 — return a SKILL.md's BODY (everything after the frontmatter block), trimmed.\n * When there is no frontmatter block, the whole file is the body. Reuses the same\n * frontmatter regex as {@link parseSkillFrontmatter} (DRY).\n */\nexport function stripSkillFrontmatter(raw: string): string {\n const match = /^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n/.exec(raw);\n return (match === null ? raw : raw.slice(match[0].length)).trim();\n}\n\nfunction extractAndParseFrontmatter(\n raw: string,\n fallbackName: string,\n): Record<string, FrontmatterValue | undefined> {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {\n code: \"missing_frontmatter\",\n });\n }\n const frontmatter = match[1] ?? \"\";\n // EC-5: guard against syntactically invalid frontmatter so the loader\n // surfaces schema_invalid rather than crashing.\n try {\n return parseSimpleYaml(frontmatter);\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n throw new ConfigurationError(\n `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,\n { code: \"schema_invalid\", cause },\n );\n }\n}\n\nfunction resolveName(fields: StringFields, fallbackName: string): string {\n if (hasContent(fields.name)) return fields.name;\n if (hasContent(fallbackName)) return fallbackName;\n throw new ConfigurationError(\"Skill at unknown path is missing required field: name\", {\n code: \"schema_invalid\",\n });\n}\n\nfunction ensureRequiredFields(fields: StringFields, name: string): void {\n if (!hasContent(fields.description)) {\n throw new ConfigurationError(`Skill ${name} is missing required field: description`, {\n code: \"schema_invalid\",\n });\n }\n}\n\nfunction buildFrontmatter(\n fields: StringFields,\n parsed: Record<string, FrontmatterValue | undefined>,\n name: string,\n): SkillFrontmatter {\n const description = fields.description;\n if (description === undefined) {\n // ensureRequiredFields already threw; this is unreachable but satisfies TS\n throw new ConfigurationError(`Skill ${name} missing description`, { code: \"schema_invalid\" });\n }\n const result: SkillFrontmatter = { name, description };\n if (hasContent(fields.category)) result.category = fields.category;\n const deps = parseDependencies(fields.dependencies);\n if (deps !== undefined) result.dependencies = deps;\n const bins = parseDependencies(fields[\"requires-bins\"]);\n if (bins !== undefined) result.requiresBins = bins;\n const disable = readAuthorizationFlag(parsed, \"disable-model-invocation\", name);\n if (disable !== undefined) result.disableModelInvocation = disable;\n const invocable = readAuthorizationFlag(parsed, \"user-invocable\", name);\n if (invocable !== undefined) result.userInvocable = invocable;\n return result;\n}\n\n/**\n * Read one boolean authorization flag, refusing a value this parser cannot represent.\n *\n * Unknown frontmatter keys are ignored here for forward compatibility, and that is right for\n * metadata. It is wrong for these two: the simple-YAML dialect coerces only the literals `true`\n * and `false`, so `disable-model-invocation: yes` — a valid YAML boolean — arrives as the STRING\n * `\"yes\"`, compares unequal to `true`, and the skill is disclosed to the model. The author wrote a\n * restriction and got the default.\n *\n * A restriction that fails open is worse than one that is absent, because the author stops looking.\n * So the value is refused and named, and the run stops on the line that caused it.\n */\nfunction readAuthorizationFlag(\n parsed: Record<string, FrontmatterValue | undefined>,\n key: string,\n name: string,\n): boolean | undefined {\n const value = parsed[key];\n if (value === undefined) return undefined;\n if (typeof value === \"boolean\") return value;\n throw new ConfigurationError(\n `Skill ${name}: \"${key}\" must be true or false (got ${JSON.stringify(value)}) — ` +\n `this dialect reads only the literals \\`true\\` and \\`false\\`, and a value it cannot read ` +\n `would leave the skill disclosed`,\n { code: \"schema_invalid\" },\n );\n}\n\nfunction parseDependencies(raw: string | undefined): string[] | undefined {\n if (!hasContent(raw)) return undefined;\n const deps = (raw as string)\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return deps.length > 0 ? deps : undefined;\n}\n\nfunction hasContent(value: string | undefined): value is string {\n return value !== undefined && value.trim().length > 0;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { assertNoSymlinkEscape, safePathJoin } from \"../../security/path-guard.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { parseSkillFrontmatter, stripSkillFrontmatter } from \"./skill-frontmatter.js\";\n\n/**\n * A discovered skill's metadata. The skill BODY is never included — only the\n * strict frontmatter fields plus the resolved `source` path.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport interface Skill {\n name: string;\n description: string;\n /** Absolute path to the discovered `SKILL.md`. */\n source: string;\n category?: string;\n dependencies?: string[];\n /** `requires-bins` — executables that must be on PATH for the model to be offered this skill. */\n requiresBins?: string[];\n /** `disable-model-invocation: true` — the model is never told this skill exists. */\n disableModelInvocation?: boolean;\n /** `user-invocable: false` — carried for a host that has a picker; this SDK has no such surface. */\n userInvocable?: boolean;\n}\n\n/**\n * Information passed to `onInvalidSkill` when a `SKILL.md` is present but its\n * frontmatter is malformed (missing required field or invalid YAML).\n *\n * @public\n */\nexport interface InvalidSkillInfo {\n /** The skill directory name (used as the fallback skill name). */\n name: string;\n /** Absolute path to the offending `SKILL.md`. */\n source: string;\n /** Typed reason: `missing_frontmatter` or `schema_invalid`. */\n code: string;\n message: string;\n}\n\n/**\n * Options for {@link discoverSkills}.\n *\n * @public\n */\nexport interface DiscoverSkillsOptions {\n /**\n * Called once per directory that contains a `SKILL.md` with malformed\n * frontmatter. The skill is excluded from the result; discovery continues\n * (strict-frontmatter ADR / EC-5). A directory WITHOUT a `SKILL.md` is NOT a\n * malformed skill and does not trigger this callback.\n *\n * Default: no-op (a library primitive must not write to the consumer's\n * stderr by default).\n */\n onInvalidSkill?: (info: InvalidSkillInfo) => void;\n}\n\n/**\n * Discover `SKILL.md` skills under an arbitrary directory.\n *\n * For each immediate subdirectory `<dir>/<name>/` containing a `SKILL.md`, the\n * file's strict YAML frontmatter is parsed (`name`/`description` required;\n * `category`/`dependencies` optional). Malformed skills are skipped (optionally\n * reported via {@link DiscoverSkillsOptions.onInvalidSkill}); a subdirectory\n * whose realpath escapes `dir` (via symlink) is skipped (symlink-escape guard,\n * reusing `@theokit/sdk/path-safety`).\n *\n * NEVER throws: a missing, unreadable, or non-directory `dir` yields `[]`.\n *\n * Discovery order follows the filesystem `readdir` order (OS-dependent). Sort\n * the result before {@link buildSkillsBlock} if a stable block order matters.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport async function discoverSkills(\n dir: string,\n options?: DiscoverSkillsOptions,\n): Promise<Skill[]> {\n let entries: Awaited<ReturnType<typeof readWorkspaceDir>>;\n try {\n entries = await readWorkspaceDir(dir, \"skills_read_error\", \"skills directory\");\n } catch {\n // never-throw contract: unreadable / not-a-directory → no skills (EC-1)\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n let skillDir: string;\n try {\n skillDir = safePathJoin(dir, entry.name);\n assertNoSymlinkEscape(skillDir, dir);\n } catch {\n continue;\n }\n const skillPath = join(skillDir, \"SKILL.md\");\n let raw: string;\n try {\n raw = await readFile(skillPath, \"utf8\");\n } catch {\n // no SKILL.md in this subdir → not a skill, not an error (EC-2)\n continue;\n }\n const skill = tryParseSkill(raw, entry.name, skillPath, options);\n if (skill !== undefined) skills.push(skill);\n }\n return skills;\n}\n\nfunction tryParseSkill(\n raw: string,\n fallbackName: string,\n source: string,\n options: DiscoverSkillsOptions | undefined,\n): Skill | undefined {\n try {\n const frontmatter = parseSkillFrontmatter(raw, fallbackName);\n const skill: Skill = {\n name: frontmatter.name,\n description: frontmatter.description,\n source,\n };\n if (frontmatter.category !== undefined) skill.category = frontmatter.category;\n if (frontmatter.dependencies !== undefined) skill.dependencies = frontmatter.dependencies;\n // The authorization flags travel with the record. Dropping them here would reproduce the defect\n // one layer down: the parser would read the declaration and the discovery result would not\n // carry it, which is indistinguishable from never having parsed it.\n if (frontmatter.requiresBins !== undefined) skill.requiresBins = frontmatter.requiresBins;\n if (frontmatter.disableModelInvocation !== undefined) {\n skill.disableModelInvocation = frontmatter.disableModelInvocation;\n }\n if (frontmatter.userInvocable !== undefined) skill.userInvocable = frontmatter.userInvocable;\n return skill;\n } catch (cause) {\n if (cause instanceof ConfigurationError) {\n options?.onInvalidSkill?.({\n name: fallbackName,\n source,\n code: cause.code ?? \"unknown\",\n message: cause.message,\n });\n return undefined;\n }\n throw cause;\n }\n}\n\n/**\n * Read the BODY of a discovered skill — everything after its frontmatter.\n *\n * A thin selector over {@link discoverSkills} rather than a second reader, which is the same\n * relationship `loadSubagentDefinition` has to `discoverSubagents` in the sibling domain: one\n * parser is the point.\n *\n * ## Why this exists rather than a field on `Skill`\n *\n * `Skill` documents that *\"the skill BODY is never included\"*. That is a written contract with no\n * written reason, and widening it on a guess about the reason is not a trade worth making — a\n * catalog you can put in a prompt without carrying every body is the likely intent, and this keeps\n * that shape intact for whoever relied on it.\n *\n * The body was never expensive to obtain: `discoverSkills` already reads each file in full and\n * discards everything but the frontmatter. What was missing was a door that hands it over.\n *\n * ## What it is for\n *\n * Turning a discovered skill into an inline one — `SkillsSettings.inline` requires `instructions`,\n * and without this the only route was to open `source` and split the frontmatter by hand. That is a\n * second implementation of this module's own convention, and it would fail SILENTLY if the format\n * moved: the frontmatter would land inside the instructions and nothing would say so.\n *\n * Reported by the `theocode` session, which needed exactly that to give an operator's\n * `~/.theokit/skills/` to an agent through the SDK's own parser.\n *\n * @param skill - a record returned by {@link discoverSkills}; its `source` is read.\n * @returns the trimmed body. A file that is all frontmatter yields an empty string.\n * @throws if `source` is unreadable — unlike discovery, which skips what it cannot read, a caller\n * naming ONE skill has asked about that skill and an empty string would answer a question it did\n * not ask.\n * @public\n */\nexport async function loadSkillInstructions(skill: Skill): Promise<string> {\n return stripSkillFrontmatter(await readFile(skill.source, \"utf8\"));\n}\n","/**\n * Block-body XML escape (ADR D9 — prompt-injection defence).\n *\n * Order matters: `&` MUST be escaped first so subsequent `<`/`>` replacements\n * do not double-encode the `&` characters they introduce.\n *\n * @internal\n */\nexport const escapeBlockBody = (s: string): string =>\n s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n","import { escapeBlockBody } from \"../system-prompt/escape.js\";\n\n/**\n * Render the `<skills>` system-prompt block from a skill list.\n *\n * Input is the structural subset `{ name, description }` — the skill BODY is\n * NOT in the type, so it cannot leak into the prompt. Both fields are passed\n * through `escapeBlockBody` to neutralise prompt-injection vectors hidden in\n * user-controlled SKILL.md frontmatter (injection-escape ADR).\n *\n * Returns `undefined` for an empty list so the caller can omit the block.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport function buildSkillsBlock(\n skills: ReadonlyArray<{ name: string; description: string }>,\n): string | undefined {\n if (skills.length === 0) return undefined;\n const lines = skills.map(\n (skill) => ` - ${escapeBlockBody(skill.name)}: ${escapeBlockBody(skill.description)}`,\n );\n return `<skills>\\n${lines.join(\"\\n\")}\\n</skills>`;\n}\n"]}
@@ -1,8 +1,8 @@
1
- import { atOrInsideRoot } from './chunk-UKMBRMGT.js';
1
+ import { atOrInsideRoot } from './chunk-DGO4LPLP.js';
2
2
  import { ConfigurationError } from './chunk-TNFOU6SB.js';
3
- import { createHash } from 'crypto';
4
- import { realpathSync, lstatSync, readlinkSync } from 'fs';
5
- import { resolve, dirname } from 'path';
3
+ import { createHash } from 'node:crypto';
4
+ import { realpathSync, lstatSync, readlinkSync } from 'node:fs';
5
+ import { resolve, dirname } from 'node:path';
6
6
 
7
7
  var PathTraversalError = class extends ConfigurationError {
8
8
  name = "PathTraversalError";
@@ -218,5 +218,5 @@ function safeFilenameForId(id, options) {
218
218
  }
219
219
 
220
220
  export { ForbiddenPathError, PathTraversalError, assertNoSymlinkEscape, isForbiddenPath, safeFilenameForId, safePathJoin, sanitizeIdentifier, validateArtifactPath };
221
- //# sourceMappingURL=chunk-PKRPV3R7.js.map
222
- //# sourceMappingURL=chunk-PKRPV3R7.js.map
221
+ //# sourceMappingURL=chunk-NO6AG22P.js.map
222
+ //# sourceMappingURL=chunk-NO6AG22P.js.map