@theokit/sdk 5.11.1 → 5.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (408) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/a2a/index.cjs +8 -8
  3. package/dist/a2a/index.js +3 -3
  4. package/dist/agent-BVKIUP7F.cjs +71 -0
  5. package/dist/{agent-OKP4LHG2.cjs.map → agent-BVKIUP7F.cjs.map} +1 -1
  6. package/dist/{agent-CmKwSyyD.d.ts → agent-CVzDy_Oj.d.ts} +17 -8
  7. package/dist/{agent-XMkGzff_.d.cts → agent-Cn0sphpA.d.cts} +17 -8
  8. package/dist/agent-IG7BP6GN.js +62 -0
  9. package/dist/{agent-DFV52V6E.js.map → agent-IG7BP6GN.js.map} +1 -1
  10. package/dist/agent-prims-CZyxfCmS.d.cts +206 -0
  11. package/dist/agent-prims-CZyxfCmS.d.ts +206 -0
  12. package/dist/agent-session-store-22GKA7O4.js +6 -0
  13. package/dist/{agent-session-store-ODD3IZCW.js.map → agent-session-store-22GKA7O4.js.map} +1 -1
  14. package/dist/agent-session-store-WP6NGDVX.cjs +23 -0
  15. package/dist/{agent-session-store-SVEJHSUA.cjs.map → agent-session-store-WP6NGDVX.cjs.map} +1 -1
  16. package/dist/auth/index.cjs +33 -33
  17. package/dist/auth/index.js +6 -6
  18. package/dist/{batch-427O54FQ.js → batch-EP3FCL6D.js} +9 -9
  19. package/dist/{batch-427O54FQ.js.map → batch-EP3FCL6D.js.map} +1 -1
  20. package/dist/{batch-U4SO4RLX.cjs → batch-IZ5EDKJ5.cjs} +29 -29
  21. package/dist/{batch-U4SO4RLX.cjs.map → batch-IZ5EDKJ5.cjs.map} +1 -1
  22. package/dist/{chunk-IIYJMXYW.cjs → chunk-27V5I2WW.cjs} +4 -4
  23. package/dist/{chunk-IIYJMXYW.cjs.map → chunk-27V5I2WW.cjs.map} +1 -1
  24. package/dist/{chunk-DPY2M74T.js → chunk-2C24NSNV.js} +5 -5
  25. package/dist/{chunk-DPY2M74T.js.map → chunk-2C24NSNV.js.map} +1 -1
  26. package/dist/{chunk-7F7XE6CM.js → chunk-2S4HQCDF.js} +3 -3
  27. package/dist/{chunk-7F7XE6CM.js.map → chunk-2S4HQCDF.js.map} +1 -1
  28. package/dist/{chunk-LZJXEPEQ.js → chunk-2SFLLOMN.js} +3 -3
  29. package/dist/{chunk-LZJXEPEQ.js.map → chunk-2SFLLOMN.js.map} +1 -1
  30. package/dist/{chunk-RGQICWFU.js → chunk-354F3OF5.js} +3 -3
  31. package/dist/{chunk-RGQICWFU.js.map → chunk-354F3OF5.js.map} +1 -1
  32. package/dist/{chunk-TX56N7FR.js → chunk-3FSBE4PS.js} +4 -4
  33. package/dist/{chunk-TX56N7FR.js.map → chunk-3FSBE4PS.js.map} +1 -1
  34. package/dist/{chunk-RQMCOFAI.cjs → chunk-3HA7N2BB.cjs} +8 -8
  35. package/dist/{chunk-RQMCOFAI.cjs.map → chunk-3HA7N2BB.cjs.map} +1 -1
  36. package/dist/{chunk-GM54F4FE.cjs → chunk-3KIUY67P.cjs} +9 -9
  37. package/dist/{chunk-GM54F4FE.cjs.map → chunk-3KIUY67P.cjs.map} +1 -1
  38. package/dist/{chunk-CZJ6Q7CW.js → chunk-3OY6H3I7.js} +66 -7
  39. package/dist/chunk-3OY6H3I7.js.map +1 -0
  40. package/dist/{chunk-ZWACQM5U.cjs → chunk-3WKWBYPO.cjs} +9 -9
  41. package/dist/{chunk-ZWACQM5U.cjs.map → chunk-3WKWBYPO.cjs.map} +1 -1
  42. package/dist/{chunk-Q34EBNYG.js → chunk-4GUFNDZR.js} +5 -5
  43. package/dist/{chunk-Q34EBNYG.js.map → chunk-4GUFNDZR.js.map} +1 -1
  44. package/dist/{chunk-XDUGSTYJ.cjs → chunk-4YD2WI57.cjs} +15 -15
  45. package/dist/{chunk-XDUGSTYJ.cjs.map → chunk-4YD2WI57.cjs.map} +1 -1
  46. package/dist/{chunk-SKDNCTXG.cjs → chunk-5A4UFBHW.cjs} +15 -15
  47. package/dist/{chunk-SKDNCTXG.cjs.map → chunk-5A4UFBHW.cjs.map} +1 -1
  48. package/dist/{chunk-SUVCHX4L.js → chunk-5AR6O7GW.js} +3 -3
  49. package/dist/{chunk-SUVCHX4L.js.map → chunk-5AR6O7GW.js.map} +1 -1
  50. package/dist/{chunk-SVCPJRUB.js → chunk-5HSMH2ZZ.js} +6 -6
  51. package/dist/{chunk-SVCPJRUB.js.map → chunk-5HSMH2ZZ.js.map} +1 -1
  52. package/dist/{chunk-GTX6FEDP.js → chunk-5IRDK5BC.js} +3 -3
  53. package/dist/{chunk-GTX6FEDP.js.map → chunk-5IRDK5BC.js.map} +1 -1
  54. package/dist/{chunk-H3CVJSFC.js → chunk-5IUDICRK.js} +3 -3
  55. package/dist/{chunk-H3CVJSFC.js.map → chunk-5IUDICRK.js.map} +1 -1
  56. package/dist/{chunk-FYEHYQ7T.cjs → chunk-6CXRYPG7.cjs} +15 -15
  57. package/dist/{chunk-FYEHYQ7T.cjs.map → chunk-6CXRYPG7.cjs.map} +1 -1
  58. package/dist/{chunk-NQVWSSX2.cjs → chunk-6OCJWGVD.cjs} +12 -12
  59. package/dist/{chunk-NQVWSSX2.cjs.map → chunk-6OCJWGVD.cjs.map} +1 -1
  60. package/dist/{chunk-MPR2ISL2.cjs → chunk-6XQLTQB6.cjs} +4 -4
  61. package/dist/{chunk-MPR2ISL2.cjs.map → chunk-6XQLTQB6.cjs.map} +1 -1
  62. package/dist/{chunk-72CPLZPC.cjs → chunk-7INPXUJI.cjs} +5 -5
  63. package/dist/{chunk-72CPLZPC.cjs.map → chunk-7INPXUJI.cjs.map} +1 -1
  64. package/dist/{chunk-HG5LKFG7.cjs → chunk-7WARNHWW.cjs} +16 -16
  65. package/dist/{chunk-HG5LKFG7.cjs.map → chunk-7WARNHWW.cjs.map} +1 -1
  66. package/dist/{chunk-KIKKTW5O.cjs → chunk-7YUPSBDV.cjs} +4 -4
  67. package/dist/{chunk-KIKKTW5O.cjs.map → chunk-7YUPSBDV.cjs.map} +1 -1
  68. package/dist/{chunk-IZDDSLL2.js → chunk-AB4OPJLD.js} +6 -6
  69. package/dist/{chunk-IZDDSLL2.js.map → chunk-AB4OPJLD.js.map} +1 -1
  70. package/dist/{chunk-XERM7PDQ.js → chunk-AGO5S5WD.js} +3 -3
  71. package/dist/{chunk-XERM7PDQ.js.map → chunk-AGO5S5WD.js.map} +1 -1
  72. package/dist/{chunk-ZWFTECOE.js → chunk-AHKS3DKI.js} +3 -3
  73. package/dist/{chunk-ZWFTECOE.js.map → chunk-AHKS3DKI.js.map} +1 -1
  74. package/dist/{chunk-ECD6ER3E.cjs → chunk-AIZ6WXSL.cjs} +4 -4
  75. package/dist/{chunk-ECD6ER3E.cjs.map → chunk-AIZ6WXSL.cjs.map} +1 -1
  76. package/dist/{chunk-KO6SHCEH.js → chunk-AQW54OWQ.js} +3 -3
  77. package/dist/{chunk-KO6SHCEH.js.map → chunk-AQW54OWQ.js.map} +1 -1
  78. package/dist/{chunk-P22BZE7G.cjs → chunk-AT2PESXB.cjs} +7 -7
  79. package/dist/{chunk-P22BZE7G.cjs.map → chunk-AT2PESXB.cjs.map} +1 -1
  80. package/dist/{chunk-MHIBYKWA.js → chunk-BJ7BPLQK.js} +131 -72
  81. package/dist/chunk-BJ7BPLQK.js.map +1 -0
  82. package/dist/{chunk-HV4V63CO.js → chunk-BO3PV3OP.js} +4 -4
  83. package/dist/{chunk-HV4V63CO.js.map → chunk-BO3PV3OP.js.map} +1 -1
  84. package/dist/{chunk-7NQ7HJ3E.cjs → chunk-BRWI4HLK.cjs} +6 -6
  85. package/dist/{chunk-7NQ7HJ3E.cjs.map → chunk-BRWI4HLK.cjs.map} +1 -1
  86. package/dist/{chunk-GHHIJEFX.cjs → chunk-BWYC7RZG.cjs} +9 -9
  87. package/dist/{chunk-GHHIJEFX.cjs.map → chunk-BWYC7RZG.cjs.map} +1 -1
  88. package/dist/{chunk-UNFBHV46.cjs → chunk-CEGU5AVD.cjs} +9 -9
  89. package/dist/chunk-CEGU5AVD.cjs.map +1 -0
  90. package/dist/{chunk-JV7JA7BM.cjs → chunk-CGH4BCIY.cjs} +11 -11
  91. package/dist/{chunk-JV7JA7BM.cjs.map → chunk-CGH4BCIY.cjs.map} +1 -1
  92. package/dist/{chunk-L3C6CCBG.cjs → chunk-CWQPQTUZ.cjs} +21 -21
  93. package/dist/{chunk-L3C6CCBG.cjs.map → chunk-CWQPQTUZ.cjs.map} +1 -1
  94. package/dist/{chunk-X3LOJJMO.js → chunk-DF77RBF4.js} +9 -9
  95. package/dist/{chunk-X3LOJJMO.js.map → chunk-DF77RBF4.js.map} +1 -1
  96. package/dist/{chunk-GMSRRJ53.js → chunk-EEK7S77K.js} +3 -3
  97. package/dist/{chunk-GMSRRJ53.js.map → chunk-EEK7S77K.js.map} +1 -1
  98. package/dist/{chunk-NKIQTLFA.cjs → chunk-EQ5IDL6W.cjs} +4 -4
  99. package/dist/{chunk-NKIQTLFA.cjs.map → chunk-EQ5IDL6W.cjs.map} +1 -1
  100. package/dist/{chunk-D5KXKBEN.cjs → chunk-FEAWEBWN.cjs} +5 -5
  101. package/dist/{chunk-D5KXKBEN.cjs.map → chunk-FEAWEBWN.cjs.map} +1 -1
  102. package/dist/{chunk-NJSIAJEK.cjs → chunk-FLDMPVUC.cjs} +4 -4
  103. package/dist/{chunk-NJSIAJEK.cjs.map → chunk-FLDMPVUC.cjs.map} +1 -1
  104. package/dist/{chunk-ASK2OQM5.cjs → chunk-G7XBVM3T.cjs} +5 -5
  105. package/dist/{chunk-ASK2OQM5.cjs.map → chunk-G7XBVM3T.cjs.map} +1 -1
  106. package/dist/{chunk-U27XHOEF.js → chunk-H7G65PNI.js} +5 -5
  107. package/dist/{chunk-U27XHOEF.js.map → chunk-H7G65PNI.js.map} +1 -1
  108. package/dist/{chunk-JW3GVLM6.cjs → chunk-H7HD6RXO.cjs} +4 -4
  109. package/dist/{chunk-JW3GVLM6.cjs.map → chunk-H7HD6RXO.cjs.map} +1 -1
  110. package/dist/{chunk-ZHWKT2NI.js → chunk-HJ2LV464.js} +3 -3
  111. package/dist/{chunk-ZHWKT2NI.js.map → chunk-HJ2LV464.js.map} +1 -1
  112. package/dist/{chunk-KGZ66JVZ.cjs → chunk-HN4IKB6X.cjs} +14 -14
  113. package/dist/{chunk-KGZ66JVZ.cjs.map → chunk-HN4IKB6X.cjs.map} +1 -1
  114. package/dist/{chunk-XKMVJ443.js → chunk-HWMGR3HA.js} +6 -6
  115. package/dist/{chunk-XKMVJ443.js.map → chunk-HWMGR3HA.js.map} +1 -1
  116. package/dist/{chunk-EQ5NCH2Y.cjs → chunk-IO4RI5ZJ.cjs} +24 -24
  117. package/dist/{chunk-EQ5NCH2Y.cjs.map → chunk-IO4RI5ZJ.cjs.map} +1 -1
  118. package/dist/{chunk-OLSEWLK5.cjs → chunk-IOFMRS6T.cjs} +6 -6
  119. package/dist/{chunk-OLSEWLK5.cjs.map → chunk-IOFMRS6T.cjs.map} +1 -1
  120. package/dist/{chunk-7QJYKVP6.js → chunk-KCM3RMKR.js} +3 -3
  121. package/dist/{chunk-7QJYKVP6.js.map → chunk-KCM3RMKR.js.map} +1 -1
  122. package/dist/{chunk-7KIN3L2S.js → chunk-KU5O7WPK.js} +3 -3
  123. package/dist/{chunk-7KIN3L2S.js.map → chunk-KU5O7WPK.js.map} +1 -1
  124. package/dist/{chunk-J5623RIV.cjs → chunk-KX5QXLBS.cjs} +10 -10
  125. package/dist/{chunk-J5623RIV.cjs.map → chunk-KX5QXLBS.cjs.map} +1 -1
  126. package/dist/{chunk-J6GXS5A2.cjs → chunk-L7JPXZ2C.cjs} +17 -17
  127. package/dist/{chunk-J6GXS5A2.cjs.map → chunk-L7JPXZ2C.cjs.map} +1 -1
  128. package/dist/{chunk-P7NNKEWO.cjs → chunk-L7V4DDIE.cjs} +4 -4
  129. package/dist/{chunk-P7NNKEWO.cjs.map → chunk-L7V4DDIE.cjs.map} +1 -1
  130. package/dist/chunk-LO6LIUZ6.cjs +18 -0
  131. package/dist/{chunk-SGC7LSHF.cjs.map → chunk-LO6LIUZ6.cjs.map} +1 -1
  132. package/dist/{chunk-P5N3Q2NH.cjs → chunk-LSWHDOCL.cjs} +192 -148
  133. package/dist/chunk-LSWHDOCL.cjs.map +1 -0
  134. package/dist/{chunk-3JHIFQ4I.js → chunk-M4U24U6G.js} +3 -3
  135. package/dist/{chunk-3JHIFQ4I.js.map → chunk-M4U24U6G.js.map} +1 -1
  136. package/dist/{chunk-GGRPAQ3B.js → chunk-MLGETTYX.js} +5 -5
  137. package/dist/{chunk-GGRPAQ3B.js.map → chunk-MLGETTYX.js.map} +1 -1
  138. package/dist/{chunk-XZHJYSPB.js → chunk-MLJDW2YK.js} +4 -4
  139. package/dist/{chunk-XZHJYSPB.js.map → chunk-MLJDW2YK.js.map} +1 -1
  140. package/dist/{chunk-5Q4RTLMP.js → chunk-MS3TQSXU.js} +3 -3
  141. package/dist/{chunk-5Q4RTLMP.js.map → chunk-MS3TQSXU.js.map} +1 -1
  142. package/dist/{chunk-W2ZVTEOD.js → chunk-MSEL5E5P.js} +6 -6
  143. package/dist/{chunk-W2ZVTEOD.js.map → chunk-MSEL5E5P.js.map} +1 -1
  144. package/dist/{chunk-ZAM7BJXJ.cjs → chunk-MSW7LB2V.cjs} +5 -5
  145. package/dist/{chunk-ZAM7BJXJ.cjs.map → chunk-MSW7LB2V.cjs.map} +1 -1
  146. package/dist/{chunk-7HUCU3KK.js → chunk-NHOEXFZT.js} +5 -5
  147. package/dist/{chunk-7HUCU3KK.js.map → chunk-NHOEXFZT.js.map} +1 -1
  148. package/dist/{chunk-6G4IRQUD.js → chunk-NJBJ3CUG.js} +6 -6
  149. package/dist/{chunk-6G4IRQUD.js.map → chunk-NJBJ3CUG.js.map} +1 -1
  150. package/dist/{chunk-I2ORT6QD.js → chunk-NKQVLWDF.js} +3 -3
  151. package/dist/{chunk-I2ORT6QD.js.map → chunk-NKQVLWDF.js.map} +1 -1
  152. package/dist/{chunk-N7KS3TIR.cjs → chunk-NP362THY.cjs} +28 -15
  153. package/dist/chunk-NP362THY.cjs.map +1 -0
  154. package/dist/{chunk-NWS2CS3Y.cjs → chunk-NWWTYBD3.cjs} +5 -5
  155. package/dist/{chunk-NWS2CS3Y.cjs.map → chunk-NWWTYBD3.cjs.map} +1 -1
  156. package/dist/{chunk-SVQIW5RJ.js → chunk-OLJOI4LK.js} +4 -4
  157. package/dist/{chunk-SVQIW5RJ.js.map → chunk-OLJOI4LK.js.map} +1 -1
  158. package/dist/{chunk-7JAKO3NM.js → chunk-ORLTGPOS.js} +3 -3
  159. package/dist/{chunk-7JAKO3NM.js.map → chunk-ORLTGPOS.js.map} +1 -1
  160. package/dist/{chunk-5DD4NJN3.js → chunk-PKRPV3R7.js} +3 -3
  161. package/dist/{chunk-5DD4NJN3.js.map → chunk-PKRPV3R7.js.map} +1 -1
  162. package/dist/{chunk-R5YPDAKD.cjs → chunk-POBZXULR.cjs} +4 -4
  163. package/dist/{chunk-R5YPDAKD.cjs.map → chunk-POBZXULR.cjs.map} +1 -1
  164. package/dist/{chunk-35KBC4BX.cjs → chunk-Q6WT5535.cjs} +446 -387
  165. package/dist/chunk-Q6WT5535.cjs.map +1 -0
  166. package/dist/{chunk-MQGSOKR3.js → chunk-Q7UG242V.js} +3 -3
  167. package/dist/{chunk-MQGSOKR3.js.map → chunk-Q7UG242V.js.map} +1 -1
  168. package/dist/{chunk-XKZFIP7Z.cjs → chunk-QG4SXZ2A.cjs} +4 -4
  169. package/dist/{chunk-XKZFIP7Z.cjs.map → chunk-QG4SXZ2A.cjs.map} +1 -1
  170. package/dist/{chunk-NAGPKWOD.cjs → chunk-QISSETTG.cjs} +4 -4
  171. package/dist/{chunk-NAGPKWOD.cjs.map → chunk-QISSETTG.cjs.map} +1 -1
  172. package/dist/{chunk-OR6XIWLB.js → chunk-QK2ERTO3.js} +4 -4
  173. package/dist/{chunk-OR6XIWLB.js.map → chunk-QK2ERTO3.js.map} +1 -1
  174. package/dist/{chunk-ZDYDBXSD.js → chunk-RN3XDZGS.js} +6 -6
  175. package/dist/{chunk-ZDYDBXSD.js.map → chunk-RN3XDZGS.js.map} +1 -1
  176. package/dist/{chunk-6LHQPOMI.cjs → chunk-RNXXYF22.cjs} +66 -7
  177. package/dist/chunk-RNXXYF22.cjs.map +1 -0
  178. package/dist/{chunk-TMKTSYDS.cjs → chunk-RS7YQR4X.cjs} +6 -6
  179. package/dist/{chunk-TMKTSYDS.cjs.map → chunk-RS7YQR4X.cjs.map} +1 -1
  180. package/dist/{chunk-HU666T3Q.cjs → chunk-S46WJTDL.cjs} +14 -14
  181. package/dist/{chunk-HU666T3Q.cjs.map → chunk-S46WJTDL.cjs.map} +1 -1
  182. package/dist/{chunk-ZD66GFH5.cjs → chunk-SDZ4XDWS.cjs} +35 -35
  183. package/dist/{chunk-ZD66GFH5.cjs.map → chunk-SDZ4XDWS.cjs.map} +1 -1
  184. package/dist/{chunk-ZJZ7LV3A.js → chunk-SV63WTA2.js} +3 -3
  185. package/dist/{chunk-ZJZ7LV3A.js.map → chunk-SV63WTA2.js.map} +1 -1
  186. package/dist/{chunk-CQYBJOB2.cjs → chunk-SXH37C7X.cjs} +4 -4
  187. package/dist/{chunk-CQYBJOB2.cjs.map → chunk-SXH37C7X.cjs.map} +1 -1
  188. package/dist/{chunk-GMRFAX4I.js → chunk-SZQFCHU6.js} +4 -4
  189. package/dist/{chunk-GMRFAX4I.js.map → chunk-SZQFCHU6.js.map} +1 -1
  190. package/dist/{chunk-6PB5AQCY.cjs → chunk-TLVI2OQK.cjs} +16 -16
  191. package/dist/{chunk-6PB5AQCY.cjs.map → chunk-TLVI2OQK.cjs.map} +1 -1
  192. package/dist/{chunk-B5MHRQJZ.js → chunk-TNFOU6SB.js} +3 -3
  193. package/dist/chunk-TNFOU6SB.js.map +1 -0
  194. package/dist/{chunk-N6OEX5TN.cjs → chunk-UDAKAE4Z.cjs} +16 -16
  195. package/dist/{chunk-N6OEX5TN.cjs.map → chunk-UDAKAE4Z.cjs.map} +1 -1
  196. package/dist/{chunk-26NEAJ6T.js → chunk-VVGFZQZC.js} +4 -4
  197. package/dist/{chunk-26NEAJ6T.js.map → chunk-VVGFZQZC.js.map} +1 -1
  198. package/dist/{chunk-XCQ432OX.js → chunk-VZGE7NAI.js} +3 -3
  199. package/dist/{chunk-XCQ432OX.js.map → chunk-VZGE7NAI.js.map} +1 -1
  200. package/dist/{chunk-C7GKRGKN.cjs → chunk-WDVG5R2Q.cjs} +19 -19
  201. package/dist/{chunk-C7GKRGKN.cjs.map → chunk-WDVG5R2Q.cjs.map} +1 -1
  202. package/dist/{chunk-FTZTZBCU.js → chunk-XESYJ4JC.js} +4 -4
  203. package/dist/{chunk-FTZTZBCU.js.map → chunk-XESYJ4JC.js.map} +1 -1
  204. package/dist/{chunk-OPAKPEPG.js → chunk-XJ3WU5CZ.js} +5 -5
  205. package/dist/{chunk-OPAKPEPG.js.map → chunk-XJ3WU5CZ.js.map} +1 -1
  206. package/dist/{chunk-BBJ77S62.js → chunk-XJ4C4NFX.js} +64 -20
  207. package/dist/chunk-XJ4C4NFX.js.map +1 -0
  208. package/dist/{chunk-AY43232P.js → chunk-XRPMZ7TO.js} +4 -4
  209. package/dist/{chunk-AY43232P.js.map → chunk-XRPMZ7TO.js.map} +1 -1
  210. package/dist/{chunk-X6DUEDAF.js → chunk-XZKIAENA.js} +3 -3
  211. package/dist/{chunk-X6DUEDAF.js.map → chunk-XZKIAENA.js.map} +1 -1
  212. package/dist/{chunk-X7EUUHXU.js → chunk-YMVTUV5K.js} +3 -3
  213. package/dist/{chunk-X7EUUHXU.js.map → chunk-YMVTUV5K.js.map} +1 -1
  214. package/dist/{chunk-3JANYJYS.cjs → chunk-YOVZMN4P.cjs} +8 -8
  215. package/dist/{chunk-3JANYJYS.cjs.map → chunk-YOVZMN4P.cjs.map} +1 -1
  216. package/dist/{chunk-TY56BKSK.cjs → chunk-YQ3XC6C2.cjs} +4 -4
  217. package/dist/{chunk-TY56BKSK.cjs.map → chunk-YQ3XC6C2.cjs.map} +1 -1
  218. package/dist/{chunk-JLIRFWUG.js → chunk-ZJAYFSVI.js} +22 -9
  219. package/dist/chunk-ZJAYFSVI.js.map +1 -0
  220. package/dist/{chunk-MU7IAA7G.js → chunk-ZLFFDPXC.js} +9 -9
  221. package/dist/{chunk-MU7IAA7G.js.map → chunk-ZLFFDPXC.js.map} +1 -1
  222. package/dist/{chunk-JLRLCBJ4.cjs → chunk-ZOFRNNZD.cjs} +4 -4
  223. package/dist/{chunk-JLRLCBJ4.cjs.map → chunk-ZOFRNNZD.cjs.map} +1 -1
  224. package/dist/{chunk-MPQJE3G4.cjs → chunk-ZWLFHSON.cjs} +15 -15
  225. package/dist/{chunk-MPQJE3G4.cjs.map → chunk-ZWLFHSON.cjs.map} +1 -1
  226. package/dist/compact-session-3U2JYTVG.js +25 -0
  227. package/dist/{compact-session-4HAUP3HK.js.map → compact-session-3U2JYTVG.js.map} +1 -1
  228. package/dist/{compact-session-Y56D3S57.cjs → compact-session-NM56BREJ.cjs} +24 -24
  229. package/dist/{compact-session-Y56D3S57.cjs.map → compact-session-NM56BREJ.cjs.map} +1 -1
  230. package/dist/compaction.cjs +18 -18
  231. package/dist/compaction.js +3 -3
  232. package/dist/concurrency.cjs +6 -6
  233. package/dist/concurrency.js +4 -4
  234. package/dist/context/index.cjs +9 -9
  235. package/dist/context/index.js +6 -6
  236. package/dist/context-4RSI3AXT.cjs +24 -0
  237. package/dist/{context-B5J4GX2F.cjs.map → context-4RSI3AXT.cjs.map} +1 -1
  238. package/dist/context-7EBZST3L.js +7 -0
  239. package/dist/{context-3FZRAO5K.js.map → context-7EBZST3L.js.map} +1 -1
  240. package/dist/{cron-Blj6wMYg.d.cts → cron-BWDYetbq.d.cts} +2 -2
  241. package/dist/{cron-BPccQCO8.d.ts → cron-CHn-F0fC.d.ts} +2 -2
  242. package/dist/cron.cjs +42 -42
  243. package/dist/cron.d.cts +4 -4
  244. package/dist/cron.d.ts +4 -4
  245. package/dist/cron.js +41 -41
  246. package/dist/{errors-BWOJeWTZ.d.cts → errors-BiDQFWTG.d.cts} +10 -7
  247. package/dist/{errors-DDvG18IB.d.ts → errors-Pf22PJJ-.d.ts} +10 -7
  248. package/dist/errors.cjs +23 -23
  249. package/dist/errors.d.cts +2 -3
  250. package/dist/errors.d.ts +2 -3
  251. package/dist/errors.js +2 -2
  252. package/dist/eval.cjs +54 -54
  253. package/dist/eval.js +42 -42
  254. package/dist/{executor-3NI7KNL4.js → executor-3D2UDJHW.js} +8 -8
  255. package/dist/{executor-3NI7KNL4.js.map → executor-3D2UDJHW.js.map} +1 -1
  256. package/dist/{executor-2SUGI3ZT.cjs → executor-HKHT4CWS.cjs} +26 -26
  257. package/dist/{executor-2SUGI3ZT.cjs.map → executor-HKHT4CWS.cjs.map} +1 -1
  258. package/dist/filesystem/index.cjs +12 -12
  259. package/dist/filesystem/index.js +3 -3
  260. package/dist/fs-session-store-6ZCCJX3K.js +11 -0
  261. package/dist/{fs-session-store-3VI4CPTX.js.map → fs-session-store-6ZCCJX3K.js.map} +1 -1
  262. package/dist/fs-session-store-S2AXVJIX.cjs +20 -0
  263. package/dist/{fs-session-store-IQ6PEHYF.cjs.map → fs-session-store-S2AXVJIX.cjs.map} +1 -1
  264. package/dist/generate-object-S7MNVF2V.js +7 -0
  265. package/dist/{generate-object-NTKAWE7D.js.map → generate-object-S7MNVF2V.js.map} +1 -1
  266. package/dist/generate-object-XBWD2PU7.cjs +20 -0
  267. package/dist/{generate-object-YHTWG5DB.cjs.map → generate-object-XBWD2PU7.cjs.map} +1 -1
  268. package/dist/index-manager-HO456AVW.cjs +27 -0
  269. package/dist/{index-manager-YGXRFJMZ.cjs.map → index-manager-HO456AVW.cjs.map} +1 -1
  270. package/dist/index-manager-QDH56FER.js +18 -0
  271. package/dist/{index-manager-JVYIVTZG.js.map → index-manager-QDH56FER.js.map} +1 -1
  272. package/dist/index.cjs +172 -172
  273. package/dist/index.d.cts +9 -9
  274. package/dist/index.d.ts +9 -9
  275. package/dist/index.js +59 -59
  276. package/dist/{inject-session-R73PRM7B.cjs → inject-session-JVTZNQF6.cjs} +9 -9
  277. package/dist/{inject-session-R73PRM7B.cjs.map → inject-session-JVTZNQF6.cjs.map} +1 -1
  278. package/dist/{inject-session-LJX5JGV5.js → inject-session-THABTKJY.js} +7 -7
  279. package/dist/{inject-session-LJX5JGV5.js.map → inject-session-THABTKJY.js.map} +1 -1
  280. package/dist/interactive/index.cjs +4 -4
  281. package/dist/interactive/index.js +2 -2
  282. package/dist/internal/budget/normalize-usage.d.ts +5 -1
  283. package/dist/internal/error-mappers/openai-compatible.d.ts +3 -1
  284. package/dist/internal/memory/adapters/index.cjs +8 -8
  285. package/dist/internal/memory/adapters/index.js +7 -7
  286. package/dist/internal/memory/storage/index.cjs +42 -42
  287. package/dist/internal/memory/storage/index.js +10 -10
  288. package/dist/internal/persistence/index.cjs +25 -25
  289. package/dist/internal/persistence/index.js +8 -8
  290. package/dist/internal/providers/catalog-schema.d.ts +1 -1
  291. package/dist/internal/runtime/lifecycle/throw-on-error-run.d.ts +18 -0
  292. package/dist/internal/runtime/model-selection.d.ts +1 -1
  293. package/dist/internal/runtime/system-prompt/native-reasoning.d.ts +2 -2
  294. package/dist/internal/runtime/typed-run-error.d.ts +16 -0
  295. package/dist/internal/security/index.cjs +13 -13
  296. package/dist/internal/security/index.js +3 -3
  297. package/dist/judge-call-4LR4IFVT.js +5 -0
  298. package/dist/{judge-call-LOL5FQEX.js.map → judge-call-4LR4IFVT.js.map} +1 -1
  299. package/dist/judge-call-ZTUQMQRL.cjs +22 -0
  300. package/dist/{judge-call-AQQYK6VT.cjs.map → judge-call-ZTUQMQRL.cjs.map} +1 -1
  301. package/dist/mcp-auth.cjs +14 -14
  302. package/dist/mcp-auth.js +4 -4
  303. package/dist/models.cjs +22 -22
  304. package/dist/models.js +8 -8
  305. package/dist/oauth-transaction-store-OMX436YB.cjs +38 -0
  306. package/dist/{oauth-transaction-store-ZINDV7AF.cjs.map → oauth-transaction-store-OMX436YB.cjs.map} +1 -1
  307. package/dist/oauth-transaction-store-U34YLEGP.js +5 -0
  308. package/dist/{oauth-transaction-store-NP2YHZ6D.js.map → oauth-transaction-store-U34YLEGP.js.map} +1 -1
  309. package/dist/path-safety.cjs +10 -10
  310. package/dist/path-safety.js +3 -3
  311. package/dist/persistence.cjs +31 -31
  312. package/dist/persistence.js +10 -10
  313. package/dist/project.cjs +5 -5
  314. package/dist/project.js +3 -3
  315. package/dist/providers.cjs +9 -9
  316. package/dist/providers.js +7 -7
  317. package/dist/registry-5TDAF7RK.js +9 -0
  318. package/dist/{registry-5FK42CRV.js.map → registry-5TDAF7RK.js.map} +1 -1
  319. package/dist/registry-W7TSCLQO.cjs +46 -0
  320. package/dist/{registry-BVTNO73E.cjs.map → registry-W7TSCLQO.cjs.map} +1 -1
  321. package/dist/retry.cjs +4 -4
  322. package/dist/retry.js +3 -3
  323. package/dist/{run-until-DG2KXWB2.cjs → run-until-4KHVF7YC.cjs} +8 -4
  324. package/dist/run-until-4KHVF7YC.cjs.map +1 -0
  325. package/dist/{run-until-6D6TQ4JN.js → run-until-G6BW2DWM.js} +8 -4
  326. package/dist/run-until-G6BW2DWM.js.map +1 -0
  327. package/dist/sandbox/index.cjs +22 -22
  328. package/dist/sandbox/index.js +4 -4
  329. package/dist/{run-DsA5Ip2i.d.cts → sdk-agent-4-dXI2GS.d.cts} +1500 -753
  330. package/dist/{run-DsA5Ip2i.d.ts → sdk-agent-CTOJlWA_.d.ts} +1500 -753
  331. package/dist/server/auth/index.cjs +22 -22
  332. package/dist/server/auth/index.js +7 -7
  333. package/dist/server/errors-envelope.cjs +14 -14
  334. package/dist/server/errors-envelope.d.cts +2 -3
  335. package/dist/server/errors-envelope.d.ts +2 -3
  336. package/dist/server/errors-envelope.js +3 -3
  337. package/dist/session-transcript-7TZ6HXSX.cjs +62 -0
  338. package/dist/{session-transcript-RACG7BK2.cjs.map → session-transcript-7TZ6HXSX.cjs.map} +1 -1
  339. package/dist/session-transcript-BSHZYYY6.js +5 -0
  340. package/dist/{session-transcript-AR7IBICT.js.map → session-transcript-BSHZYYY6.js.map} +1 -1
  341. package/dist/skills.cjs +8 -8
  342. package/dist/skills.js +5 -5
  343. package/dist/stream-object-R6SMYV4R.js +7 -0
  344. package/dist/{stream-object-QAEHG4YO.js.map → stream-object-R6SMYV4R.js.map} +1 -1
  345. package/dist/stream-object-TEEK3CAX.cjs +20 -0
  346. package/dist/{stream-object-TKVGGA54.cjs.map → stream-object-TEEK3CAX.cjs.map} +1 -1
  347. package/dist/subagents-loader-TXOA5EJJ.cjs +16 -0
  348. package/dist/{subagents-loader-5TZCAIYQ.cjs.map → subagents-loader-TXOA5EJJ.cjs.map} +1 -1
  349. package/dist/subagents-loader-Z744TAKD.js +7 -0
  350. package/dist/{subagents-loader-PEV3PD4Q.js.map → subagents-loader-Z744TAKD.js.map} +1 -1
  351. package/dist/subagents-loader.cjs +7 -7
  352. package/dist/subagents-loader.d.cts +4 -3
  353. package/dist/subagents-loader.d.ts +4 -3
  354. package/dist/subagents-loader.js +5 -5
  355. package/dist/subscription/index.cjs +3 -3
  356. package/dist/subscription/index.js +2 -2
  357. package/dist/task-store.cjs +7 -7
  358. package/dist/task-store.js +4 -4
  359. package/dist/types/agent-prims.d.ts +16 -0
  360. package/dist/types/agent.d.ts +15 -6
  361. package/dist/types/run.d.ts +14 -14
  362. package/dist/workflow.cjs +27 -27
  363. package/dist/workflow.d.cts +2 -2
  364. package/dist/workflow.d.ts +2 -2
  365. package/dist/workflow.js +6 -6
  366. package/docs/error-codes.md +24 -23
  367. package/docs/harness-capability-map.md +4 -4
  368. package/package.json +1 -1
  369. package/dist/agent-DFV52V6E.js +0 -62
  370. package/dist/agent-OKP4LHG2.cjs +0 -71
  371. package/dist/agent-session-store-ODD3IZCW.js +0 -6
  372. package/dist/agent-session-store-SVEJHSUA.cjs +0 -23
  373. package/dist/chunk-35KBC4BX.cjs.map +0 -1
  374. package/dist/chunk-6LHQPOMI.cjs.map +0 -1
  375. package/dist/chunk-B5MHRQJZ.js.map +0 -1
  376. package/dist/chunk-BBJ77S62.js.map +0 -1
  377. package/dist/chunk-CZJ6Q7CW.js.map +0 -1
  378. package/dist/chunk-JLIRFWUG.js.map +0 -1
  379. package/dist/chunk-MHIBYKWA.js.map +0 -1
  380. package/dist/chunk-N7KS3TIR.cjs.map +0 -1
  381. package/dist/chunk-P5N3Q2NH.cjs.map +0 -1
  382. package/dist/chunk-SGC7LSHF.cjs +0 -18
  383. package/dist/chunk-UNFBHV46.cjs.map +0 -1
  384. package/dist/compact-session-4HAUP3HK.js +0 -25
  385. package/dist/context-3FZRAO5K.js +0 -7
  386. package/dist/context-B5J4GX2F.cjs +0 -24
  387. package/dist/fs-session-store-3VI4CPTX.js +0 -11
  388. package/dist/fs-session-store-IQ6PEHYF.cjs +0 -20
  389. package/dist/generate-object-NTKAWE7D.js +0 -7
  390. package/dist/generate-object-YHTWG5DB.cjs +0 -20
  391. package/dist/index-manager-JVYIVTZG.js +0 -18
  392. package/dist/index-manager-YGXRFJMZ.cjs +0 -27
  393. package/dist/judge-call-AQQYK6VT.cjs +0 -22
  394. package/dist/judge-call-LOL5FQEX.js +0 -5
  395. package/dist/oauth-transaction-store-NP2YHZ6D.js +0 -5
  396. package/dist/oauth-transaction-store-ZINDV7AF.cjs +0 -38
  397. package/dist/registry-5FK42CRV.js +0 -9
  398. package/dist/registry-BVTNO73E.cjs +0 -46
  399. package/dist/run-until-6D6TQ4JN.js.map +0 -1
  400. package/dist/run-until-DG2KXWB2.cjs.map +0 -1
  401. package/dist/sdk-agent-dNwjwCt5.d.cts +0 -940
  402. package/dist/sdk-agent-wEOLXD3F.d.ts +0 -940
  403. package/dist/session-transcript-AR7IBICT.js +0 -5
  404. package/dist/session-transcript-RACG7BK2.cjs +0 -62
  405. package/dist/stream-object-QAEHG4YO.js +0 -7
  406. package/dist/stream-object-TKVGGA54.cjs +0 -20
  407. package/dist/subagents-loader-5TZCAIYQ.cjs +0 -16
  408. package/dist/subagents-loader-PEV3PD4Q.js +0 -7
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/auth/oauth-engine.ts","../src/internal/auth/resolve-credential.ts"],"names":["fresh"],"mappings":"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","file":"chunk-TX56N7FR.js","sourcesContent":["/**\n * M42 — the SDK OAuth engine. Promoted DOWN from agent-builder's hardened `agents/lib/oauth.ts` +\n * `oauth-device.ts` (M37), generalized to `provider: string` + a caller-supplied {@link CredentialStoreConfig}\n * (no hardcoded client IDs — the caller declares its provider's endpoints). Ported VERBATIM (ADR D3): the\n * refresh coalescing lock, the no-token-in-error discipline, and the RFC 8628 device grant.\n *\n * Reuses the SDK's typed error taxonomy (`AuthCallbackError` from `server/auth`) for exchange/refresh\n * failures. All network I/O + clock + sleep are INJECTED so the flow is deterministic.\n *\n * The device flows follow their published specs: the RFC 8628 device grant (device_code →\n * authorization_pending / slow_down) and OpenAI's two-step headless flow with JWT-claim account attribution.\n *\n * @internal\n */\nimport { AuthCallbackError } from \"../../server/auth/errors.js\";\nimport { authFilePath, readStoredOAuth, writeCredential } from \"./credential-store.js\";\nimport type {\n CredentialStoreConfig,\n HttpDeps,\n OAuthProviderConfig,\n OAuthTokens,\n ResolvedCredential,\n} from \"./types.js\";\n\n/** Refresh a token this many ms BEFORE it actually expires (avoid a last-second, mid-request expiry). */\nconst REFRESH_SKEW_MS = 60_000;\n\n/** Parse an OAuth token endpoint response into our token triple. Throws a typed error on a bad shape. */\nfunction parseTokenResponse(body: unknown, now: number): OAuthTokens {\n const b = body as {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n account_id?: unknown;\n };\n if (typeof b.access_token !== \"string\" || b.access_token.length === 0) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"token response had no access_token\",\n );\n }\n if (typeof b.refresh_token !== \"string\" || b.refresh_token.length === 0) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"token response had no refresh_token\",\n );\n }\n const expiresIn = typeof b.expires_in === \"number\" ? b.expires_in : 3600;\n return {\n access: b.access_token,\n refresh: b.refresh_token,\n expires: now + expiresIn * 1000,\n ...(typeof b.account_id === \"string\" ? { accountId: b.account_id } : {}),\n };\n}\n\n/** POST a form-encoded grant to the token endpoint; return the parsed JSON or throw a typed error. */\nasync function postGrant(\n config: OAuthProviderConfig,\n form: Record<string, string>,\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n let res: Response;\n try {\n res = await deps.fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\", accept: \"application/json\" },\n body: new URLSearchParams(form).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `token endpoint request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n // The body may carry an OAuth `error` code, but never echo it verbatim — it can contain a token in\n // some providers' error payloads. Report the status only.\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `token endpoint returned HTTP ${res.status}`,\n );\n }\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n // Do NOT echo the parser message — on a 200 non-JSON body it embeds a snippet of the response, which\n // in a token context can contain access/refresh material.\n throw new AuthCallbackError(\"oauth_token_exchange_failed\", \"token response was not valid JSON\");\n }\n return parseTokenResponse(json, deps.now());\n}\n\n/** Exchange an authorization `code` (+ PKCE verifier) for the token pair. */\nexport function exchangeCode(\n config: OAuthProviderConfig,\n input: { code: string; verifier: string },\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n return postGrant(\n config,\n {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: input.verifier,\n },\n deps,\n );\n}\n\n/** Swap a refresh token for a fresh access (and possibly rotated refresh) token. */\nexport function refreshOAuthTokens(\n config: OAuthProviderConfig,\n refresh: string,\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n return postGrant(\n config,\n { grant_type: \"refresh_token\", refresh_token: refresh, client_id: config.clientId },\n deps,\n );\n}\n\n/** Persist a token triple to the store's oauth variant through the hardened 0600 writer. */\nexport function persistOAuthTokens(\n provider: string,\n tokens: OAuthTokens,\n store: CredentialStoreConfig,\n env: Record<string, string | undefined> = {},\n): string {\n return writeCredential(\n {\n type: \"oauth\",\n provider,\n access: tokens.access,\n refresh: tokens.refresh,\n expires: tokens.expires,\n ...(tokens.accountId !== undefined ? { account_id: tokens.accountId } : {}),\n },\n store,\n env,\n );\n}\n\n/**\n * In-flight refreshes, keyed by the auth-file path. Refresh tokens ROTATE (single-use) on many providers,\n * so two concurrent turns must NOT both POST the same refresh token — the second would present an\n * already-consumed token and fail. This coalesces concurrent refreshes of the same store onto one promise.\n * Process-local — the single-credential store is per-user; a cross-process lock would be over-engineering.\n */\nconst inFlightRefresh = new Map<string, Promise<OAuthTokens>>();\n\n/**\n * Return a credential guaranteed fresh enough to use. An `api` credential passes through untouched. An\n * `oauth` credential still valid (beyond the skew window) passes through; an expired one is refreshed,\n * re-persisted 0600, and returned with the new access token. Concurrent refreshes of the same store are\n * coalesced (single-use refresh tokens); the rejected promise is evicted so a failure is not cached-poison.\n */\nexport async function ensureFreshCredential(\n resolved: ResolvedCredential,\n opts: {\n config: OAuthProviderConfig;\n store: CredentialStoreConfig;\n env?: Record<string, string | undefined>;\n },\n deps: HttpDeps,\n): Promise<ResolvedCredential> {\n if (resolved.kind !== \"oauth\") return resolved;\n\n const now = deps.now();\n if (resolved.expiresAt !== undefined && resolved.expiresAt > now + REFRESH_SKEW_MS) {\n return resolved; // still valid — no network, no rewrite\n }\n\n const env = opts.env ?? {};\n const path = authFilePath(opts.store, env);\n let refresh = inFlightRefresh.get(path);\n if (refresh === undefined) {\n refresh = (async (): Promise<OAuthTokens> => {\n const stored = readStoredOAuth(opts.store, env);\n if (stored === undefined) {\n // The resolved credential said oauth but the store no longer holds it — do not fabricate; surface it.\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"no stored oauth credential to refresh\",\n );\n }\n const fresh = await refreshOAuthTokens(opts.config, stored.refresh, deps);\n // M43 D4 fix #1 — the OAuth refresh response returns JWTs, not a top-level `account_id`, so\n // `parseTokenResponse` leaves `fresh.accountId` undefined. Prefer a freshly-derived id, else PRESERVE\n // the stored one (account_id is stable) — otherwise the Codex `ChatGPT-Account-Id` header empties after\n // the first refresh and the backend 401/403s.\n const merged: OAuthTokens = {\n ...fresh,\n accountId: fresh.accountId ?? stored.account_id,\n };\n persistOAuthTokens(resolved.provider, merged, opts.store, env);\n return merged;\n })();\n inFlightRefresh.set(path, refresh);\n refresh.finally(() => inFlightRefresh.delete(path)).catch(() => {});\n }\n\n const fresh = await refresh;\n return {\n kind: \"oauth\",\n provider: resolved.provider,\n apiKey: fresh.access,\n source: resolved.source,\n inferred: false,\n expiresAt: fresh.expires,\n };\n}\n","/**\n * M42 — `resolveCredential`: the public composition entrypoint over the credential store + oauth engine.\n * Given a provider name + a {@link CredentialStoreConfig} (and, for oauth, an {@link OAuthProviderConfig}),\n * read the store and return a FRESH `ResolvedCredential` — an api credential passes through by identity; an\n * oauth credential is transparently refreshed when its access token is within the skew window. Returns\n * `undefined` when the store holds no credential for `provider` (the caller then falls back to env / apiKeys).\n *\n * Mirrors agent-builder's `resolveFreshCredential` composition (store read → oauth → ensureFreshCredential),\n * defaulting `fetch`/`now` to globals. This is the async surface an oauth provider's M41 `transform.fetch`\n * calls at stream time, so a mid-turn token expiry refreshes without rebuilding the agent.\n *\n * Re-exported from `@theokit/sdk/auth`, and therefore PUBLIC, under semver.\n *\n * NOTE — no internal-visibility tag in this block. `tsconfig.base.json` sets `stripInternal: true`,\n * and TypeScript scans EVERY leading comment range of the declaration that follows, including the\n * import right below this one. The tag that used to sit here deleted that import from the emitted\n * `.d.ts`, leaving the types it binds unresolvable for any consumer running type-aware lint\n * (usetheodev/theokit-sdk#283 records the same trap on a declaration).\n */\n\nimport { boundFetch } from \"../runtime-fetch.js\";\nimport { authFilePath, readAuthFile } from \"./credential-store.js\";\nimport { ensureFreshCredential } from \"./oauth-engine.js\";\nimport type {\n CredentialStoreConfig,\n HttpDeps,\n OAuthProviderConfig,\n ResolvedCredential,\n} from \"./types.js\";\n\n/**\n * Input to `resolveCredential`: which provider, which store, and what to inject.\n *\n * `env` and `deps` exist so nothing here reads ambient state. Omitting `env` does NOT fall back to\n * `process.env` — the store's `homeEnvVar` override simply goes unread, so a credential written\n * under a relocated home resolves as absent rather than as an error.\n *\n * `oauth` is required only to REFRESH an oauth credential. Without it an expired oauth entry cannot\n * be renewed, and an api-key-only store needs it never.\n *\n * PUBLIC and under semver, despite the `internal/` directory: `src/auth/index.ts` re-exports this\n * type, and `package.json` publishes that barrel as the '@theokit/sdk/auth' subpath. The directory\n * name is not the authority — the `exports` map is. The semver-exempt class is the sub-paths\n * declared under '@theokit/sdk/internal/*' ('internal/persistence', 'internal/security',\n * 'internal/memory-adapters'); no `exports` entry reaches this file that way.\n */\nexport interface ResolveCredentialOptions {\n /** The provider whose stored credential to resolve. */\n provider: string;\n /** Where the credential store lives. */\n store: CredentialStoreConfig;\n /** Env for the store's optional `homeEnvVar` override. Never an ambient read. */\n env?: Record<string, string | undefined>;\n /** Required to refresh an oauth credential; omit for an api-only store. */\n oauth?: OAuthProviderConfig;\n /** Injected HTTP + clock for deterministic tests; default globals. */\n deps?: { fetch?: typeof fetch; now?: () => number };\n}\n\n/** Resolve (and, when a config is supplied, refresh) the oauth variant for the requested provider. */\nasync function resolveOAuth(\n stored: { provider: string; access: string; expires: number },\n path: string,\n opts: ResolveCredentialOptions,\n env: Record<string, string | undefined>,\n): Promise<ResolvedCredential | undefined> {\n if (stored.provider !== opts.provider) return undefined;\n const base: ResolvedCredential = {\n kind: \"oauth\",\n provider: opts.provider,\n apiKey: stored.access,\n source: path,\n inferred: false,\n expiresAt: stored.expires,\n };\n if (opts.oauth === undefined) return base; // no config to refresh with — return the stored access token\n const deps: HttpDeps = {\n fetch: boundFetch(opts.deps?.fetch),\n now: opts.deps?.now ?? (() => Date.now()),\n };\n return ensureFreshCredential(base, { config: opts.oauth, store: opts.store, env }, deps);\n}\n\n/**\n * Resolve a fresh credential for `provider` from the store, or `undefined` if the store holds none for it.\n * oauth credentials are auto-refreshed when `opts.oauth` is supplied; api credentials pass through.\n */\nexport async function resolveCredential(\n opts: ResolveCredentialOptions,\n): Promise<ResolvedCredential | undefined> {\n const env = opts.env ?? {};\n const stored = readAuthFile(opts.store, env);\n if (stored === undefined) return undefined;\n\n const path = authFilePath(opts.store, env);\n\n if (stored.type === \"oauth\") {\n return resolveOAuth(stored, path, opts, env);\n }\n\n // api variant: require the stored `provider` to MATCH the requested provider (M42 review MEDIUM-2).\n // The generic SDK cannot infer a provider from a key prefix (that was agent-builder app policy), so a\n // provider-less or mismatched stored key is NOT attributed to the requested provider — otherwise a legacy\n // `{api_key:\"sk-ant-…\"}` file could be POSTed to the wrong vendor's endpoint (cross-vendor key exposure).\n // Fail-closed: credential resolution never attributes a key across providers.\n if (stored.api_key.length === 0) return undefined;\n if (stored.provider !== opts.provider) return undefined;\n return {\n kind: \"api\",\n provider: opts.provider,\n apiKey: stored.api_key,\n source: path,\n inferred: false,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/auth/oauth-engine.ts","../src/internal/auth/resolve-credential.ts"],"names":["fresh"],"mappings":"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","file":"chunk-3FSBE4PS.js","sourcesContent":["/**\n * M42 — the SDK OAuth engine. Promoted DOWN from agent-builder's hardened `agents/lib/oauth.ts` +\n * `oauth-device.ts` (M37), generalized to `provider: string` + a caller-supplied {@link CredentialStoreConfig}\n * (no hardcoded client IDs — the caller declares its provider's endpoints). Ported VERBATIM (ADR D3): the\n * refresh coalescing lock, the no-token-in-error discipline, and the RFC 8628 device grant.\n *\n * Reuses the SDK's typed error taxonomy (`AuthCallbackError` from `server/auth`) for exchange/refresh\n * failures. All network I/O + clock + sleep are INJECTED so the flow is deterministic.\n *\n * The device flows follow their published specs: the RFC 8628 device grant (device_code →\n * authorization_pending / slow_down) and OpenAI's two-step headless flow with JWT-claim account attribution.\n *\n * @internal\n */\nimport { AuthCallbackError } from \"../../server/auth/errors.js\";\nimport { authFilePath, readStoredOAuth, writeCredential } from \"./credential-store.js\";\nimport type {\n CredentialStoreConfig,\n HttpDeps,\n OAuthProviderConfig,\n OAuthTokens,\n ResolvedCredential,\n} from \"./types.js\";\n\n/** Refresh a token this many ms BEFORE it actually expires (avoid a last-second, mid-request expiry). */\nconst REFRESH_SKEW_MS = 60_000;\n\n/** Parse an OAuth token endpoint response into our token triple. Throws a typed error on a bad shape. */\nfunction parseTokenResponse(body: unknown, now: number): OAuthTokens {\n const b = body as {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n account_id?: unknown;\n };\n if (typeof b.access_token !== \"string\" || b.access_token.length === 0) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"token response had no access_token\",\n );\n }\n if (typeof b.refresh_token !== \"string\" || b.refresh_token.length === 0) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"token response had no refresh_token\",\n );\n }\n const expiresIn = typeof b.expires_in === \"number\" ? b.expires_in : 3600;\n return {\n access: b.access_token,\n refresh: b.refresh_token,\n expires: now + expiresIn * 1000,\n ...(typeof b.account_id === \"string\" ? { accountId: b.account_id } : {}),\n };\n}\n\n/** POST a form-encoded grant to the token endpoint; return the parsed JSON or throw a typed error. */\nasync function postGrant(\n config: OAuthProviderConfig,\n form: Record<string, string>,\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n let res: Response;\n try {\n res = await deps.fetch(config.tokenEndpoint, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\", accept: \"application/json\" },\n body: new URLSearchParams(form).toString(),\n });\n } catch (err) {\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `token endpoint request failed: ${(err as Error).message}`,\n );\n }\n if (!res.ok) {\n // The body may carry an OAuth `error` code, but never echo it verbatim — it can contain a token in\n // some providers' error payloads. Report the status only.\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n `token endpoint returned HTTP ${res.status}`,\n );\n }\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n // Do NOT echo the parser message — on a 200 non-JSON body it embeds a snippet of the response, which\n // in a token context can contain access/refresh material.\n throw new AuthCallbackError(\"oauth_token_exchange_failed\", \"token response was not valid JSON\");\n }\n return parseTokenResponse(json, deps.now());\n}\n\n/** Exchange an authorization `code` (+ PKCE verifier) for the token pair. */\nexport function exchangeCode(\n config: OAuthProviderConfig,\n input: { code: string; verifier: string },\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n return postGrant(\n config,\n {\n grant_type: \"authorization_code\",\n code: input.code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: input.verifier,\n },\n deps,\n );\n}\n\n/** Swap a refresh token for a fresh access (and possibly rotated refresh) token. */\nexport function refreshOAuthTokens(\n config: OAuthProviderConfig,\n refresh: string,\n deps: HttpDeps,\n): Promise<OAuthTokens> {\n return postGrant(\n config,\n { grant_type: \"refresh_token\", refresh_token: refresh, client_id: config.clientId },\n deps,\n );\n}\n\n/** Persist a token triple to the store's oauth variant through the hardened 0600 writer. */\nexport function persistOAuthTokens(\n provider: string,\n tokens: OAuthTokens,\n store: CredentialStoreConfig,\n env: Record<string, string | undefined> = {},\n): string {\n return writeCredential(\n {\n type: \"oauth\",\n provider,\n access: tokens.access,\n refresh: tokens.refresh,\n expires: tokens.expires,\n ...(tokens.accountId !== undefined ? { account_id: tokens.accountId } : {}),\n },\n store,\n env,\n );\n}\n\n/**\n * In-flight refreshes, keyed by the auth-file path. Refresh tokens ROTATE (single-use) on many providers,\n * so two concurrent turns must NOT both POST the same refresh token — the second would present an\n * already-consumed token and fail. This coalesces concurrent refreshes of the same store onto one promise.\n * Process-local — the single-credential store is per-user; a cross-process lock would be over-engineering.\n */\nconst inFlightRefresh = new Map<string, Promise<OAuthTokens>>();\n\n/**\n * Return a credential guaranteed fresh enough to use. An `api` credential passes through untouched. An\n * `oauth` credential still valid (beyond the skew window) passes through; an expired one is refreshed,\n * re-persisted 0600, and returned with the new access token. Concurrent refreshes of the same store are\n * coalesced (single-use refresh tokens); the rejected promise is evicted so a failure is not cached-poison.\n */\nexport async function ensureFreshCredential(\n resolved: ResolvedCredential,\n opts: {\n config: OAuthProviderConfig;\n store: CredentialStoreConfig;\n env?: Record<string, string | undefined>;\n },\n deps: HttpDeps,\n): Promise<ResolvedCredential> {\n if (resolved.kind !== \"oauth\") return resolved;\n\n const now = deps.now();\n if (resolved.expiresAt !== undefined && resolved.expiresAt > now + REFRESH_SKEW_MS) {\n return resolved; // still valid — no network, no rewrite\n }\n\n const env = opts.env ?? {};\n const path = authFilePath(opts.store, env);\n let refresh = inFlightRefresh.get(path);\n if (refresh === undefined) {\n refresh = (async (): Promise<OAuthTokens> => {\n const stored = readStoredOAuth(opts.store, env);\n if (stored === undefined) {\n // The resolved credential said oauth but the store no longer holds it — do not fabricate; surface it.\n throw new AuthCallbackError(\n \"oauth_token_exchange_failed\",\n \"no stored oauth credential to refresh\",\n );\n }\n const fresh = await refreshOAuthTokens(opts.config, stored.refresh, deps);\n // M43 D4 fix #1 — the OAuth refresh response returns JWTs, not a top-level `account_id`, so\n // `parseTokenResponse` leaves `fresh.accountId` undefined. Prefer a freshly-derived id, else PRESERVE\n // the stored one (account_id is stable) — otherwise the Codex `ChatGPT-Account-Id` header empties after\n // the first refresh and the backend 401/403s.\n const merged: OAuthTokens = {\n ...fresh,\n accountId: fresh.accountId ?? stored.account_id,\n };\n persistOAuthTokens(resolved.provider, merged, opts.store, env);\n return merged;\n })();\n inFlightRefresh.set(path, refresh);\n refresh.finally(() => inFlightRefresh.delete(path)).catch(() => {});\n }\n\n const fresh = await refresh;\n return {\n kind: \"oauth\",\n provider: resolved.provider,\n apiKey: fresh.access,\n source: resolved.source,\n inferred: false,\n expiresAt: fresh.expires,\n };\n}\n","/**\n * M42 — `resolveCredential`: the public composition entrypoint over the credential store + oauth engine.\n * Given a provider name + a {@link CredentialStoreConfig} (and, for oauth, an {@link OAuthProviderConfig}),\n * read the store and return a FRESH `ResolvedCredential` — an api credential passes through by identity; an\n * oauth credential is transparently refreshed when its access token is within the skew window. Returns\n * `undefined` when the store holds no credential for `provider` (the caller then falls back to env / apiKeys).\n *\n * Mirrors agent-builder's `resolveFreshCredential` composition (store read → oauth → ensureFreshCredential),\n * defaulting `fetch`/`now` to globals. This is the async surface an oauth provider's M41 `transform.fetch`\n * calls at stream time, so a mid-turn token expiry refreshes without rebuilding the agent.\n *\n * Re-exported from `@theokit/sdk/auth`, and therefore PUBLIC, under semver.\n *\n * NOTE — no internal-visibility tag in this block. `tsconfig.base.json` sets `stripInternal: true`,\n * and TypeScript scans EVERY leading comment range of the declaration that follows, including the\n * import right below this one. The tag that used to sit here deleted that import from the emitted\n * `.d.ts`, leaving the types it binds unresolvable for any consumer running type-aware lint\n * (usetheodev/theokit-sdk#283 records the same trap on a declaration).\n */\n\nimport { boundFetch } from \"../runtime-fetch.js\";\nimport { authFilePath, readAuthFile } from \"./credential-store.js\";\nimport { ensureFreshCredential } from \"./oauth-engine.js\";\nimport type {\n CredentialStoreConfig,\n HttpDeps,\n OAuthProviderConfig,\n ResolvedCredential,\n} from \"./types.js\";\n\n/**\n * Input to `resolveCredential`: which provider, which store, and what to inject.\n *\n * `env` and `deps` exist so nothing here reads ambient state. Omitting `env` does NOT fall back to\n * `process.env` — the store's `homeEnvVar` override simply goes unread, so a credential written\n * under a relocated home resolves as absent rather than as an error.\n *\n * `oauth` is required only to REFRESH an oauth credential. Without it an expired oauth entry cannot\n * be renewed, and an api-key-only store needs it never.\n *\n * PUBLIC and under semver, despite the `internal/` directory: `src/auth/index.ts` re-exports this\n * type, and `package.json` publishes that barrel as the '@theokit/sdk/auth' subpath. The directory\n * name is not the authority — the `exports` map is. The semver-exempt class is the sub-paths\n * declared under '@theokit/sdk/internal/*' ('internal/persistence', 'internal/security',\n * 'internal/memory-adapters'); no `exports` entry reaches this file that way.\n */\nexport interface ResolveCredentialOptions {\n /** The provider whose stored credential to resolve. */\n provider: string;\n /** Where the credential store lives. */\n store: CredentialStoreConfig;\n /** Env for the store's optional `homeEnvVar` override. Never an ambient read. */\n env?: Record<string, string | undefined>;\n /** Required to refresh an oauth credential; omit for an api-only store. */\n oauth?: OAuthProviderConfig;\n /** Injected HTTP + clock for deterministic tests; default globals. */\n deps?: { fetch?: typeof fetch; now?: () => number };\n}\n\n/** Resolve (and, when a config is supplied, refresh) the oauth variant for the requested provider. */\nasync function resolveOAuth(\n stored: { provider: string; access: string; expires: number },\n path: string,\n opts: ResolveCredentialOptions,\n env: Record<string, string | undefined>,\n): Promise<ResolvedCredential | undefined> {\n if (stored.provider !== opts.provider) return undefined;\n const base: ResolvedCredential = {\n kind: \"oauth\",\n provider: opts.provider,\n apiKey: stored.access,\n source: path,\n inferred: false,\n expiresAt: stored.expires,\n };\n if (opts.oauth === undefined) return base; // no config to refresh with — return the stored access token\n const deps: HttpDeps = {\n fetch: boundFetch(opts.deps?.fetch),\n now: opts.deps?.now ?? (() => Date.now()),\n };\n return ensureFreshCredential(base, { config: opts.oauth, store: opts.store, env }, deps);\n}\n\n/**\n * Resolve a fresh credential for `provider` from the store, or `undefined` if the store holds none for it.\n * oauth credentials are auto-refreshed when `opts.oauth` is supplied; api credentials pass through.\n */\nexport async function resolveCredential(\n opts: ResolveCredentialOptions,\n): Promise<ResolvedCredential | undefined> {\n const env = opts.env ?? {};\n const stored = readAuthFile(opts.store, env);\n if (stored === undefined) return undefined;\n\n const path = authFilePath(opts.store, env);\n\n if (stored.type === \"oauth\") {\n return resolveOAuth(stored, path, opts, env);\n }\n\n // api variant: require the stored `provider` to MATCH the requested provider (M42 review MEDIUM-2).\n // The generic SDK cannot infer a provider from a key prefix (that was agent-builder app policy), so a\n // provider-less or mismatched stored key is NOT attributed to the requested provider — otherwise a legacy\n // `{api_key:\"sk-ant-…\"}` file could be POSTed to the wrong vendor's endpoint (cross-vendor key exposure).\n // Fail-closed: credential resolution never attributes a key across providers.\n if (stored.api_key.length === 0) return undefined;\n if (stored.provider !== opts.provider) return undefined;\n return {\n kind: \"api\",\n provider: opts.provider,\n apiKey: stored.api_key,\n source: path,\n inferred: false,\n };\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkJLRLCBJ4_cjs = require('./chunk-JLRLCBJ4.cjs');
4
- var chunk6LHQPOMI_cjs = require('./chunk-6LHQPOMI.cjs');
3
+ var chunkZOFRNNZD_cjs = require('./chunk-ZOFRNNZD.cjs');
4
+ var chunkRNXXYF22_cjs = require('./chunk-RNXXYF22.cjs');
5
5
  var promises = require('fs/promises');
6
6
 
7
7
  function migrateSchema(opts) {
@@ -44,10 +44,10 @@ async function readVersionedJson(opts) {
44
44
  const asidePath = `${path}.corrupt.${Date.now()}`;
45
45
  try {
46
46
  await promises.rename(path, asidePath);
47
- chunk6LHQPOMI_cjs.diag(`[theokit-sdk] ${path} is corrupt; moved to ${asidePath}. Using default value.
47
+ chunkRNXXYF22_cjs.diag(`[theokit-sdk] ${path} is corrupt; moved to ${asidePath}. Using default value.
48
48
  `);
49
49
  } catch {
50
- chunk6LHQPOMI_cjs.diag(`[theokit-sdk] ${path} is corrupt; using default value.
50
+ chunkRNXXYF22_cjs.diag(`[theokit-sdk] ${path} is corrupt; using default value.
51
51
  `);
52
52
  }
53
53
  return defaultValue();
@@ -62,7 +62,7 @@ async function readVersionedJson(opts) {
62
62
  return file.data;
63
63
  }
64
64
  if (stored > currentVersion) {
65
- chunk6LHQPOMI_cjs.diag(
65
+ chunkRNXXYF22_cjs.diag(
66
66
  `[theokit-sdk] ${path} schema version ${stored} > current ${currentVersion}; using default value (forward-only).
67
67
  `
68
68
  );
@@ -75,11 +75,11 @@ async function writeVersionedJson(path, data, currentVersion) {
75
75
  _schemaVersion: currentVersion,
76
76
  data
77
77
  };
78
- await chunkJLRLCBJ4_cjs.atomicWriteJson(path, file);
78
+ await chunkZOFRNNZD_cjs.atomicWriteJson(path, file);
79
79
  }
80
80
 
81
81
  exports.migrateSchema = migrateSchema;
82
82
  exports.readVersionedJson = readVersionedJson;
83
83
  exports.writeVersionedJson = writeVersionedJson;
84
- //# sourceMappingURL=chunk-RQMCOFAI.cjs.map
85
- //# sourceMappingURL=chunk-RQMCOFAI.cjs.map
84
+ //# sourceMappingURL=chunk-3HA7N2BB.cjs.map
85
+ //# sourceMappingURL=chunk-3HA7N2BB.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/persistence/schema-version.ts"],"names":["readFile","rename","diag","atomicWriteJson"],"mappings":"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","file":"chunk-RQMCOFAI.cjs","sourcesContent":["/**\n * Schema versioning helpers (ADR D62).\n *\n * Two parallel APIs:\n * - `migrateSchema` — SQLite via `PRAGMA user_version` + ordered forward-only migrations.\n * - `readVersionedJson` / `writeVersionedJson` — JSON files with `_schemaVersion` field.\n *\n * Forward-only: never deletes data, never downgrades. Migration callbacks\n * own the transformation; this module owns the bookkeeping.\n *\n * @internal\n */\n\nimport { readFile, rename } from \"node:fs/promises\";\nimport { diag } from \"../diagnostics.js\";\nimport { atomicWriteJson } from \"./atomic-write.js\";\n\n// ────────────────────── SQLite migrations ──────────────────────\n\n/**\n * The three `better-sqlite3` methods this module actually uses, declared structurally so nothing\n * here imports the driver.\n *\n * A real `better-sqlite3` `Database` satisfies it. `pragma` is called both to read\n * (`pragma(\"user_version\", { simple: true })`, which must return the number itself rather than a\n * row array) and to write (`pragma(\"user_version = N\")`). `transaction` must return a function\n * that, when called, runs the wrapped body inside a real SQLite transaction and rolls it back if\n * the body throws — a stub that merely calls through gives `migrateSchema` no atomicity, and a\n * half-applied migration is exactly what it exists to prevent.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface SqliteLike {\n pragma: (statement: string, options?: { simple?: boolean }) => unknown;\n exec: (sql: string) => void;\n transaction: <Args extends unknown[], R>(fn: (...args: Args) => R) => (...args: Args) => R;\n}\n\n/**\n * One forward migration step.\n *\n * `up` runs inside the shared transaction and must be self-contained: it may not commit, and any\n * error it throws aborts the whole run and rolls back every step in it, including the ones that\n * already succeeded. `user_version` is set to `toVersion` immediately after `up` returns, inside\n * the same transaction, so version and schema move together or not at all.\n *\n * `toVersion` is the version the database reaches, not the one it starts from. Steps are ordered\n * by it, gaps are allowed, and two steps sharing a `toVersion` both run — nothing deduplicates\n * them.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface Migration {\n toVersion: number;\n up: (db: SqliteLike) => void;\n}\n\n/**\n * Arguments to `migrateSchema`.\n *\n * `currentVersion` is the version the code expects, and it is the ceiling: a migration whose\n * `toVersion` exceeds it is left unapplied for a later release. `migrations` may be given in any\n * order and may contain steps already applied — both are filtered and sorted here. `label` only\n * ever appears in the downgrade error message.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaOptions {\n db: SqliteLike;\n currentVersion: number;\n migrations: ReadonlyArray<Migration>;\n /** For log/error context (e.g., \"memory-index\", \"registry\"). */\n label?: string;\n}\n\n/**\n * What `migrateSchema` did.\n *\n * `from` is the `user_version` found on the database, `to` is the version it holds afterwards,\n * and `ran` is how many steps executed. When nothing was pending, `to === from` and `ran === 0`.\n *\n * `to` is the `toVersion` of the last step applied, NOT `currentVersion`. A database at version 3\n * against `currentVersion: 5` with no migration in that range comes back as\n * `{ from: 3, to: 3, ran: 0 }` — the gap is reported, not closed. Comparing `to` with\n * `currentVersion` is how a caller notices that the migration list is missing a step.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaResult {\n from: number;\n to: number;\n ran: number;\n}\n\n/**\n * Bring a SQLite database's `user_version` up to `currentVersion` by running the steps that sit\n * between the two.\n *\n * Steps are sorted ascending by `toVersion` and filtered to `> stored && <= currentVersion`, then\n * run in that order inside ONE transaction: each `up` is followed immediately by the matching\n * `user_version` write. Either every step in the run commits or none does — a step that throws\n * aborts the transaction, propagates the error, and leaves the database at the version it started\n * from. There is no partially-migrated state and no resume point.\n *\n * Forward-only. When the stored version is greater than `currentVersion` — an older build opening\n * a database a newer one already migrated — this throws immediately, before touching anything,\n * with a message naming both versions and `label`. Nothing downgrades, and no data is deleted on\n * any path here; what a step does is the step's own business.\n *\n * A stored version equal to `currentVersion` returns without opening a transaction. A `user_version`\n * that is absent or not a number is read as 0, so a fresh database runs every step.\n *\n * Concurrency: the SQLite transaction is the only serialization. This takes no file lock, so two\n * processes migrating the same file at once are arbitrated by SQLite's own write lock — the loser\n * gets a busy or locked error from the driver, not a queued turn. Wrap the call in `withFileLock`\n * if that matters.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function migrateSchema(opts: MigrateSchemaOptions): MigrateSchemaResult {\n const { db, currentVersion, migrations, label = \"db\" } = opts;\n const storedRaw = db.pragma(\"user_version\", { simple: true });\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored > currentVersion) {\n throw new Error(\n `[${label}] schema version ${stored} > current ${currentVersion}; ` +\n \"did you downgrade the SDK? Forward-only migrations only.\",\n );\n }\n\n if (stored === currentVersion) {\n return { from: stored, to: stored, ran: 0 };\n }\n\n const pending = [...migrations]\n .sort((a, b) => a.toVersion - b.toVersion)\n .filter((m) => m.toVersion > stored && m.toVersion <= currentVersion);\n\n let ran = 0;\n let lastApplied = stored;\n\n db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.pragma(`user_version = ${m.toVersion}`);\n lastApplied = m.toVersion;\n ran += 1;\n }\n })();\n\n return { from: stored, to: lastApplied, ran };\n}\n\n// ────────────────────── JSON versioned files ──────────────────────\n\n/**\n * The on-disk envelope: the payload under `data`, its schema version alongside it.\n *\n * `writeVersionedJson` always produces this shape. `readVersionedJson` reads it, but tolerates a\n * file that is not in it — a legacy object with no `_schemaVersion` is treated as version 0 and\n * handed to the migrate callback whole, so this type describes what is written, not everything\n * that can be read.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface VersionedJsonFile<T> {\n _schemaVersion: number;\n data: T;\n}\n\n/**\n * Upgrade callback for `readVersionedJson`, invoked only when the stored version is BELOW the\n * current one.\n *\n * `parsed` is the whole parsed JSON value, not `parsed.data`. That is deliberate: a legacy file\n * predating the envelope has its fields at the top level, and a callback handed only `.data`\n * would receive `undefined` for exactly the files that need migrating. It is typed `unknown`\n * because nothing has validated it — narrow before reading.\n *\n * `fromVersion` is the `_schemaVersion` found on disk, or 0 when the field is missing or not a\n * number.\n *\n * The return value is used as-is and is NOT written back; the file on disk still holds the old\n * shape until someone calls `writeVersionedJson`. Throwing here propagates out of\n * `readVersionedJson`, which otherwise never throws — return the default value instead if an\n * unmigratable file should be survivable.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport type VersionedJsonMigrate<T> = (parsed: unknown, fromVersion: number) => T;\n\n/**\n * Arguments to `readVersionedJson`.\n *\n * `defaultValue` is a factory rather than a value because it is called on several paths and each\n * caller must get its own object — returning a shared mutable default would let one caller's\n * edits appear in another's. It is called for a missing file, an unreadable one, a corrupt one,\n * a non-object payload, and a file written by a newer version.\n *\n * `migrate` is called only for a stored version strictly below `currentVersion`.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface ReadVersionedJsonOptions<T> {\n path: string;\n currentVersion: number;\n migrate: VersionedJsonMigrate<T>;\n defaultValue: () => T;\n}\n\n/**\n * Read a versioned JSON file, migrating or falling back rather than failing.\n *\n * What comes back, by case:\n *\n * - version matches `currentVersion` — the file's `data` field, returned as `T` WITHOUT\n * validation. A file whose envelope is right but whose payload is not gets through.\n * - version below `currentVersion` — whatever `migrate(parsed, stored)` returns. The migrated\n * value is not persisted; the file is untouched.\n * - version above `currentVersion` — `defaultValue()`, plus a warning. Forward-only: a file\n * written by a newer build is left alone rather than downgraded.\n * - file missing, or unreadable for any other reason (permissions, a directory in its place) —\n * `defaultValue()`, silently. Absence and denial are not distinguished.\n * - file present but not valid JSON — `defaultValue()`, and the file is RENAMED to\n * `<path>.corrupt.<epoch-ms>` so the next run starts clean and the bad bytes stay available.\n * A rename that itself fails is warned about and the read still returns the default, which\n * means the same corrupt file will be met again next time.\n * - JSON that parses to a non-object (a number, a string, `null`) — `defaultValue()`, silently\n * and without moving the file aside.\n *\n * The only ways this throws are through the callbacks you supply: `migrate` or `defaultValue`\n * raising. Nothing else here rejects.\n *\n * There is no locking. A concurrent `writeVersionedJson` on the same path is atomic at the rename,\n * so a reader sees either the old file or the new one, never a half-written one — but a\n * read-modify-write built from this pair is NOT atomic across the two calls, and needs\n * `withFileLock` around both.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function readVersionedJson<T>(opts: ReadVersionedJsonOptions<T>): Promise<T> {\n const { path, currentVersion, migrate, defaultValue } = opts;\n\n let raw: string;\n try {\n raw = await readFile(path, \"utf-8\");\n } catch {\n return defaultValue();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // T5.10 — move corrupt file aside so (a) user can investigate and\n // (b) next run starts fresh instead of hitting the same warning loop.\n const asidePath = `${path}.corrupt.${Date.now()}`;\n try {\n await rename(path, asidePath);\n diag(`[theokit-sdk] ${path} is corrupt; moved to ${asidePath}. Using default value.\\n`);\n } catch {\n diag(`[theokit-sdk] ${path} is corrupt; using default value.\\n`);\n }\n return defaultValue();\n }\n\n if (typeof parsed !== \"object\" || parsed === null) {\n return defaultValue();\n }\n\n const file = parsed as { _schemaVersion?: unknown; data?: unknown };\n const storedRaw = file._schemaVersion;\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored === currentVersion) {\n return file.data as T;\n }\n\n if (stored > currentVersion) {\n diag(\n `[theokit-sdk] ${path} schema version ${stored} > current ${currentVersion}; ` +\n \"using default value (forward-only).\\n\",\n );\n return defaultValue();\n }\n\n // EC-2 fix: pass the FULL parsed object to migrate, not just `file.data`.\n // Legacy shapes (e.g., `{ schemaVersion: \"1.0\", agents: {...} }` without\n // a `data` field) need to inspect the whole thing.\n return migrate(parsed, stored);\n}\n\n/**\n * Write `data` wrapped in the version envelope, replacing the file atomically.\n *\n * The bytes go to a temp file in the same directory and are moved into place with `rename`, so a\n * crash or a concurrent reader never observes a partial file: the path holds either the previous\n * content or the complete new content. Parent directories are created as needed, and the file is\n * created with mode 0600 (subject to the process umask), formatted with two-space indentation and\n * a trailing newline.\n *\n * `currentVersion` is stamped verbatim as `_schemaVersion`; nothing checks it against what was\n * already on disk, so this will happily overwrite a newer file with an older version. Read first\n * if that matters.\n *\n * Rejects when `data` cannot be serialized — a circular reference, or a `toJSON` that throws.\n * Serialization happens before any filesystem call, so on that path no directory is created, no\n * temp file exists, and the file already on disk is untouched.\n *\n * Atomicity is the rename's, so it is as strong as the filesystem's: on NFS, SMB or FUSE the\n * underlying writer emits a one-shot warning that the guarantee is weaker there.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function writeVersionedJson<T>(\n path: string,\n data: T,\n currentVersion: number,\n): Promise<void> {\n const file: VersionedJsonFile<T> = {\n _schemaVersion: currentVersion,\n data,\n };\n await atomicWriteJson(path, file);\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/persistence/schema-version.ts"],"names":["readFile","rename","diag","atomicWriteJson"],"mappings":";;;;;;AA4HO,SAAS,cAAc,IAAA,EAAiD;AAC7E,EAAA,MAAM,EAAE,EAAA,EAAI,cAAA,EAAgB,UAAA,EAAY,KAAA,GAAQ,MAAK,GAAI,IAAA;AACzD,EAAA,MAAM,YAAY,EAAA,CAAG,MAAA,CAAO,gBAAgB,EAAE,MAAA,EAAQ,MAAM,CAAA;AAC5D,EAAA,MAAM,MAAA,GAAS,OAAO,SAAA,KAAc,QAAA,GAAW,SAAA,GAAY,CAAA;AAE3D,EAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,CAAA,EAAI,KAAK,CAAA,iBAAA,EAAoB,MAAM,cAAc,cAAc,CAAA,0DAAA;AAAA,KAEjE;AAAA,EACF;AAEA,EAAA,IAAI,WAAW,cAAA,EAAgB;AAC7B,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,EAAA,EAAI,MAAA,EAAQ,KAAK,CAAA,EAAE;AAAA,EAC5C;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,GAAG,UAAU,EAC3B,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,SAAA,GAAY,EAAE,SAAS,CAAA,CACxC,OAAO,CAAC,CAAA,KAAM,EAAE,SAAA,GAAY,MAAA,IAAU,CAAA,CAAE,SAAA,IAAa,cAAc,CAAA;AAEtE,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,WAAA,GAAc,MAAA;AAElB,EAAA,EAAA,CAAG,YAAY,MAAM;AACnB,IAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,MAAA,CAAA,CAAE,GAAG,EAAE,CAAA;AACP,MAAA,EAAA,CAAG,MAAA,CAAO,CAAA,eAAA,EAAkB,CAAA,CAAE,SAAS,CAAA,CAAE,CAAA;AACzC,MAAA,WAAA,GAAc,CAAA,CAAE,SAAA;AAChB,MAAA,GAAA,IAAO,CAAA;AAAA,IACT;AAAA,EACF,CAAC,CAAA,EAAE;AAEH,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,EAAA,EAAI,aAAa,GAAA,EAAI;AAC9C;AA6FA,eAAsB,kBAAqB,IAAA,EAA+C;AACxF,EAAA,MAAM,EAAE,IAAA,EAAM,cAAA,EAAgB,OAAA,EAAS,cAAa,GAAI,IAAA;AAExD,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,MAAMA,iBAAA,CAAS,IAAA,EAAM,OAAO,CAAA;AAAA,EACpC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,YAAA,EAAa;AAAA,EACtB;AAEA,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EACzB,CAAA,CAAA,MAAQ;AAGN,IAAA,MAAM,YAAY,CAAA,EAAG,IAAI,CAAA,SAAA,EAAY,IAAA,CAAK,KAAK,CAAA,CAAA;AAC/C,IAAA,IAAI;AACF,MAAA,MAAMC,eAAA,CAAO,MAAM,SAAS,CAAA;AAC5B,MAAAC,sBAAA,CAAK,CAAA,cAAA,EAAiB,IAAI,CAAA,sBAAA,EAAyB,SAAS,CAAA;AAAA,CAA0B,CAAA;AAAA,IACxF,CAAA,CAAA,MAAQ;AACN,MAAAA,sBAAA,CAAK,iBAAiB,IAAI,CAAA;AAAA,CAAqC,CAAA;AAAA,IACjE;AACA,IAAA,OAAO,YAAA,EAAa;AAAA,EACtB;AAEA,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,IAAA,EAAM;AACjD,IAAA,OAAO,YAAA,EAAa;AAAA,EACtB;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA;AACb,EAAA,MAAM,YAAY,IAAA,CAAK,cAAA;AACvB,EAAA,MAAM,MAAA,GAAS,OAAO,SAAA,KAAc,QAAA,GAAW,SAAA,GAAY,CAAA;AAE3D,EAAA,IAAI,WAAW,cAAA,EAAgB;AAC7B,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AAEA,EAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,IAAAA,sBAAA;AAAA,MACE,CAAA,cAAA,EAAiB,IAAI,CAAA,gBAAA,EAAmB,MAAM,cAAc,cAAc,CAAA;AAAA;AAAA,KAE5E;AACA,IAAA,OAAO,YAAA,EAAa;AAAA,EACtB;AAKA,EAAA,OAAO,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAC/B;AAyBA,eAAsB,kBAAA,CACpB,IAAA,EACA,IAAA,EACA,cAAA,EACe;AACf,EAAA,MAAM,IAAA,GAA6B;AAAA,IACjC,cAAA,EAAgB,cAAA;AAAA,IAChB;AAAA,GACF;AACA,EAAA,MAAMC,iCAAA,CAAgB,MAAM,IAAI,CAAA;AAClC","file":"chunk-3HA7N2BB.cjs","sourcesContent":["/**\n * Schema versioning helpers (ADR D62).\n *\n * Two parallel APIs:\n * - `migrateSchema` — SQLite via `PRAGMA user_version` + ordered forward-only migrations.\n * - `readVersionedJson` / `writeVersionedJson` — JSON files with `_schemaVersion` field.\n *\n * Forward-only: never deletes data, never downgrades. Migration callbacks\n * own the transformation; this module owns the bookkeeping.\n *\n * @internal\n */\n\nimport { readFile, rename } from \"node:fs/promises\";\nimport { diag } from \"../diagnostics.js\";\nimport { atomicWriteJson } from \"./atomic-write.js\";\n\n// ────────────────────── SQLite migrations ──────────────────────\n\n/**\n * The three `better-sqlite3` methods this module actually uses, declared structurally so nothing\n * here imports the driver.\n *\n * A real `better-sqlite3` `Database` satisfies it. `pragma` is called both to read\n * (`pragma(\"user_version\", { simple: true })`, which must return the number itself rather than a\n * row array) and to write (`pragma(\"user_version = N\")`). `transaction` must return a function\n * that, when called, runs the wrapped body inside a real SQLite transaction and rolls it back if\n * the body throws — a stub that merely calls through gives `migrateSchema` no atomicity, and a\n * half-applied migration is exactly what it exists to prevent.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface SqliteLike {\n pragma: (statement: string, options?: { simple?: boolean }) => unknown;\n exec: (sql: string) => void;\n transaction: <Args extends unknown[], R>(fn: (...args: Args) => R) => (...args: Args) => R;\n}\n\n/**\n * One forward migration step.\n *\n * `up` runs inside the shared transaction and must be self-contained: it may not commit, and any\n * error it throws aborts the whole run and rolls back every step in it, including the ones that\n * already succeeded. `user_version` is set to `toVersion` immediately after `up` returns, inside\n * the same transaction, so version and schema move together or not at all.\n *\n * `toVersion` is the version the database reaches, not the one it starts from. Steps are ordered\n * by it, gaps are allowed, and two steps sharing a `toVersion` both run — nothing deduplicates\n * them.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface Migration {\n toVersion: number;\n up: (db: SqliteLike) => void;\n}\n\n/**\n * Arguments to `migrateSchema`.\n *\n * `currentVersion` is the version the code expects, and it is the ceiling: a migration whose\n * `toVersion` exceeds it is left unapplied for a later release. `migrations` may be given in any\n * order and may contain steps already applied — both are filtered and sorted here. `label` only\n * ever appears in the downgrade error message.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaOptions {\n db: SqliteLike;\n currentVersion: number;\n migrations: ReadonlyArray<Migration>;\n /** For log/error context (e.g., \"memory-index\", \"registry\"). */\n label?: string;\n}\n\n/**\n * What `migrateSchema` did.\n *\n * `from` is the `user_version` found on the database, `to` is the version it holds afterwards,\n * and `ran` is how many steps executed. When nothing was pending, `to === from` and `ran === 0`.\n *\n * `to` is the `toVersion` of the last step applied, NOT `currentVersion`. A database at version 3\n * against `currentVersion: 5` with no migration in that range comes back as\n * `{ from: 3, to: 3, ran: 0 }` — the gap is reported, not closed. Comparing `to` with\n * `currentVersion` is how a caller notices that the migration list is missing a step.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaResult {\n from: number;\n to: number;\n ran: number;\n}\n\n/**\n * Bring a SQLite database's `user_version` up to `currentVersion` by running the steps that sit\n * between the two.\n *\n * Steps are sorted ascending by `toVersion` and filtered to `> stored && <= currentVersion`, then\n * run in that order inside ONE transaction: each `up` is followed immediately by the matching\n * `user_version` write. Either every step in the run commits or none does — a step that throws\n * aborts the transaction, propagates the error, and leaves the database at the version it started\n * from. There is no partially-migrated state and no resume point.\n *\n * Forward-only. When the stored version is greater than `currentVersion` — an older build opening\n * a database a newer one already migrated — this throws immediately, before touching anything,\n * with a message naming both versions and `label`. Nothing downgrades, and no data is deleted on\n * any path here; what a step does is the step's own business.\n *\n * A stored version equal to `currentVersion` returns without opening a transaction. A `user_version`\n * that is absent or not a number is read as 0, so a fresh database runs every step.\n *\n * Concurrency: the SQLite transaction is the only serialization. This takes no file lock, so two\n * processes migrating the same file at once are arbitrated by SQLite's own write lock — the loser\n * gets a busy or locked error from the driver, not a queued turn. Wrap the call in `withFileLock`\n * if that matters.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function migrateSchema(opts: MigrateSchemaOptions): MigrateSchemaResult {\n const { db, currentVersion, migrations, label = \"db\" } = opts;\n const storedRaw = db.pragma(\"user_version\", { simple: true });\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored > currentVersion) {\n throw new Error(\n `[${label}] schema version ${stored} > current ${currentVersion}; ` +\n \"did you downgrade the SDK? Forward-only migrations only.\",\n );\n }\n\n if (stored === currentVersion) {\n return { from: stored, to: stored, ran: 0 };\n }\n\n const pending = [...migrations]\n .sort((a, b) => a.toVersion - b.toVersion)\n .filter((m) => m.toVersion > stored && m.toVersion <= currentVersion);\n\n let ran = 0;\n let lastApplied = stored;\n\n db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.pragma(`user_version = ${m.toVersion}`);\n lastApplied = m.toVersion;\n ran += 1;\n }\n })();\n\n return { from: stored, to: lastApplied, ran };\n}\n\n// ────────────────────── JSON versioned files ──────────────────────\n\n/**\n * The on-disk envelope: the payload under `data`, its schema version alongside it.\n *\n * `writeVersionedJson` always produces this shape. `readVersionedJson` reads it, but tolerates a\n * file that is not in it — a legacy object with no `_schemaVersion` is treated as version 0 and\n * handed to the migrate callback whole, so this type describes what is written, not everything\n * that can be read.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface VersionedJsonFile<T> {\n _schemaVersion: number;\n data: T;\n}\n\n/**\n * Upgrade callback for `readVersionedJson`, invoked only when the stored version is BELOW the\n * current one.\n *\n * `parsed` is the whole parsed JSON value, not `parsed.data`. That is deliberate: a legacy file\n * predating the envelope has its fields at the top level, and a callback handed only `.data`\n * would receive `undefined` for exactly the files that need migrating. It is typed `unknown`\n * because nothing has validated it — narrow before reading.\n *\n * `fromVersion` is the `_schemaVersion` found on disk, or 0 when the field is missing or not a\n * number.\n *\n * The return value is used as-is and is NOT written back; the file on disk still holds the old\n * shape until someone calls `writeVersionedJson`. Throwing here propagates out of\n * `readVersionedJson`, which otherwise never throws — return the default value instead if an\n * unmigratable file should be survivable.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport type VersionedJsonMigrate<T> = (parsed: unknown, fromVersion: number) => T;\n\n/**\n * Arguments to `readVersionedJson`.\n *\n * `defaultValue` is a factory rather than a value because it is called on several paths and each\n * caller must get its own object — returning a shared mutable default would let one caller's\n * edits appear in another's. It is called for a missing file, an unreadable one, a corrupt one,\n * a non-object payload, and a file written by a newer version.\n *\n * `migrate` is called only for a stored version strictly below `currentVersion`.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface ReadVersionedJsonOptions<T> {\n path: string;\n currentVersion: number;\n migrate: VersionedJsonMigrate<T>;\n defaultValue: () => T;\n}\n\n/**\n * Read a versioned JSON file, migrating or falling back rather than failing.\n *\n * What comes back, by case:\n *\n * - version matches `currentVersion` — the file's `data` field, returned as `T` WITHOUT\n * validation. A file whose envelope is right but whose payload is not gets through.\n * - version below `currentVersion` — whatever `migrate(parsed, stored)` returns. The migrated\n * value is not persisted; the file is untouched.\n * - version above `currentVersion` — `defaultValue()`, plus a warning. Forward-only: a file\n * written by a newer build is left alone rather than downgraded.\n * - file missing, or unreadable for any other reason (permissions, a directory in its place) —\n * `defaultValue()`, silently. Absence and denial are not distinguished.\n * - file present but not valid JSON — `defaultValue()`, and the file is RENAMED to\n * `<path>.corrupt.<epoch-ms>` so the next run starts clean and the bad bytes stay available.\n * A rename that itself fails is warned about and the read still returns the default, which\n * means the same corrupt file will be met again next time.\n * - JSON that parses to a non-object (a number, a string, `null`) — `defaultValue()`, silently\n * and without moving the file aside.\n *\n * The only ways this throws are through the callbacks you supply: `migrate` or `defaultValue`\n * raising. Nothing else here rejects.\n *\n * There is no locking. A concurrent `writeVersionedJson` on the same path is atomic at the rename,\n * so a reader sees either the old file or the new one, never a half-written one — but a\n * read-modify-write built from this pair is NOT atomic across the two calls, and needs\n * `withFileLock` around both.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function readVersionedJson<T>(opts: ReadVersionedJsonOptions<T>): Promise<T> {\n const { path, currentVersion, migrate, defaultValue } = opts;\n\n let raw: string;\n try {\n raw = await readFile(path, \"utf-8\");\n } catch {\n return defaultValue();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // T5.10 — move corrupt file aside so (a) user can investigate and\n // (b) next run starts fresh instead of hitting the same warning loop.\n const asidePath = `${path}.corrupt.${Date.now()}`;\n try {\n await rename(path, asidePath);\n diag(`[theokit-sdk] ${path} is corrupt; moved to ${asidePath}. Using default value.\\n`);\n } catch {\n diag(`[theokit-sdk] ${path} is corrupt; using default value.\\n`);\n }\n return defaultValue();\n }\n\n if (typeof parsed !== \"object\" || parsed === null) {\n return defaultValue();\n }\n\n const file = parsed as { _schemaVersion?: unknown; data?: unknown };\n const storedRaw = file._schemaVersion;\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored === currentVersion) {\n return file.data as T;\n }\n\n if (stored > currentVersion) {\n diag(\n `[theokit-sdk] ${path} schema version ${stored} > current ${currentVersion}; ` +\n \"using default value (forward-only).\\n\",\n );\n return defaultValue();\n }\n\n // EC-2 fix: pass the FULL parsed object to migrate, not just `file.data`.\n // Legacy shapes (e.g., `{ schemaVersion: \"1.0\", agents: {...} }` without\n // a `data` field) need to inspect the whole thing.\n return migrate(parsed, stored);\n}\n\n/**\n * Write `data` wrapped in the version envelope, replacing the file atomically.\n *\n * The bytes go to a temp file in the same directory and are moved into place with `rename`, so a\n * crash or a concurrent reader never observes a partial file: the path holds either the previous\n * content or the complete new content. Parent directories are created as needed, and the file is\n * created with mode 0600 (subject to the process umask), formatted with two-space indentation and\n * a trailing newline.\n *\n * `currentVersion` is stamped verbatim as `_schemaVersion`; nothing checks it against what was\n * already on disk, so this will happily overwrite a newer file with an older version. Read first\n * if that matters.\n *\n * Rejects when `data` cannot be serialized — a circular reference, or a `toJSON` that throws.\n * Serialization happens before any filesystem call, so on that path no directory is created, no\n * temp file exists, and the file already on disk is untouched.\n *\n * Atomicity is the rename's, so it is as strong as the filesystem's: on NFS, SMB or FUSE the\n * underlying writer emits a one-shot warning that the guarantee is weaker there.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function writeVersionedJson<T>(\n path: string,\n data: T,\n currentVersion: number,\n): Promise<void> {\n const file: VersionedJsonFile<T> = {\n _schemaVersion: currentVersion,\n data,\n };\n await atomicWriteJson(path, file);\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkSA6K24NB_cjs = require('./chunk-SA6K24NB.cjs');
4
- var chunkUNFBHV46_cjs = require('./chunk-UNFBHV46.cjs');
4
+ var chunkCEGU5AVD_cjs = require('./chunk-CEGU5AVD.cjs');
5
5
  var crypto = require('crypto');
6
6
  var fs = require('fs');
7
7
  var path = require('path');
8
8
 
9
- var PathTraversalError = class extends chunkUNFBHV46_cjs.ConfigurationError {
9
+ var PathTraversalError = class extends chunkCEGU5AVD_cjs.ConfigurationError {
10
10
  name = "PathTraversalError";
11
11
  constructor(input, resolvedPath) {
12
12
  super(`Path traversal attempt: ${input} \u2192 ${resolvedPath}`, {
@@ -14,7 +14,7 @@ var PathTraversalError = class extends chunkUNFBHV46_cjs.ConfigurationError {
14
14
  });
15
15
  }
16
16
  };
17
- var ForbiddenPathError = class extends chunkUNFBHV46_cjs.ConfigurationError {
17
+ var ForbiddenPathError = class extends chunkCEGU5AVD_cjs.ConfigurationError {
18
18
  name = "ForbiddenPathError";
19
19
  constructor(path) {
20
20
  super(
@@ -188,18 +188,18 @@ function rejectParentTraversal(input, normalized) {
188
188
  function sanitizeIdentifier(input, options) {
189
189
  const maxLen = options?.maxLen ?? 64;
190
190
  if (input.length === 0 || input.length > maxLen) {
191
- throw new chunkUNFBHV46_cjs.ConfigurationError(`Identifier length out of range (1-${maxLen}): "${input}"`, {
191
+ throw new chunkCEGU5AVD_cjs.ConfigurationError(`Identifier length out of range (1-${maxLen}): "${input}"`, {
192
192
  code: "invalid_identifier"
193
193
  });
194
194
  }
195
195
  const controlChar = firstControlCharLabel(input);
196
196
  if (controlChar !== void 0) {
197
- throw new chunkUNFBHV46_cjs.ConfigurationError(`Identifier contains ${controlChar}: "${input}"`, {
197
+ throw new chunkCEGU5AVD_cjs.ConfigurationError(`Identifier contains ${controlChar}: "${input}"`, {
198
198
  code: "invalid_identifier"
199
199
  });
200
200
  }
201
201
  if (!IDENTIFIER_PATTERN.test(input)) {
202
- throw new chunkUNFBHV46_cjs.ConfigurationError(`Identifier contains invalid characters: "${input}"`, {
202
+ throw new chunkCEGU5AVD_cjs.ConfigurationError(`Identifier contains invalid characters: "${input}"`, {
203
203
  code: "invalid_identifier"
204
204
  });
205
205
  }
@@ -207,7 +207,7 @@ function sanitizeIdentifier(input, options) {
207
207
  }
208
208
  function safeFilenameForId(id, options) {
209
209
  if (id.length === 0) {
210
- throw new chunkUNFBHV46_cjs.ConfigurationError("Filename id must be a non-empty string", {
210
+ throw new chunkCEGU5AVD_cjs.ConfigurationError("Filename id must be a non-empty string", {
211
211
  code: "invalid_filename_id"
212
212
  });
213
213
  }
@@ -227,5 +227,5 @@ exports.safeFilenameForId = safeFilenameForId;
227
227
  exports.safePathJoin = safePathJoin;
228
228
  exports.sanitizeIdentifier = sanitizeIdentifier;
229
229
  exports.validateArtifactPath = validateArtifactPath;
230
- //# sourceMappingURL=chunk-GM54F4FE.cjs.map
231
- //# sourceMappingURL=chunk-GM54F4FE.cjs.map
230
+ //# sourceMappingURL=chunk-3KIUY67P.cjs.map
231
+ //# sourceMappingURL=chunk-3KIUY67P.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/security/path-guard.ts"],"names":["ConfigurationError","atOrInsideRoot","resolve","path","realpathSync","lstatSync","readlinkSync","dirname","createHash"],"mappings":"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","file":"chunk-GM54F4FE.cjs","sourcesContent":["/**\n * Canonical path-guard module (ADRs D79-D81).\n *\n * Three primitives + one typed error:\n * - `safePathJoin(base, ...parts)` — resolve THEN prefix-check (ADR D80).\n * - `assertNoSymlinkEscape(path, base)` — `realpathSync` resolves entire\n * symlink chain (EC-1 fix; Hermes v0.2 #386, #61).\n * - `sanitizeIdentifier(input, { maxLen })` — strict grammar\n * `^[a-z0-9][a-z0-9-_]*$` (ADR D81; case-insensitive on input,\n * lowercase on output).\n * - `PathTraversalError` — extends ConfigurationError with code\n * `path_traversal` (ADR D65: no new hierarchy).\n *\n * Wire at all sites where user input becomes a path. CI lint gate\n * `tests/lint/no-unguarded-path-input.test.ts` prevents regression\n * (ADR D85).\n *\n * @internal\n */\n\nimport { createHash } from \"node:crypto\";\nimport { lstatSync, readlinkSync, realpathSync, type Stats } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { atOrInsideRoot } from \"./path-containment.js\";\n\n/**\n * Thrown when a path operation would escape its allowed base directory.\n * Extends `ConfigurationError` (no new error hierarchy per ADR D65).\n *\n */\nexport class PathTraversalError extends ConfigurationError {\n override readonly name: string = \"PathTraversalError\";\n\n constructor(input: string, resolvedPath: string) {\n super(`Path traversal attempt: ${input} → ${resolvedPath}`, {\n code: \"path_traversal\",\n });\n }\n}\n\n/**\n * Thrown when an agent tool is asked to read or write a sensitive path\n * that the blocklist forbids (`.env`, `.git/`, `node_modules/`, `.theo/`,\n * lock files). Distinct from `PathTraversalError` because the path is\n * lexically inside the project — it is just sensitive.\n *\n * Extends `ConfigurationError` (no new error hierarchy per ADR D65).\n *\n * @public\n */\nexport class ForbiddenPathError extends ConfigurationError {\n override readonly name: string = \"ForbiddenPathError\";\n\n constructor(path: string) {\n super(\n `Path '${path}' is in the sensitive-file blocklist (.env, .git/, node_modules/, .theo/, lock files)`,\n {\n code: \"forbidden_path\",\n },\n );\n }\n}\n\n/**\n * Is `target` the same as `base`, or strictly under it?\n *\n * The prefix must not double the separator. `resolve` strips a trailing `sep` from every base\n * EXCEPT the filesystem root, where `resolve(sep) === sep`; there `base + sep` would be `//`, which\n * no absolute path starts with, so a root base rejected EVERY path (#149). Only the root takes that\n * branch — for any other base the prefix is unchanged, so containment is not weakened.\n *\n * Both containment checks in this module go through here: they had the same defect and were fixed\n * one at a time, which is exactly how the two drifted apart in the first place.\n *\n * Both arguments MUST already be resolved absolute paths.\n */\nfunction isInside(target: string, base: string): boolean {\n // B-117 — compared after symlink resolution, via the shared rule. A prefix test judges a link by\n // its NAME: `<base>/link/x` where `link` points outside is lexically inside and physically not.\n // Measured reachable from the plugin manager and the MCP client before this was changed.\n //\n // The root itself stays allowed, which `insideRoot` alone does not answer: it returns false for\n // `target === base` (correct for its own caller, wrong here, where `safePathJoin(base)` with no\n // parts must return `base`). Kept as an explicit second clause rather than by weakening the\n // shared rule for everyone.\n return atOrInsideRoot(target, base);\n}\n\n/**\n * Join `base` with `...parts` and ensure the resolved absolute path stays\n * under `base`. Resolves FIRST, then prefix-checks (ADR D80) — prevents\n * normalized-escape bypasses like `subdir/.\\\\./bar`.\n *\n * Returns the safe absolute path. Throws `PathTraversalError` if escape.\n *\n */\nexport function safePathJoin(base: string, ...parts: string[]): string {\n if (base === \"\") {\n throw new Error(\"safePathJoin: base must be non-empty\");\n }\n // T5.5 — NUL byte + C0/DEL control char rejection at the boundary.\n // Apply before path resolution so a malicious input never reaches\n // `resolve` (which on some platforms behaved unexpectedly with NUL\n // and in N-API callers historically silently truncated).\n rejectNulAndControlChars(base, \"base\");\n for (const part of parts) {\n rejectNulAndControlChars(part, \"path segment\");\n }\n const baseResolved = resolve(base);\n const target = resolve(base, ...parts);\n if (!isInside(target, baseResolved)) {\n throw new PathTraversalError(parts.join(\"/\"), target);\n }\n return target;\n}\n\n/**\n * T5.5 — the operator-legible label for the first NUL (`\\x00`), C0 control char (`\\x01-\\x1F`) or\n * DEL (`\\x7F`) in `input`, or `undefined` when it carries none. Centralizes the scan so every\n * public path-guard / sanitize entrypoint shares one definition of \"control character\".\n *\n * Detection is separated from the throw so the two callers can share the scan and the precise\n * diagnostic while each raising the error ITS OWN contract documents (#368) — a path guard reports\n * a traversal, an identifier validator reports an invalid identifier. Before that split both threw\n * `PathTraversalError`, which made an identifier validator's error class depend on the bytes of the\n * input it was handed.\n *\n * @internal\n */\nfunction firstControlCharLabel(input: string): string | undefined {\n for (let i = 0; i < input.length; i++) {\n const code = input.charCodeAt(i);\n if (code === 0x00 || (code >= 0x01 && code <= 0x1f) || code === 0x7f) {\n return code === 0x00 ? \"<nul-byte>\" : `<control-char-0x${code.toString(16)}>`;\n }\n }\n return undefined;\n}\n\n/**\n * Reject any path-shaped input carrying a NUL or control character, as `PathTraversalError` — the\n * same shape as every other path-shape rejection, so callers do not learn a second error class.\n *\n * @internal\n */\nfunction rejectNulAndControlChars(input: string, role: string): void {\n const label = firstControlCharLabel(input);\n if (label !== undefined) throw new PathTraversalError(`${role}: ${input}`, label);\n}\n\n/**\n * Assert that `path` — including every directory component in the chain —\n * stays under `base` after symlink resolution. No-op when nothing on the\n * path exists yet.\n *\n * Two-bug history:\n * 1. **EC-1** (original fix, kept): a multi-level symlink chain A → B → C\n * must be resolved end-to-end. `realpathSync` does this in 1 syscall.\n * 2. **Defence-in-depth** (added v1.x): the previous implementation only\n * called `lstatSync(path)` on the terminal component. If an INTERMEDIATE\n * directory was a symlink (`base/inner-symlink → /outside`), `lstat` on\n * `base/inner-symlink/file.txt` followed the symlink and reported the\n * regular file — escape went undetected. Fix: walk up to the nearest\n * existing ancestor and `realpath` THAT, then re-attach the suffix and\n * check the result against the canonical base.\n *\n */\nexport function assertNoSymlinkEscape(path: string, base: string): void {\n // T5.5 — reject NUL / control chars before any FS call (a NUL byte\n // in the path used to silently truncate at the C boundary on legacy\n // libc — defense in depth even on modern Node).\n rejectNulAndControlChars(path, \"path\");\n rejectNulAndControlChars(base, \"base\");\n // Canonical base — symlinks in the base path itself are absorbed once here.\n let baseResolved: string;\n try {\n baseResolved = realpathSync(base);\n } catch {\n // base doesn't exist as a real directory yet — fall back to lexical resolve.\n baseResolved = resolve(base);\n }\n\n // Find the deepest ancestor of `path` that exists, then realpath it.\n // Anything from there onward is \"not yet on disk\" and contributes only\n // its lexical suffix. This covers three cases:\n // - path exists (regular file or symlink at any depth) → realpath the full path\n // - path doesn't exist but intermediate dir is a symlink → realpath the ancestor\n // - nothing on the path exists → no escape risk (return)\n const resolved = realpathOfDeepestExisting(path);\n if (resolved === undefined) return; // path has no existing prefix — nothing to attack\n\n if (!isInside(resolved, baseResolved)) {\n throw new PathTraversalError(`symlink ${path}`, resolved);\n }\n}\n\n/**\n * Find the deepest ancestor of `path` that exists on disk, resolve all\n * symlinks in that ancestor via `realpathSync`, and re-attach the\n * lexical suffix. Returns `undefined` when no ancestor exists.\n *\n * Handles dangling symlinks: if the terminal IS a symlink but its target\n * is missing, we still detect escape via `readlinkSync` + parent resolve.\n */\nfunction realpathOfDeepestExisting(path: string): string | undefined {\n // First try the full path — the common case.\n try {\n return realpathSync(path);\n } catch {\n // Not resolvable. Two sub-cases.\n }\n\n // Sub-case A: terminal is a dangling symlink.\n try {\n const stat: Stats = lstatSync(path);\n if (stat.isSymbolicLink()) {\n const target = readlinkSync(path);\n // Resolve target relative to the REAL parent dir, so intermediate\n // symlinks in the parent chain are absorbed.\n const parentReal = realpathOfDeepestExisting(dirname(path));\n const parentBase = parentReal ?? dirname(path);\n return resolve(parentBase, target);\n }\n } catch {\n // lstat failed too — terminal doesn't exist at all.\n }\n\n // Sub-case B: walk up to the nearest existing ancestor, then re-attach\n // the suffix lexically.\n let cursor = dirname(path);\n let suffix = path.slice(cursor.length);\n while (cursor !== dirname(cursor)) {\n try {\n const real = realpathSync(cursor);\n // Reconstruct: ancestor's realpath + remaining (still-lexical) suffix\n return resolve(real, `.${suffix}`);\n } catch {\n suffix = path.slice(dirname(cursor).length);\n cursor = dirname(cursor);\n }\n }\n // Reached filesystem root without finding any existing ancestor.\n return undefined;\n}\n\nconst LOCK_FILES = new Set([\"pnpm-lock.yaml\", \"package-lock.json\", \"yarn.lock\", \"bun.lockb\"]);\n\n// T5.6 — top-level credential dot-dirs / dot-files. Matching against\n// the FIRST path segment (lowercase). Adding any entry here costs a\n// CHANGELOG note + an explicit case-fold test (entries are lowercased\n// at module load).\nconst SENSITIVE_FIRST_SEGMENTS = new Set([\n \".ssh\",\n \".aws\",\n \".docker\",\n \".kube\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n]);\n\n// T5.6 — credential basenames blocked at ANY depth (lowercase). Catches\n// the developer-laptop case where an agent recurses into a subdir.\nconst SENSITIVE_BASENAMES = new Set([\n \"id_rsa\",\n \"id_ed25519\",\n \"id_ecdsa\",\n \"id_dsa\",\n \"authorized_keys\",\n \"known_hosts\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n]);\n\n// T5.6 — extension suffixes blocked at ANY depth (lowercase). Covers\n// the entire `*.pem` / `*.key` private-material family.\nconst SENSITIVE_SUFFIXES = [\".pem\", \".key\", \".p12\", \".pfx\"];\n\n/**\n * Decide whether a project-relative path points to a known-sensitive file\n * that a coding agent must not read or write.\n *\n * Universal blocklist (works for any agent operating on a project tree):\n *\n * - `.env`, `.env.<anything>` — except `.env.example` (template safe to read)\n * - `.git/` — version control internals\n * - `node_modules/` — dependency cache (changes don't belong to the user)\n * - `.theo/` — TheoKit build artefacts / state\n * - Lock files at any depth: `pnpm-lock.yaml`, `package-lock.json`,\n * `yarn.lock`, `bun.lockb`\n *\n * Operates on path segments (forward-slash normalized). Cross-platform safe.\n *\n * Use together with `safePathJoin` + `assertNoSymlinkEscape`: the former two\n * defeat traversal, this one defeats reading a file that is lexically inside\n * the project but should not be agent-visible.\n *\n * @public\n */\nexport function isForbiddenPath(input: string): boolean {\n // T5.6 — lowercase normalization defeats case-only bypass on\n // case-insensitive filesystems (Windows/macOS-default) where `.ENV`\n // and `.env` map to the same inode but a case-sensitive string\n // check passes the former.\n const normalized = input.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").toLowerCase();\n if (normalized.length === 0) return false;\n\n const segments = normalized.split(\"/\").filter((s) => s.length > 0);\n if (segments.length === 0) return false;\n\n if (isForbiddenFirstSegment(segments[0]!)) return true;\n if (isForbiddenBasename(segments[segments.length - 1]!)) return true;\n return false;\n}\n\nfunction isForbiddenFirstSegment(first: string): boolean {\n // .env.example is explicitly allowlisted (template safe to read)\n if (first === \".env.example\") return false;\n if (first === \".env\") return true;\n if (/^\\.env\\./.test(first)) return true;\n if (first === \".git\" || first === \"node_modules\" || first === \".theo\") return true;\n return SENSITIVE_FIRST_SEGMENTS.has(first);\n}\n\nfunction isForbiddenBasename(basename: string): boolean {\n if (LOCK_FILES.has(basename)) return true;\n if (SENSITIVE_BASENAMES.has(basename)) return true;\n for (const suffix of SENSITIVE_SUFFIXES) {\n if (basename.endsWith(suffix)) return true;\n }\n return false;\n}\n\nconst IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9\\-_]*$/i;\n\n/**\n * T1.4 — validate a relative artifact path string BEFORE it is used to look\n * up a fixture or to fetch from PaaS. Rejects every well-known traversal\n * vector at the boundary, throwing `PathTraversalError`.\n *\n * Vectors rejected:\n * - classic `..` parent-directory traversal (any segment).\n * - backslash separators (Windows-style `..\\\\windows`).\n * - URL-encoded `%2e%2e` / `%2E%2E` (double-decoded traversal).\n * - NUL byte injection (`\\x00`).\n * - Windows drive letter prefix (`C:`, `D:\\\\...`).\n * - Home-tilde expansion (`~/`, `~root/...`).\n * - Absolute paths starting with `/`.\n *\n * Does NOT touch the filesystem — the call is shape-only. Live symlink\n * traversal protection happens via `assertNoSymlinkEscape` at the FS-resolve\n * boundary.\n *\n * @param input - Caller-supplied artifact path.\n * @throws `PathTraversalError` on any rejection.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/security` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function validateArtifactPath(input: string): void {\n rejectKnownPrefixVectors(input);\n const normalized = decodeAndNormalize(input);\n rejectParentTraversal(input, normalized);\n}\n\nfunction rejectKnownPrefixVectors(input: string): void {\n if (input.includes(\"\\x00\")) {\n throw new PathTraversalError(input, \"<nul-byte>\");\n }\n if (input.startsWith(\"/\") || input.startsWith(\"~\")) {\n throw new PathTraversalError(input, input);\n }\n if (/^[A-Za-z]:[\\\\/]?/.test(input)) {\n throw new PathTraversalError(input, input);\n }\n}\n\nfunction decodeAndNormalize(input: string): string {\n // URL-encoded traversal — 2 passes catches `%252e%252e`.\n // Malformed sequences (decodeURIComponent throws) are themselves a rejection.\n let decoded = input;\n for (let i = 0; i < 2; i += 1) {\n try {\n const next = decodeURIComponent(decoded);\n if (next === decoded) break;\n decoded = next;\n } catch {\n throw new PathTraversalError(input, \"<malformed-url-encoding>\");\n }\n }\n // Normalize backslash to forward slash before segment-walking.\n return decoded.replace(/\\\\/g, \"/\");\n}\n\nfunction rejectParentTraversal(input: string, normalized: string): void {\n for (const segment of normalized.split(\"/\")) {\n if (segment === \"..\" || segment === \"..%00\") {\n throw new PathTraversalError(input, normalized);\n }\n }\n // Defense in depth: literal `..` anywhere in the normalized string.\n if (normalized.includes(\"..\")) {\n throw new PathTraversalError(input, normalized);\n }\n}\n\n/**\n * Validate that `input` is a safe path component (skill name, agent ID,\n * namespace, etc.) and return its lowercase form. Strict grammar\n * `^[a-z0-9][a-z0-9-_]*$` rejects path separators, dots, null bytes,\n * whitespace, unicode invisible chars, and any leading `-`/`_`.\n *\n * ONE error class leaves this function: `ConfigurationError` with code\n * `invalid_identifier`, for every rejection — length out of range, a path\n * separator, `..`, a space, a leading `-`, and a NUL / C0 control char / DEL\n * alike. A caller can branch on that code and know it has covered the whole\n * rejection surface.\n *\n * Before #368 the control-char branch threw `PathTraversalError` instead, which\n * made the error CLASS a function of the attacker's bytes; the message still names\n * the offending byte, which is the part that was worth keeping.\n *\n * @param input - User-supplied identifier candidate.\n * @param options.maxLen - Maximum allowed length (default 64).\n * @returns Lowercase form of `input`.\n * @throws `ConfigurationError` with code `invalid_identifier` on every rejection.\n */\nexport function sanitizeIdentifier(input: string, options?: { maxLen?: number }): string {\n const maxLen = options?.maxLen ?? 64;\n if (input.length === 0 || input.length > maxLen) {\n throw new ConfigurationError(`Identifier length out of range (1-${maxLen}): \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n // T5.5 — explicit NUL / control char rejection ahead of the generic pattern check. The\n // IDENTIFIER_PATTERN regex already excludes these (they are not in `[a-z0-9\\-_]`); naming the\n // offending byte gives operators a precise diagnostic instead of the generic \"invalid characters\"\n // message, which is what makes a prompt-injection trace legible (Rule 3).\n //\n // #368 — the diagnostic used to arrive as a `PathTraversalError`, because the branch reused\n // `safePathJoin`'s thrower. That made the error CLASS a function of the attacker's bytes: a\n // caller branching on the documented `invalid_identifier` rethrew for any input carrying a\n // control char, so a rejection surfaced as a 500 and the 400/500 split became an oracle. The\n // detection is shared; the error each contract documents is not.\n const controlChar = firstControlCharLabel(input);\n if (controlChar !== undefined) {\n throw new ConfigurationError(`Identifier contains ${controlChar}: \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n if (!IDENTIFIER_PATTERN.test(input)) {\n throw new ConfigurationError(`Identifier contains invalid characters: \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n return input.toLowerCase();\n}\n\n/**\n * Convert ANY opaque id (agent id, run id, conversation id, namespace, email,\n * arbitrary string) into a deterministic, filesystem-safe filename component.\n *\n * Unlike {@link sanitizeIdentifier} (which THROWS on non-conforming input),\n * this is a total function: it NEVER throws on a non-empty string. It returns\n * the lowercased id verbatim when it already matches the safe grammar\n * `^[a-z0-9][a-z0-9-_]*$` and fits `maxLen` (so UUIDs, hashes, and slugs stay\n * human-readable), otherwise a deterministic `h-<16 hex>` sha256 token\n * (collision-resistant and always a valid filename). The output charset is\n * always `[a-z0-9_-]`, safe as a literal path segment on every filesystem.\n *\n * @param id - any opaque identifier (must be a non-empty string)\n * @param options.maxLen - max length for the passthrough branch (default 128).\n * Ids longer than this are hashed; the hash token itself is always short.\n * @throws ConfigurationError (code `invalid_filename_id`) only on empty input.\n *\n * @example\n * safeFilenameForId(\"550e8400-e29b-41d4-a716-446655440000\") // passthrough\n * safeFilenameForId(\"user@example.com\") // \"h-<16hex>\"\n *\n */\nexport function safeFilenameForId(id: string, options?: { maxLen?: number }): string {\n if (id.length === 0) {\n throw new ConfigurationError(\"Filename id must be a non-empty string\", {\n code: \"invalid_filename_id\",\n });\n }\n const maxLen = options?.maxLen ?? 128;\n const lower = id.toLowerCase();\n if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {\n return lower;\n }\n return `h-${createHash(\"sha256\").update(id).digest(\"hex\").slice(0, 16)}`;\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/security/path-guard.ts"],"names":["ConfigurationError","atOrInsideRoot","resolve","path","realpathSync","lstatSync","readlinkSync","dirname","createHash"],"mappings":"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","file":"chunk-3KIUY67P.cjs","sourcesContent":["/**\n * Canonical path-guard module (ADRs D79-D81).\n *\n * Three primitives + one typed error:\n * - `safePathJoin(base, ...parts)` — resolve THEN prefix-check (ADR D80).\n * - `assertNoSymlinkEscape(path, base)` — `realpathSync` resolves entire\n * symlink chain (EC-1 fix; Hermes v0.2 #386, #61).\n * - `sanitizeIdentifier(input, { maxLen })` — strict grammar\n * `^[a-z0-9][a-z0-9-_]*$` (ADR D81; case-insensitive on input,\n * lowercase on output).\n * - `PathTraversalError` — extends ConfigurationError with code\n * `path_traversal` (ADR D65: no new hierarchy).\n *\n * Wire at all sites where user input becomes a path. CI lint gate\n * `tests/lint/no-unguarded-path-input.test.ts` prevents regression\n * (ADR D85).\n *\n * @internal\n */\n\nimport { createHash } from \"node:crypto\";\nimport { lstatSync, readlinkSync, realpathSync, type Stats } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { atOrInsideRoot } from \"./path-containment.js\";\n\n/**\n * Thrown when a path operation would escape its allowed base directory.\n * Extends `ConfigurationError` (no new error hierarchy per ADR D65).\n *\n */\nexport class PathTraversalError extends ConfigurationError {\n override readonly name: string = \"PathTraversalError\";\n\n constructor(input: string, resolvedPath: string) {\n super(`Path traversal attempt: ${input} → ${resolvedPath}`, {\n code: \"path_traversal\",\n });\n }\n}\n\n/**\n * Thrown when an agent tool is asked to read or write a sensitive path\n * that the blocklist forbids (`.env`, `.git/`, `node_modules/`, `.theo/`,\n * lock files). Distinct from `PathTraversalError` because the path is\n * lexically inside the project — it is just sensitive.\n *\n * Extends `ConfigurationError` (no new error hierarchy per ADR D65).\n *\n * @public\n */\nexport class ForbiddenPathError extends ConfigurationError {\n override readonly name: string = \"ForbiddenPathError\";\n\n constructor(path: string) {\n super(\n `Path '${path}' is in the sensitive-file blocklist (.env, .git/, node_modules/, .theo/, lock files)`,\n {\n code: \"forbidden_path\",\n },\n );\n }\n}\n\n/**\n * Is `target` the same as `base`, or strictly under it?\n *\n * The prefix must not double the separator. `resolve` strips a trailing `sep` from every base\n * EXCEPT the filesystem root, where `resolve(sep) === sep`; there `base + sep` would be `//`, which\n * no absolute path starts with, so a root base rejected EVERY path (#149). Only the root takes that\n * branch — for any other base the prefix is unchanged, so containment is not weakened.\n *\n * Both containment checks in this module go through here: they had the same defect and were fixed\n * one at a time, which is exactly how the two drifted apart in the first place.\n *\n * Both arguments MUST already be resolved absolute paths.\n */\nfunction isInside(target: string, base: string): boolean {\n // B-117 — compared after symlink resolution, via the shared rule. A prefix test judges a link by\n // its NAME: `<base>/link/x` where `link` points outside is lexically inside and physically not.\n // Measured reachable from the plugin manager and the MCP client before this was changed.\n //\n // The root itself stays allowed, which `insideRoot` alone does not answer: it returns false for\n // `target === base` (correct for its own caller, wrong here, where `safePathJoin(base)` with no\n // parts must return `base`). Kept as an explicit second clause rather than by weakening the\n // shared rule for everyone.\n return atOrInsideRoot(target, base);\n}\n\n/**\n * Join `base` with `...parts` and ensure the resolved absolute path stays\n * under `base`. Resolves FIRST, then prefix-checks (ADR D80) — prevents\n * normalized-escape bypasses like `subdir/.\\\\./bar`.\n *\n * Returns the safe absolute path. Throws `PathTraversalError` if escape.\n *\n */\nexport function safePathJoin(base: string, ...parts: string[]): string {\n if (base === \"\") {\n throw new Error(\"safePathJoin: base must be non-empty\");\n }\n // T5.5 — NUL byte + C0/DEL control char rejection at the boundary.\n // Apply before path resolution so a malicious input never reaches\n // `resolve` (which on some platforms behaved unexpectedly with NUL\n // and in N-API callers historically silently truncated).\n rejectNulAndControlChars(base, \"base\");\n for (const part of parts) {\n rejectNulAndControlChars(part, \"path segment\");\n }\n const baseResolved = resolve(base);\n const target = resolve(base, ...parts);\n if (!isInside(target, baseResolved)) {\n throw new PathTraversalError(parts.join(\"/\"), target);\n }\n return target;\n}\n\n/**\n * T5.5 — the operator-legible label for the first NUL (`\\x00`), C0 control char (`\\x01-\\x1F`) or\n * DEL (`\\x7F`) in `input`, or `undefined` when it carries none. Centralizes the scan so every\n * public path-guard / sanitize entrypoint shares one definition of \"control character\".\n *\n * Detection is separated from the throw so the two callers can share the scan and the precise\n * diagnostic while each raising the error ITS OWN contract documents (#368) — a path guard reports\n * a traversal, an identifier validator reports an invalid identifier. Before that split both threw\n * `PathTraversalError`, which made an identifier validator's error class depend on the bytes of the\n * input it was handed.\n *\n * @internal\n */\nfunction firstControlCharLabel(input: string): string | undefined {\n for (let i = 0; i < input.length; i++) {\n const code = input.charCodeAt(i);\n if (code === 0x00 || (code >= 0x01 && code <= 0x1f) || code === 0x7f) {\n return code === 0x00 ? \"<nul-byte>\" : `<control-char-0x${code.toString(16)}>`;\n }\n }\n return undefined;\n}\n\n/**\n * Reject any path-shaped input carrying a NUL or control character, as `PathTraversalError` — the\n * same shape as every other path-shape rejection, so callers do not learn a second error class.\n *\n * @internal\n */\nfunction rejectNulAndControlChars(input: string, role: string): void {\n const label = firstControlCharLabel(input);\n if (label !== undefined) throw new PathTraversalError(`${role}: ${input}`, label);\n}\n\n/**\n * Assert that `path` — including every directory component in the chain —\n * stays under `base` after symlink resolution. No-op when nothing on the\n * path exists yet.\n *\n * Two-bug history:\n * 1. **EC-1** (original fix, kept): a multi-level symlink chain A → B → C\n * must be resolved end-to-end. `realpathSync` does this in 1 syscall.\n * 2. **Defence-in-depth** (added v1.x): the previous implementation only\n * called `lstatSync(path)` on the terminal component. If an INTERMEDIATE\n * directory was a symlink (`base/inner-symlink → /outside`), `lstat` on\n * `base/inner-symlink/file.txt` followed the symlink and reported the\n * regular file — escape went undetected. Fix: walk up to the nearest\n * existing ancestor and `realpath` THAT, then re-attach the suffix and\n * check the result against the canonical base.\n *\n */\nexport function assertNoSymlinkEscape(path: string, base: string): void {\n // T5.5 — reject NUL / control chars before any FS call (a NUL byte\n // in the path used to silently truncate at the C boundary on legacy\n // libc — defense in depth even on modern Node).\n rejectNulAndControlChars(path, \"path\");\n rejectNulAndControlChars(base, \"base\");\n // Canonical base — symlinks in the base path itself are absorbed once here.\n let baseResolved: string;\n try {\n baseResolved = realpathSync(base);\n } catch {\n // base doesn't exist as a real directory yet — fall back to lexical resolve.\n baseResolved = resolve(base);\n }\n\n // Find the deepest ancestor of `path` that exists, then realpath it.\n // Anything from there onward is \"not yet on disk\" and contributes only\n // its lexical suffix. This covers three cases:\n // - path exists (regular file or symlink at any depth) → realpath the full path\n // - path doesn't exist but intermediate dir is a symlink → realpath the ancestor\n // - nothing on the path exists → no escape risk (return)\n const resolved = realpathOfDeepestExisting(path);\n if (resolved === undefined) return; // path has no existing prefix — nothing to attack\n\n if (!isInside(resolved, baseResolved)) {\n throw new PathTraversalError(`symlink ${path}`, resolved);\n }\n}\n\n/**\n * Find the deepest ancestor of `path` that exists on disk, resolve all\n * symlinks in that ancestor via `realpathSync`, and re-attach the\n * lexical suffix. Returns `undefined` when no ancestor exists.\n *\n * Handles dangling symlinks: if the terminal IS a symlink but its target\n * is missing, we still detect escape via `readlinkSync` + parent resolve.\n */\nfunction realpathOfDeepestExisting(path: string): string | undefined {\n // First try the full path — the common case.\n try {\n return realpathSync(path);\n } catch {\n // Not resolvable. Two sub-cases.\n }\n\n // Sub-case A: terminal is a dangling symlink.\n try {\n const stat: Stats = lstatSync(path);\n if (stat.isSymbolicLink()) {\n const target = readlinkSync(path);\n // Resolve target relative to the REAL parent dir, so intermediate\n // symlinks in the parent chain are absorbed.\n const parentReal = realpathOfDeepestExisting(dirname(path));\n const parentBase = parentReal ?? dirname(path);\n return resolve(parentBase, target);\n }\n } catch {\n // lstat failed too — terminal doesn't exist at all.\n }\n\n // Sub-case B: walk up to the nearest existing ancestor, then re-attach\n // the suffix lexically.\n let cursor = dirname(path);\n let suffix = path.slice(cursor.length);\n while (cursor !== dirname(cursor)) {\n try {\n const real = realpathSync(cursor);\n // Reconstruct: ancestor's realpath + remaining (still-lexical) suffix\n return resolve(real, `.${suffix}`);\n } catch {\n suffix = path.slice(dirname(cursor).length);\n cursor = dirname(cursor);\n }\n }\n // Reached filesystem root without finding any existing ancestor.\n return undefined;\n}\n\nconst LOCK_FILES = new Set([\"pnpm-lock.yaml\", \"package-lock.json\", \"yarn.lock\", \"bun.lockb\"]);\n\n// T5.6 — top-level credential dot-dirs / dot-files. Matching against\n// the FIRST path segment (lowercase). Adding any entry here costs a\n// CHANGELOG note + an explicit case-fold test (entries are lowercased\n// at module load).\nconst SENSITIVE_FIRST_SEGMENTS = new Set([\n \".ssh\",\n \".aws\",\n \".docker\",\n \".kube\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n]);\n\n// T5.6 — credential basenames blocked at ANY depth (lowercase). Catches\n// the developer-laptop case where an agent recurses into a subdir.\nconst SENSITIVE_BASENAMES = new Set([\n \"id_rsa\",\n \"id_ed25519\",\n \"id_ecdsa\",\n \"id_dsa\",\n \"authorized_keys\",\n \"known_hosts\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n]);\n\n// T5.6 — extension suffixes blocked at ANY depth (lowercase). Covers\n// the entire `*.pem` / `*.key` private-material family.\nconst SENSITIVE_SUFFIXES = [\".pem\", \".key\", \".p12\", \".pfx\"];\n\n/**\n * Decide whether a project-relative path points to a known-sensitive file\n * that a coding agent must not read or write.\n *\n * Universal blocklist (works for any agent operating on a project tree):\n *\n * - `.env`, `.env.<anything>` — except `.env.example` (template safe to read)\n * - `.git/` — version control internals\n * - `node_modules/` — dependency cache (changes don't belong to the user)\n * - `.theo/` — TheoKit build artefacts / state\n * - Lock files at any depth: `pnpm-lock.yaml`, `package-lock.json`,\n * `yarn.lock`, `bun.lockb`\n *\n * Operates on path segments (forward-slash normalized). Cross-platform safe.\n *\n * Use together with `safePathJoin` + `assertNoSymlinkEscape`: the former two\n * defeat traversal, this one defeats reading a file that is lexically inside\n * the project but should not be agent-visible.\n *\n * @public\n */\nexport function isForbiddenPath(input: string): boolean {\n // T5.6 — lowercase normalization defeats case-only bypass on\n // case-insensitive filesystems (Windows/macOS-default) where `.ENV`\n // and `.env` map to the same inode but a case-sensitive string\n // check passes the former.\n const normalized = input.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\").toLowerCase();\n if (normalized.length === 0) return false;\n\n const segments = normalized.split(\"/\").filter((s) => s.length > 0);\n if (segments.length === 0) return false;\n\n if (isForbiddenFirstSegment(segments[0]!)) return true;\n if (isForbiddenBasename(segments[segments.length - 1]!)) return true;\n return false;\n}\n\nfunction isForbiddenFirstSegment(first: string): boolean {\n // .env.example is explicitly allowlisted (template safe to read)\n if (first === \".env.example\") return false;\n if (first === \".env\") return true;\n if (/^\\.env\\./.test(first)) return true;\n if (first === \".git\" || first === \"node_modules\" || first === \".theo\") return true;\n return SENSITIVE_FIRST_SEGMENTS.has(first);\n}\n\nfunction isForbiddenBasename(basename: string): boolean {\n if (LOCK_FILES.has(basename)) return true;\n if (SENSITIVE_BASENAMES.has(basename)) return true;\n for (const suffix of SENSITIVE_SUFFIXES) {\n if (basename.endsWith(suffix)) return true;\n }\n return false;\n}\n\nconst IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9\\-_]*$/i;\n\n/**\n * T1.4 — validate a relative artifact path string BEFORE it is used to look\n * up a fixture or to fetch from PaaS. Rejects every well-known traversal\n * vector at the boundary, throwing `PathTraversalError`.\n *\n * Vectors rejected:\n * - classic `..` parent-directory traversal (any segment).\n * - backslash separators (Windows-style `..\\\\windows`).\n * - URL-encoded `%2e%2e` / `%2E%2E` (double-decoded traversal).\n * - NUL byte injection (`\\x00`).\n * - Windows drive letter prefix (`C:`, `D:\\\\...`).\n * - Home-tilde expansion (`~/`, `~root/...`).\n * - Absolute paths starting with `/`.\n *\n * Does NOT touch the filesystem — the call is shape-only. Live symlink\n * traversal protection happens via `assertNoSymlinkEscape` at the FS-resolve\n * boundary.\n *\n * @param input - Caller-supplied artifact path.\n * @throws `PathTraversalError` on any rejection.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/security` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function validateArtifactPath(input: string): void {\n rejectKnownPrefixVectors(input);\n const normalized = decodeAndNormalize(input);\n rejectParentTraversal(input, normalized);\n}\n\nfunction rejectKnownPrefixVectors(input: string): void {\n if (input.includes(\"\\x00\")) {\n throw new PathTraversalError(input, \"<nul-byte>\");\n }\n if (input.startsWith(\"/\") || input.startsWith(\"~\")) {\n throw new PathTraversalError(input, input);\n }\n if (/^[A-Za-z]:[\\\\/]?/.test(input)) {\n throw new PathTraversalError(input, input);\n }\n}\n\nfunction decodeAndNormalize(input: string): string {\n // URL-encoded traversal — 2 passes catches `%252e%252e`.\n // Malformed sequences (decodeURIComponent throws) are themselves a rejection.\n let decoded = input;\n for (let i = 0; i < 2; i += 1) {\n try {\n const next = decodeURIComponent(decoded);\n if (next === decoded) break;\n decoded = next;\n } catch {\n throw new PathTraversalError(input, \"<malformed-url-encoding>\");\n }\n }\n // Normalize backslash to forward slash before segment-walking.\n return decoded.replace(/\\\\/g, \"/\");\n}\n\nfunction rejectParentTraversal(input: string, normalized: string): void {\n for (const segment of normalized.split(\"/\")) {\n if (segment === \"..\" || segment === \"..%00\") {\n throw new PathTraversalError(input, normalized);\n }\n }\n // Defense in depth: literal `..` anywhere in the normalized string.\n if (normalized.includes(\"..\")) {\n throw new PathTraversalError(input, normalized);\n }\n}\n\n/**\n * Validate that `input` is a safe path component (skill name, agent ID,\n * namespace, etc.) and return its lowercase form. Strict grammar\n * `^[a-z0-9][a-z0-9-_]*$` rejects path separators, dots, null bytes,\n * whitespace, unicode invisible chars, and any leading `-`/`_`.\n *\n * ONE error class leaves this function: `ConfigurationError` with code\n * `invalid_identifier`, for every rejection — length out of range, a path\n * separator, `..`, a space, a leading `-`, and a NUL / C0 control char / DEL\n * alike. A caller can branch on that code and know it has covered the whole\n * rejection surface.\n *\n * Before #368 the control-char branch threw `PathTraversalError` instead, which\n * made the error CLASS a function of the attacker's bytes; the message still names\n * the offending byte, which is the part that was worth keeping.\n *\n * @param input - User-supplied identifier candidate.\n * @param options.maxLen - Maximum allowed length (default 64).\n * @returns Lowercase form of `input`.\n * @throws `ConfigurationError` with code `invalid_identifier` on every rejection.\n */\nexport function sanitizeIdentifier(input: string, options?: { maxLen?: number }): string {\n const maxLen = options?.maxLen ?? 64;\n if (input.length === 0 || input.length > maxLen) {\n throw new ConfigurationError(`Identifier length out of range (1-${maxLen}): \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n // T5.5 — explicit NUL / control char rejection ahead of the generic pattern check. The\n // IDENTIFIER_PATTERN regex already excludes these (they are not in `[a-z0-9\\-_]`); naming the\n // offending byte gives operators a precise diagnostic instead of the generic \"invalid characters\"\n // message, which is what makes a prompt-injection trace legible (Rule 3).\n //\n // #368 — the diagnostic used to arrive as a `PathTraversalError`, because the branch reused\n // `safePathJoin`'s thrower. That made the error CLASS a function of the attacker's bytes: a\n // caller branching on the documented `invalid_identifier` rethrew for any input carrying a\n // control char, so a rejection surfaced as a 500 and the 400/500 split became an oracle. The\n // detection is shared; the error each contract documents is not.\n const controlChar = firstControlCharLabel(input);\n if (controlChar !== undefined) {\n throw new ConfigurationError(`Identifier contains ${controlChar}: \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n if (!IDENTIFIER_PATTERN.test(input)) {\n throw new ConfigurationError(`Identifier contains invalid characters: \"${input}\"`, {\n code: \"invalid_identifier\",\n });\n }\n return input.toLowerCase();\n}\n\n/**\n * Convert ANY opaque id (agent id, run id, conversation id, namespace, email,\n * arbitrary string) into a deterministic, filesystem-safe filename component.\n *\n * Unlike {@link sanitizeIdentifier} (which THROWS on non-conforming input),\n * this is a total function: it NEVER throws on a non-empty string. It returns\n * the lowercased id verbatim when it already matches the safe grammar\n * `^[a-z0-9][a-z0-9-_]*$` and fits `maxLen` (so UUIDs, hashes, and slugs stay\n * human-readable), otherwise a deterministic `h-<16 hex>` sha256 token\n * (collision-resistant and always a valid filename). The output charset is\n * always `[a-z0-9_-]`, safe as a literal path segment on every filesystem.\n *\n * @param id - any opaque identifier (must be a non-empty string)\n * @param options.maxLen - max length for the passthrough branch (default 128).\n * Ids longer than this are hashed; the hash token itself is always short.\n * @throws ConfigurationError (code `invalid_filename_id`) only on empty input.\n *\n * @example\n * safeFilenameForId(\"550e8400-e29b-41d4-a716-446655440000\") // passthrough\n * safeFilenameForId(\"user@example.com\") // \"h-<16hex>\"\n *\n */\nexport function safeFilenameForId(id: string, options?: { maxLen?: number }): string {\n if (id.length === 0) {\n throw new ConfigurationError(\"Filename id must be a non-empty string\", {\n code: \"invalid_filename_id\",\n });\n }\n const maxLen = options?.maxLen ?? 128;\n const lower = id.toLowerCase();\n if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {\n return lower;\n }\n return `h-${createHash(\"sha256\").update(id).digest(\"hex\").slice(0, 16)}`;\n}\n"]}
@@ -60,15 +60,74 @@ if (!REDACT_ENABLED && !warnedOptOut) {
60
60
  );
61
61
  warnedOptOut = true;
62
62
  }
63
+ function isSegmentChar(code) {
64
+ return code >= 48 && code <= 57 || // 0-9
65
+ code >= 65 && code <= 90 || // A-Z
66
+ code >= 97 && code <= 122 || // a-z
67
+ code === 45 || // -
68
+ code === 95;
69
+ }
70
+ function runEnd(s, from) {
71
+ let i = from;
72
+ while (i < s.length && isSegmentChar(s.charCodeAt(i))) i++;
73
+ return i;
74
+ }
75
+ function jwtEndAfterFirstRun(s, r1) {
76
+ if (s[r1] !== "." || !s.startsWith("eyJ", r1 + 1)) return -1;
77
+ const r2 = runEnd(s, r1 + 4);
78
+ if (r2 - (r1 + 4) < 4 || s[r2] !== ".") return -1;
79
+ const r3 = runEnd(s, r2 + 1);
80
+ return r3 - (r2 + 1) < 4 ? -1 : r3;
81
+ }
82
+ function maskJwts(s) {
83
+ let out = "";
84
+ let copied = 0;
85
+ let start = s.indexOf("eyJ");
86
+ while (start >= 0) {
87
+ const r1 = runEnd(s, start + 3);
88
+ const end = r1 - (start + 3) >= 4 ? jwtEndAfterFirstRun(s, r1) : -1;
89
+ if (end < 0) {
90
+ start = s.indexOf("eyJ", r1 + 1);
91
+ continue;
92
+ }
93
+ out += s.slice(copied, start) + maskToken(s.slice(start, end));
94
+ copied = end;
95
+ start = s.indexOf("eyJ", end);
96
+ }
97
+ return copied === 0 ? s : out + s.slice(copied);
98
+ }
99
+ var PEM_LABEL = "(?:RSA |EC |DSA |OPENSSH |ENCRYPTED |)";
100
+ var PEM_BEGIN = new RegExp(`-----BEGIN[ ]+${PEM_LABEL}PRIVATE KEY-----`, "g");
101
+ var PEM_END = new RegExp(`-----END[ ]+${PEM_LABEL}PRIVATE KEY-----`, "g");
102
+ function maskPemBlocks(s) {
103
+ let out = "";
104
+ let cursor = 0;
105
+ for (; ; ) {
106
+ PEM_BEGIN.lastIndex = cursor;
107
+ const begin = PEM_BEGIN.exec(s);
108
+ if (begin === null) break;
109
+ PEM_END.lastIndex = begin.index + begin[0].length + 1;
110
+ const end = PEM_END.exec(s);
111
+ if (end === null) break;
112
+ const blockEnd = end.index + end[0].length;
113
+ out += s.slice(cursor, begin.index) + maskToken(s.slice(begin.index, blockEnd));
114
+ cursor = blockEnd;
115
+ }
116
+ return out + s.slice(cursor);
117
+ }
63
118
  var BUILTIN_PATTERNS = [
64
119
  // T5.4: 30+ vendor prefixes (was 12 pre-T5.4). Order matters — more
65
120
  // specific prefixes precede generic ones (e.g., sk-ant-admin01 before
66
121
  // sk-ant-, sk-proj- before sk-). PEM block deliberately first so its
67
- // multi-line span runs before any per-line patterns can fire.
68
- /-----BEGIN[ ]+(?:RSA |EC |DSA |OPENSSH |ENCRYPTED |)PRIVATE KEY-----[\s\S]+?-----END[ ]+(?:RSA |EC |DSA |OPENSSH |ENCRYPTED |)PRIVATE KEY-----/g,
122
+ // multi-line span runs before any per-line patterns can fire. A scan
123
+ // rather than a regex: the regex was quadratic on many BEGIN headers with
124
+ // no END header (B-544).
125
+ maskPemBlocks,
69
126
  // JWT — exact 3-segment base64url. Dotted; the body floor of 4 chars per
70
127
  // segment matches the minimum legal payload while skipping `a.b.c` noise.
71
- /eyJ[A-Za-z0-9_-]{4,}\.eyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}/g,
128
+ // A scan rather than a regex: the regex was quadratic on a long run of
129
+ // `eyJ` it could not complete (B-554).
130
+ maskJwts,
72
131
  // Azure Storage SAS — match the sig= component (URL-encoded base64).
73
132
  /(?<=[?&]sig=)[A-Za-z0-9%+/]{20,}/g,
74
133
  // Anthropic
@@ -162,8 +221,8 @@ function redactSecrets(text, opts) {
162
221
  if (coerced === null) return "";
163
222
  if (!REDACT_ENABLED) return coerced;
164
223
  let s = coerced;
165
- for (const re of BUILTIN_PATTERNS) {
166
- s = s.replace(re, (m) => maskToken(m));
224
+ for (const masker of BUILTIN_PATTERNS) {
225
+ s = typeof masker === "function" ? masker(s) : s.replace(masker, (m) => maskToken(m));
167
226
  }
168
227
  for (const re of _extraPatterns) {
169
228
  s = s.replace(re, (m) => maskToken(m));
@@ -179,5 +238,5 @@ function redactSecrets(text, opts) {
179
238
  }
180
239
 
181
240
  export { API_KEY_ENV_VAR, addPattern, diag, diagFailure, maskToken, readEnv, redactSecrets, resolveApiKey, setDiagnosticsSink };
182
- //# sourceMappingURL=chunk-CZJ6Q7CW.js.map
183
- //# sourceMappingURL=chunk-CZJ6Q7CW.js.map
241
+ //# sourceMappingURL=chunk-3OY6H3I7.js.map
242
+ //# sourceMappingURL=chunk-3OY6H3I7.js.map